blitz types to c++?

Blitz3D Forums/Blitz3D Programming/blitz types to c++?

mindstorms(Posted 2006) [#1]
I am wondering if anyone knew about this before, and if there is a better way to do it?

c++ header file:
class test
{
public:
	int x,y,z;
	float a,b;
};



c++ cpp file:
#include <stdlib.h>
#include <math.h>
//the header file
#include "testing.h"

#define BBDECL extern "C" _declspec(dllexport)

#define BBCALL _stdcall

BBDECL void BBCALL boo(test* obj)
{
	obj->a = 5.6f;
	obj->x = 4;
	obj->y = 5;
	obj->z = 6;
	obj->b = 10.9f;
}



the decls file in userlibs:
.lib "testing.dll"

boo(obj*):"_boo@4"



the blitz file:
Type test
	Field x,y,z
	Field a#,b#
End Type

t.test = New test
boo(t)

Print(t\a)
Print(t\b)
Print(t\x)
Print(t\y)
Print(t\z)
waitkey()
end



c++ code compiled in visual c++ 2005 express edition.
I am not sure if you can return a c++ class, all I have tried is passing a blitz type into the c++ class.

Lego is the best...


mindstorms(Posted 2006) [#2]
Is this method slower than poking/peeking from banks to send multiple amounts of data (it is sure easier!)?


mindstorms(Posted 2006) [#3]
You can even send arrays, and set up double linked lists on both sides to loop through them(in c++ or blitz!)


mindstorms(Posted 2006) [#4]
What are the ramifications to using this? Doesn't work in debug mode because there Blitz checks for array bounds...

c++ cpp file:
#include <stdlib.h>
#include <math.h>
//the header file
#include "testing.h"
#define BBDECL extern "C" _declspec(dllexport)
#define BBCALL _stdcall

class test
{
public:
	int* temp;
};

BBDECL void BBCALL boo(test* obj)
{
	obj->temp = (int*) malloc(sizeof(int) * 500);
	for(int i=0;i<500;i++)
	{
		obj->temp[i] = 4;
	}
}


blitz code:
Graphics 800,600,0,2
Type test
	Field temp[100]
End Type
t.test = New test
boo(t)

For i = 0 To 499
	Print(t\temp[i])
Next
WaitKey()
End

(uses same decls as above)
I am sure somewhere there is a memory leak...is there any way to get blitz debugging to work with this?


b32(Posted 2006) [#5]
This is a very interesting topic. I didn't knew you could send types to a .dll. That could be very handy.
In the last post, why do you set the temp field length to [100]? Shouldn't it be 500?


mindstorms(Posted 2006) [#6]
The whole point was that the c++ code is able to resize the blitz arrays, perhaps dynamically... and Blitz in non-debugger mode doesn't really seem to care. There's probably a catch somewhere, though...like that it doesn't work in debug mode :( The idea is maybe you could return any amount of data, resizing the arrays in the c++ data. You could perhaps have in the type an int telling how big the array is, then simply access it from blitz.

...I guess all I am after is a way to return any amount of data from a dll in one call without knowing how many before hand, but simply let the dll return the right amount and a reference to how much that is.


octothorpe(Posted 2006) [#7]
Interesting stuff!

perhaps dynamically


Definitely dynamically. Pretty hard to do something that's not dynamic with a Dynamically Linked Library. ;)

I am sure somewhere there is a memory leak


Yeah, you need to free() your malloc()ed memory. I also imagine that Blitz will use the array pointer to free the original 100 element array, so you should also revert the pointer to its original value before letting Blitz delete the object. Of course, if your objects will stay alive until your program ends, you can just forget about leaks and let the OS clean up after you.

If you want to trick the Blitz debugger, I would think this might work:

Type packet
	Field foo#, bar%
End Type

Type result
	Field p.packet[1000000000] ; just to fool Blitz's bounds checker (C++, not Blitz, will instantiate result objects)
	Field max_packets%
End Type

Local r.result = get_data() ; call c++
For i% = 0 To r\max_packets - 1
	Local p.packet = r\p[i]
	Print "got a packet: foo=" + p\foo + " bar=" + p\bar
Next
free_data(r) ; call c++ to clean up


struct packet {
	float foo;
	int bar;
};

struct result {
	packet * packet_ptr;
	int max_packets;
};

BBDECL result * BBCALL get_data() {
	result * r = new result;
	// create some arbitrary packets
	r->max_packets = 42;
	r->packet_ptr = (packet*) malloc(sizeof(packet) * 42);
	for(int i=0;i<42;i++) {
		r->packet_ptr[i].foo = 6.02f;
		r->packet_ptr[i].bar = i;
	}
	return(r);
}

BBDECL void BBCALL free_data(result * r) {
	free(r->packet_ptr);
	delete(r);
}


Because you're relying on C++ to create the "result" object, Blitz won't ever try to allocate 1000000000 packets. At the same time, the Blitz debugger assumes that any existing "result" objects will have been created with a nice big fat array and won't mind if you dereference elements with indices below 1000000000. This leaves the responsibility of bounds checking in Blitz up to you.

Note that objects created in C++ won't be added to Blitz's builtin linked-lists so you won't be able to use First, After, etc.

P.S. "class test { public:" is the same as "struct test {"


mindstorms(Posted 2006) [#8]
Octothorpe, I am having trouble getting your version to work, I cannot figure out in the decls file how to tell blitz that it is returning an object...all I have managed to do is make the blitzcc.exe completely crash, having to restart my computer, or get error messages...


octothorpe(Posted 2006) [#9]
I don't know anything about decls files, but if you're unable to return an object pointer from C++, how about passing in an object with a pointer field and modifying the pointer?

Type result_wrapper
	Field r.result
End Type

Local rw.result_wrapper = New result_wrapper
get_data(rw) ; call C++
Local r.result = rw\r
; then do the same as before

struct result_wrapper {
	result * r;
};
BBDECL void BBCALL get_data(result_wrapper * rw) {
	rw->r = new result;
	//etc


Make sense?


mindstorms(Posted 2006) [#10]
...

Blitz creates an unhandled memory exception when you try to create a type in c++....It is looking like blitz does some special conversion of its types in order to send it to c++, and does not work with creating types in c++. :(


octothorpe(Posted 2006) [#11]
Hmm, that sucks. Sorry about the wild goose chase.

One hack that should work and make the Blitz debugger happy would be if you added another C++ function to your original code which advances your array pointer. You'd only be able to reference N (100 in your example above) elements at a time, but could move the pointer forward to the next N when you finish with them.

BBDECL void BBCALL advance_ptr(test* obj) {
	obj->temp = obj->temp + 100;
}



octothorpe(Posted 2006) [#12]
Totally unsafe and crazy:

b = CreateBank()
PokeByte(b, -16, 255)
Print PeekByte(b, 254)


Maybe with some research you could modify the bounds of a bank safely from C++? I'd be worried that outside of debug mode, banks might not have that header and you'd be writing all over other data.


mindstorms(Posted 2006) [#13]
Yeah, I've already tried that...writing in c++ writes the bytes to different locations, so trying to read them back in blitz returns a garbled mess.

Also, I was thinking about having a function return the size of the data, and then have blitz resize a bank to that size and pass it to another function which fills it. This should work, but I have not tried implementing it yet.


mindstorms(Posted 2007) [#14]
sorry to drag this back, but is there a faster to way to get function pointers in blitz with arguments than this?

c++ code
// a function pointer declaration for calling Blitz3D code
void (__stdcall *ABlitz3DFunction)(void);

class passer
{
public:
	int t,h;
	float b,s;
};

passer pass;

#define BBDECL extern "C" _declspec(dllexport)
#define BBCALL _stdcall

// standard DLL function declaration for Blitz3D userlibs
BBDECL void BBCALL SetBlitz3DFunction(void)
{
    unsigned int stackpos,adrs;
    unsigned int *func;
    __asm
    {
        mov stackpos,esp
        mov esp,ebp
        add esp,4
        pop adrs
        mov esp,stackpos
     }
     func=(unsigned int*)&ABlitz3DFunction;
    *func=adrs;
}

// extra DLL function for test purposes only - your DLL would call ABlitz3DFunction() directly!
BBDECL void BBCALL CallBlitz3DFunction(void)
{
	//"pass" arguments into the function
	pass.t = 6;
	pass.s = 7.8f;
	pass.b = 5.2f;
	pass.h = 8;
    ABlitz3DFunction();
}

BBDECL void BBCALL Get_Args(passer* p)
{
	p->t = pass.t;
	p->h = pass.h;
	p->s = pass.s;
	p->b = pass.b;
}


blitz code:
Global SETUP_FUNCS%=1
Gosub test_function
SETUP_FUNCS=0

CallBlitz3DFunction()     ;<-- testing DLL access only

WaitKey
End


.test_function
	SetBlitz3DFunction()
	If SETUP_FUNCS=0
		p.passer = New passer
		GET_Args(p)
		ABlitz3DFunction(p\t,p\h,p\s,p\b)
	EndIf
Return
	
Function ABlitz3DFunction(t,h,s#,b#)
	; *** function body goes here!!!
		Print "This works!!!!"+t+h+s+b
		Return
End Function
	
	Type passer
		Field t,h,b#,s#
	End Type


it is probably a little messy still, but the idea is there. Have c++ call the blitz code, which responds by asking c++ what the arguments are, and then calls the necessary function. Also, I have not clocked it to see how fast it is, but I am assuming that it is not unmanageably slow.

edit: after timing it looping through 2 million times (without print command) it took it just over 1 second. with 2000 times it took 1 millisecond. Compare that to the 602 millisecs it takes to make a function call within blitz wile passing one parameter 2 million times. :)