Working with unchangeable C structs

BlitzMax Forums/BlitzMax Programming/Working with unchangeable C structs

plash(Posted 2010) [#1]
I have a C struct that I cannot change, and is used in many of the functions I'm wrapping (so using a custom struct and converting/passing it around in all of those functions would be more of a chore than the current solution).
I've noted that types inside extern blocks have a 4 byte header (as I'm sure normal types have as well). This isn't much of an obstacle if I was able to control the form of the C struct, but I cannot (it would be a matter of adding an int padding field to the top of the struct).

Soo, is there some black magic I can do to get around this limitation without resorting to making a function to get/set each field in the C struct?
(Mind ye, playing with the pointer of the first field is not ideal, as there are many fields in the C struct.)

Some code to play with:
' teststruct.bmx

SuperStrict

Framework brl.blitz
Import brl.standardio

Import "teststruct.c"

Extern "C"
	Type CTest
		Field a:Int
		Field b:Int
	End Type
	Function bmx_create_test:CTest(a:Int, b:Int)
	Function bmx_delete_test(ts:CTest)
End Extern

Local ts:CTest = bmx_create_test(1, 100)
' This should print something like this: "ts.a = 100, ts.b = 6357097" (offset by four bytes!)
Print("ts.a = " + ts.a + ", ts.b = " + ts.b)
bmx_delete_test(ts)

// teststruct.c

typedef struct s_test {
	//int _padding;  // Comment this out to see how it should work (we cannot do this with the actual C struct)
	int a;
	int b;
} test_t;

extern test_t* bmx_create_test(int a, int b) {
	test_t* ts = malloc(sizeof(test_t));
	ts->a = a;
	ts->b = b;
	return ts;
}

extern void bmx_delete_test(test_t* ts) {
	if (ts) {
		free(ts);
	}
}



Tommo(Posted 2010) [#2]
You can use a bmax type, and send the pointer of first member to your C function.

Type CTest
	Field a:Int
	Field b:Int
	Method pointer:Byte Ptr()
		Return VarPtr a
	End Method
End Type

Local test:CTest = New CTest
somecfunction(test.pointer())


If it's not performance critical, you can also copy the data from C struct into a bmax object, like:
SuperStrict

Framework brl.blitz
Import brl.standardio

Import "teststruct.c"

Extern "C"
	Function bmx_create_test:Byte Ptr(a:Int, b:Int)
	Function bmx_delete_test(ts:Byte Ptr)
End Extern

Type CTest
	Field a:Int
	Field b:Int
	
	Function FromCStruct:CTest(s:Byte Ptr)
		Local t:CTest = New CTest
		MemCopy(VarPtr t.a, s, SizeOf(CTest))
		Return t
	End Function
	
End Type

Local cs:Byte Ptr = bmx_create_test(1, 100)	
Local ts:CTest = CTest.FromCStruct(cs)

Print("ts.a = " + ts.a + ", ts.b = " + ts.b)
bmx_delete_test(cs)



plash(Posted 2010) [#3]
Unfortunately, neither of those will work in my situation.
For the first one, I'm receiving a pointer, the library creates it for me. For the second, I would be maintaining two pointers (you cannot pass CTest back to the C functions because it's not an extern type), and I would end up with double the memory usage.


Tommo(Posted 2010) [#4]

you cannot pass CTest back to the C functions because it's not an extern type


You can directly pass pointer of CTest.a back, just like the first example.
Or you can copy data back to the C struct before sending it to your C function.


plash(Posted 2010) [#5]
You can directly pass pointer of CTest.a back, just like the first example.
Except you would have to have multiple functions for different levels of pointers (there are several functions that take SomeStruct**, which, indeed, you can do easily with extern types [e.g. SomeStruct Ptr]).

Or you can copy data back to the C struct before sending it to your C function.
Again, memory.

EDIT: I suppose I should say that I'm trying to do it with the regular extern types, as there are many advantages to doing so (see above).
I'm really surprised BlitzMax doesn't change the way extern types are managed. I'll continue to use the extern types with field get/set functions on the C side until I can find a way around the issue, whilst retaining the benefits of extern types.


Tommo(Posted 2010) [#6]

Again, memory.


It depends on what kind of struct you are dealing with.
If you don't need to keep the reference, it would be fine to use a bmax object as temporary accessor.
Actually I don't use this method often in my wrappers. The C getter/setter pattern is more solid.
And I think "Extern Type" is kind of useless...
:(


plash(Posted 2010) [#7]
I do need to keep the reference. In fact, I need the reference to all of the structs, otherwise I wouldn't be able to call any functions.

Anyhow, I was wrapping OpenCV, but it seems that they still haven't fixed the plethora of issues that plagued me back two years ago. Plenty of applications (tvtime, xawtv, vlc) all show my camera feeds just fine, but OpenCV segfaults after about four seconds.


Difference(Posted 2010) [#8]
Bruceys OpenCV works fine for me, as shown in contourfinder.bmx example.

It's part of his ofx.mod available at http://code.google.com/p/maxmods/source/checkout


Otus(Posted 2010) [#9]
Use an Extern type and wrappers that correct the pointer. (The +4B is for class virtual function list with C++ to make Methods work.)

Something like...
Extern
  Type TBlah
    Field a:int
  End Type
  Function create_blah:TBlah(a:int)
  Function use_blah(b:TBlah)
End Extern

void *create_blah(int a)
{
  struct blah *b = real_create_blah(a);
  return (void*)b-1;
}

void use_blah(void *b)
{
  real_use_blah( (struct blah*)(b+1) );
}



plash(Posted 2010) [#10]
Bruceys OpenCV works fine for me, as shown in contourfinder.bmx example.
I know he has an OpenFrameworks module, and it did not compile the last time I tried it. I also wanted a module with just OpenCV. I know the segfault is not my fault because the same program in C also segfaults.

EDIT:
The +4B is for class virtual function list with C++ to make Methods work.
I wish there was a way to turn that off.