Im researching about how to integrate full script support inside my app but have a bit of a problem planning my C API to be LUA friendly.
Basically I got bunch of structs that are created through init and free
functions like this:
[ test.h ]
typedef struct
{
char name[ 50 ];
} Test;
Test *TestAdd( char *name );
Test *TestDelete( Test *test );
[ test.c ]
Test *TestAdd( char *name )
{
Test *test = ( Test * ) calloc( 1, sizeof( Test ) );
strcpy( test->name, name );
return test;
}
Test *TestDelete( Test *test )
{
free( test );
return NULL;
}
Im using swig to generate the LUA module so I create the following interface file:
[ test.i ]
%module test
%{
%}
Test *TestAdd( char *name );
Test *TestDelete( Test * test );
Everything work fine if the user code is like this:
a = test.TestAdd( "test" )
a = test.TestDelete( a )
if( a != nil ) print( a.name )
But if the user code is like that:
a = test.TestAdd( "test" )
test.TestDelete( a )
if( a != nil ) print( a.name ) -- Crash the app with bad_access (not just a LuaVM error).
Or even worst:
a = test.TestAdd( "test" )
test.TestDelete( a )
test.TestDelete( a )
-- Another way of making crash my app completely!
Is there any way that I can create a set of API in C that would avoid this kind of problem and would allow the user to safely add/delete and access properties in a safe way that would not create “bad access” error and make the whole program crash, the best would be that the LUAVM simply return an error and the execution continue.
I’ve been searching and tried different approach to my C API to avoid this problem but failed…
Anybody can help me or give me some pointers about the direction to go with this.
Thanks in advance,
There is a way to do this: stop exporting C-style interfaces directly to Lua. C is not Lua, and you should never intend for Lua programs to act like C programs.
Unless there is no way to avoid it, Lua code should never have to free anything. If Lua code explicitly creates something, then Lua code should have governance over its lifetime. That means you use garbage collection to delete the memory: when the Lua GC is finished with it, you use a metamethod to free the pointer with whatever call you use.
In general, you give Lua a pointer to an object in one of two ways: either Lua owns that pointer, or that pointer is to an object that will outlive the
lua_Stateitself (and therefore will always be available to the Lua script). There are occasions where you might hand Lua some temporary object that it can reference, but these should be avoided whenever possible.What you need to do is use the
%newobjectdirective to tell SWIG thatTestAddreturns a pointer that needs to be deleted. Then, you need to associate theTestobject withTestDeleteas the deleter, using %newfree typemap. This will properly transfer ownership of theTestfrom C to Lua. At which point, C should never delete it manually. Just let Lua and SWIG do their job.As such,
TestDeleteshould not be exposed to Lua directly. It will be called implicitly when the GC detects that nobody is referring to theTestinstance anymore.