我正在研究如何在我的应用程序中集成完整的脚本支持,但是在规划我的C API以使LUA友好时会遇到一些问题。
基本上我有一堆通过init和free创建的结构 这样的功能:
[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;
}
我使用swig生成LUA模块,因此我创建了以下接口文件:
[test.i]
%module test
%{
%}
Test *TestAdd( char *name );
Test *TestDelete( Test * test );
如果用户代码如下所示,一切正常:
a = test.TestAdd( "test" )
a = test.TestDelete( a )
if( a != nil ) print( a.name )
但是如果用户代码是这样的:
a = test.TestAdd( "test" )
test.TestDelete( a )
if( a != nil ) print( a.name ) -- Crash the app with bad_access (not just a LuaVM error).
甚至最差:
a = test.TestAdd( "test" )
test.TestDelete( a )
test.TestDelete( a )
-- Another way of making crash my app completely!
有没有办法可以在C中创建一组API来避免这种问题,并允许用户以安全的方式安全地添加/删除和访问属性,不会产生“错误访问”错误并使整个程序崩溃,最好的是LUAVM只返回一个错误并继续执行。
我一直在搜索并尝试使用不同的方法来处理我的C API以避免此问题,但失败了......
任何人都可以帮助我,或者给我一些关于这个方向的指示。
提前致谢,
答案 0 :(得分:5)
有一种方法可以做到这一点:停止将C风格的接口直接导出到Lua。 C不是Lua,你永远不应该让Lua程序像C程序那样行事。
除非没有办法避免它,否则Lua代码永远不必释放任何东西。如果Lua代码明确地创建了某些东西,那么Lua代码应该在其生命周期内具有治理。这意味着你使用垃圾收集来删除内存:当Lua GC完成它时,你使用metamethod来释放你使用的任何调用指针。
通常,您可以通过以下两种方式之一为Lua指定一个对象:Lua 拥有该指针,或者该指针指向一个比lua_State
本身更长的对象(因此将始终可用于Lua脚本)。在某些情况下,你可以向Lua提供一些它可以引用的临时对象,但是应该尽可能避免这些。
您需要做的是use the %newobject
directive告诉SWIG TestAdd
返回需要删除的指针。然后,您需要将Test
对象与TestDelete
关联为删除者using %newfree typemap。这样可以将Test
的所有权从C转移到Lua。此时,C应该从不手动删除它。让Lua和SWIG完成他们的工作。
因此,TestDelete
不应直接暴露给Lua。当GC检测到没有人再引用Test
实例时,将隐式调用它。