Returning allocated structure from C

BlitzMax Forums/BlitzMax Programming/Returning allocated structure from C

Nicholas(Posted 2006) [#1]
I'm trying to return a structure (created in C), back to BM.
The C routine is thus :

struct __HEADER	*Create_Header(void)
{
struct __HEADER	*temp;

	if ((temp=(struct __HEADER *) calloc(1,sizeof(struct __HEADER)))!=NULL)
	{
		temp->header=LINKLIST_HEADER;
	}
	
	return (temp);
}


and the BM code is :

Import "/Users/nicholaskingsley/LinkList/LinkList.c"

Extern
	Type LinkListNode
		Field header:Int
		Field dataSize:Int
		Field data:Byte Ptr
		Field prevNode:LinkListNode Ptr
		Field nextNode:LinkListNode Ptr
	EndType
	
	Type LinkListHeader
		Field header:Int
		Field numNodes:Int
		Field firstNode:LinkListNode Ptr
		Field lastNode:LinkListNode Ptr
	EndType
	
	Function Create_Header:LinkListHeader()
	'Function Diag:Byte(header:LinkListHeader,fileName$)
EndExtern

Local header:LinkListHeader

	header=Create_Header()
	Print "Returned Header header :"
	Print header.header


The C function is being called no problem, memory is being allocated and the header code is being stored.

Unfortunately the value in header.header in BM is 0, which must mean a pointer to the stucture isn't being returned for some reason.


Brendane(Posted 2006) [#2]
Ok. The problem is that you are creating a C struct and trying to assign it to a BlitzMax object. A BlitzMax object is not a C struct - it has an object pointer and a virtual function table much like a C++ struct/class can have.

So, what you need to do is to first make your structure a C++ struct and force it to have a vtable pointer by giving it a virtual destructor. This also means renaming your .c file to .cpp firstly so that is is compiled with the C++ compiler.

Then you need to define your struct like so :-
struct __HEADER
{
	virtual ~__HEADER() {}; // This is the important bit

	int header;
	// etc..	
};

extern "C"
{
struct __HEADER	*Create_Header(void)
{
	struct __HEADER	*temp;
	
	if ((temp=(struct __HEADER *) calloc(1,sizeof(struct __HEADER)))!=NULL)
	{
		temp->header=195; // test value
	}
	
	return (temp);
}
}


Note that the function definition is now wrapped with extern "C".

Now you should be able to import your new .cpp file, call Create_Header and observe that the header field contains the test value 195.

Remember you can't expect BM to garbage collect this object - you'll need another function in your .cpp file to destroy it.


Brendane(Posted 2006) [#3]
Also - instead of using calloc() to allocate the structure - you should use the C++ new operator...

ie.

temp = new __HEADER;
if( temp )
{
// etc...
}

Your Delete_Header() function should look something like this :-

void Delete_Header( __HEADER *header )
{
delete header;
}

This will call the destructor properly if you do end up putting any code in there.

The destructor is the function which begins with a ~ incase you don't know C++ and is called whenever a C++ object goes out of scope or is destroyed.

Good luck.


skidracer(Posted 2006) [#4]
BlitzMax object is not a C struct - it has an object pointer and a virtual function table much like a C++ struct/class can have.


That's not true for types declared inside an extern block. When BlitzMax was first born these did use to associate directly with C structs, unfortunately on win32 I think they got broken when the com IUnknown interface feature was added. Easiest solution I think is to add a dummy int to the beginning of your C struct declaration (but not your blitzmax extern type definition).


Dreamora(Posted 2006) [#5]
To use external C Structs, don't forget that a memory allocated in C is a Byte Ptr, it is not an Int. so assigning Byte Ptr to int will in most cases just be completely wrong.

(this is only possible with BM own objects which have a handlefromobject and objectfromhandle functionality)


Nicholas(Posted 2006) [#6]
Thanks for that - it works well now.


Brendane(Posted 2006) [#7]
"That's not true for types declared inside an extern block."
- ah, I didn't know that. I was half asleep when making his example work and something did strike me a little odd at the time, I didn't expect it to work by adding a single 4 byte param into the struct (the vtable ptr effectively) - now it makes sense.

Can we please have some documentation update regarding extern and c/c++ struct/class interfacing - would be nice if this stuff was openly documented.