如何为LUA创建安全的C接口
我正在研究如何在我的应用程序中集成完整的脚本支持,但是在规划我的C API以使其对LUA友好时遇到了一些问题 基本上我得到了一堆通过init和free创建的结构 功能如下:如何为LUA创建安全的C接口,c,lua,swig,C,Lua,Swig,我正在研究如何在我的应用程序中集成完整的脚本支持,但是在规划我的C API以使其对LUA友好时遇到了一些问题 基本上我得到了一堆通过init和free创建的结构 功能如下: a = test.TestAdd( "test" ) a = test.TestDelete( a ) if( a != nil ) print( a.name ) [测试h] typedef struct { char name[ 50 ]; } Test; Test *TestAdd( char *name );
a = test.TestAdd( "test" )
a = test.TestDelete( a )
if( a != nil ) print( a.name )
[测试h]
typedef struct
{
char name[ 50 ];
} Test;
Test *TestAdd( char *name );
Test *TestDelete( 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使用swig生成LUA模块,因此我创建了以下接口文件:
[测试一]
%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方法,但失败了
任何人都可以帮助我,或者给我一些关于这个方向的建议
提前感谢,有一种方法可以做到这一点:停止将C风格接口直接导出到Lua。C不是Lua,您永远不应该让Lua程序像C程序一样工作 除非无法避免,否则Lua代码永远不应该释放任何东西。如果Lua代码显式地创建了一些东西,那么Lua代码应该在其生命周期中拥有治理权。这意味着您可以使用垃圾收集来删除内存:当Lua-GC完成它时,您可以使用元方法来释放指针,无论您使用什么调用 通常,可以通过以下两种方式之一为Lua提供指向对象的指针:Lua拥有该指针,或者该指针指向的对象将超过
Lua_状态本身(因此Lua脚本始终可用)。在某些情况下,您可能会向Lua提供一些它可以引用的临时对象,但应尽可能避免这些情况
您需要做的是告诉SWIG,TestAdd
返回一个需要删除的指针。然后,您需要将Test
对象与TestDelete
关联为删除器。这将正确地将测试的所有权从C转移到Lua。在这一点上,C永远不应该手动删除它。让Lua和SWIG做他们的工作
因此,TestDelete
不应直接暴露于Lua。当GC检测到没有人再引用Test
实例时,它将被隐式调用。有一种方法可以做到这一点:停止将C风格接口直接导出到Lua。C不是Lua,您永远不应该让Lua程序像C程序一样工作
除非无法避免,否则Lua代码永远不应该释放任何东西。如果Lua代码显式地创建了一些东西,那么Lua代码应该在其生命周期中拥有治理权。这意味着您可以使用垃圾收集来删除内存:当Lua-GC完成它时,您可以使用元方法来释放指针,无论您使用什么调用
通常,可以通过以下两种方式之一为Lua提供指向对象的指针:Lua拥有该指针,或者该指针指向的对象将超过Lua_状态本身(因此Lua脚本始终可用)。在某些情况下,您可能会向Lua提供一些它可以引用的临时对象,但应尽可能避免这些情况
您需要做的是告诉SWIG,TestAdd
返回一个需要删除的指针。然后,您需要将Test
对象与TestDelete
关联为删除器。这将正确地将测试的所有权从C转移到Lua。在这一点上,C永远不应该手动删除它。让Lua和SWIG做他们的工作
因此,TestDelete
不应直接暴露于Lua。当GC检测到没有人再引用Test
实例时,它将被隐式调用。如果您可以将指针设置为NULL
,那么free
两次将不会有问题,您只需在使用它的每个函数中检查它是否NULL
。如果您可以将指针设置为NULL
那么free
两次就不会有问题了,你只需要检查使用它的每个函数中的NULL
。嗯,我不确定我是否得到了它,你能用我上面发布的代码创建一个简单的例子吗?从您重定向我的页面中,我感觉到:%newobject create\u foo;%删除对象销毁\u foo。。。Foo*创建_Foo();无效销毁_foo(foo*foo);那是去的路吗不?但是我仍然不知道我能不能实现这个…@McBob:很抱歉链接;我已经改正了。但是,正如我所说的,不要向用户公开TestDelete
。我不知道你没有得到什么,因为SWIG文档非常全面。如果你不明白SWIG类型映射和选项是如何工作的,你应该先弄清楚。嗯,我不确定我是否明白,你能用我上面发布的代码创建一个简单的例子吗?从您重定向我的页面中,我感觉到:%newobject create\u foo;%删除对象销毁\u foo。。。Foo*创建_Foo();无效销毁_foo(foo*foo);那是去的路吗不?但是我仍然不知道我能不能实现这个…@McBob:很抱歉链接;我已经改正了。但是,正如我所说的,不要向用户公开TestDelete
。我不知道你没有得到什么,因为SWIG文档非常全面。如果您不了解SWIG类型映射和选项是如何工作的,您应该首先弄清楚这一点。