我使用luaL_newlib(luaState, afbFunction)
afbFunction
是静态luaL_Reg
数组。很遗憾,luaL_Reg
仅支持两个字段:name
和func
。因此,无法将句柄传递给函数并实现类似:
static const luaL_Reg afbFunction[] = {
{"notice" , LuaPrintMsg, MSG_NOTICE},
{"info" , LuaPrintMsg, MSG_INFO},
{"warning", LuaPrintWar, MSG_WARN},
{NULL, NULL} /* sentinel */
};
我有两个动机,因为没有像Lua函数那样多的C入口点:
有人有解决方案为多个Lua函数提供唯一的C入口点吗?当使用luaL_newlib()
时,C函数接收lib表作为第二个参数,但它似乎接收到完整的表而不是相应的条目,甚至也无法测试标签。
答案 0 :(得分:2)
使用upvalues!
如果您不使用luaL_newlib
(这是一个定义为...的宏
#define luaL_newlib(L,l) \
(luaL_checkversion(L), luaL_newlibtable(L,l), luaL_setfuncs(L,l,0))
...)但显式调用luaL_setfuncs
,可以使每个函数都有一些upvalues。可以使用nil
初始化所有这些内容,您可以稍后对其进行修补。虽然您无法使用额外字段扩展luaL_Reg
,但您可以定义额外的struct
并为其他参数设置单独的数组。
最小样本实现(可能需要更多错误检查):
(应该按原样复制和粘贴,文本块应该成为注释)
#include <lua.h>
#include <lauxlib.h>
/*
首先,定义一个具有函数名称的struct
以及之后需要的任何内容。此示例仅使用字符串,但您可以使用int
,void*
,...甚至可以使用多个值。
*/
typedef struct xlua_uvinfo {
const char *name; /* this is how we identify the functions */
const char *uv; /* this can change arbitrarily */
} xlua_uvinfo;
/*
然后我们将有一个函数来修补堆栈上的libtable以放入真正的upvalue数据。您需要调整所推送字段的类型(lua_pushX
),顺序和/或名称。
请注意,由于我们按名称识别功能,因此并非所有功能都需要启动。 (因此,您可以在luaL_Reg
中包含正常函数,这些函数最终将使用虚拟的上升值插槽,这些插槽将在此处填充。)
*/
static void setupvalues( lua_State *L, const xlua_uvinfo *l ) {
for (; l->name != NULL; l++) {
/* get the function by name */
if (lua_getfield( L, -1, l->name ) == LUA_TFUNCTION) {
/* this needs to change according to what you have in the struct */
lua_pushstring( L, l->uv );
lua_setupvalue( L, -2, 1 );
}
/* pop the function */
lua_pop( L, 1 );
}
}
/*
这是你的功能的虚拟实现。我使用字符串前缀 - 这很容易调试/检查。
*/
#define MSG_NOTICE "NOTE"
#define MSG_INFO "INFO"
#define MSG_WARN "WARN"
static int LuaPrintMsg( lua_State *L ) {
const char *prefix = lua_tostring( L, lua_upvalueindex(1) );
printf( "%s: %s\n", prefix, lua_tostring( L, 1 ) );
return 0;
}
/* plain luaL_Reg array */
static const luaL_Reg afbFunction[] = {
{"notice" , LuaPrintMsg},
{"info" , LuaPrintMsg},
{"warning", LuaPrintMsg},
{NULL, NULL}
};
/*
现在更改的部分:为upvalues添加一个额外的数组,然后调整luaopen_X
函数。
*/
/* extra upvalue array (must also be terminated by { NULL, NULL } !) */
static const xlua_uvinfo afbUpvalues[] = {
{"notice" , MSG_NOTICE},
{"info" , MSG_INFO},
{"warning", MSG_WARN},
{NULL, NULL}
};
int luaopen_foo( lua_State *L ) {
/* as mentioned above, we can't use `luaL_newlib`, expand manually */
luaL_checkversion( L );
/* create the empty table, pre-allocated to the right size */
luaL_newlibtable( L, afbFunction );
/* now push a dummy upvalue (more if you add extra upvalues!) */
lua_pushnil( L );
/* register the functions, giving 1 upvalue to each */
luaL_setfuncs( L, afbFunction, 1 );
/* now set the actual upvalues */
setupvalues( L, afbUpvalues );
return 1;
}
如果将其保存为foo.c
,请编译为(在Linux上)gcc -shared -fPIC -o foo.so foo.c
,然后运行lua -l foo
,您将获得所需的行为:
foo.info "foo"
--> INFO: foo
foo.notice "bar"
--> NOTE: bar
foo.warning "baz"
--> WARN: baz
答案 1 :(得分:0)
虽然之前的响应由“nobody”提供,但我最终使用了不同的解决方案来解决我的问题。在我的解决方案之后。
我的目标是向开发人员提供尽可能简单的东西,用他们自己的命令集扩展Lua。我的程序将JSON配置文件作为输入。配置包含一个插件部分:
"plugin": {
"label" : "MyPlug",
"sharelib": "ctl-audio-plugin-sample.ctlso",
"lua2c": ["Lua2cHelloWorld1", "Lua2cHelloWorld2"]
}
其中:
我的插件使用C宏来使Lua接口对开发人员透明。我的项目使用Json-C,我的包装器导入/导出Lua表到json_object,反之亦然。以下是一个简单的例子。
CTLP_LUA2C (Lua2cHelloWorld2, label, argsJ, context) {
MyPluginCtxT *pluginCtx= (MyPluginCtxT*)context;
if (!context || pluginCtx->magic != MY_PLUGIN_MAGIC) {
AFB_ERROR("CONTROLER-PLUGIN-SAMPLE:Lua2cHelloWorld2 (Hoops) Invalid Sample Plugin Context");
return -1;
};
pluginCtx->count++;
AFB_NOTICE ("CONTROLER-PLUGIN-SAMPLE:Lua2cHelloWorld2 SamplePolicyCount action=%s args=%s count=%d"
,label, jsonToString(argsJ), pluginCtx->count);
return 0;
}
其中:
技术上CTLP_LUA2C在我的示例中创建了一个静态C函数(Lua2cHelloWorld2)。然后它创建一个公共包装函数名称luac_Lua2cHelloWorld2,包装器是向前的。
return((*Lua2cWrap)(luaState, MACRO_STR_VALUE(FuncName), FuncName))
其中:
我的代码可用here.
关于Lua / C集成模型的一般评论: 将C集成到Lua中远非复杂,Lua接口库显然缺少一些实用程序。
用于创建Lua命令的扩展版NewLib应该支持类似。
{"xxxx" , Callback, handle, "free information on command"},
{"yyyy" , Callbacl, handle, "info....},
使用NewLib创建的最小函数应该只接收与目标函数有关的项目作为第一个参数,而不是5.3中的完整表格。接收fuul表是完全没用的,并且阻止使用通用回调。
应该实现从Lua表移动到C表的通用模型。我的项目使用Json-C,显然可以提出许多C数据表示。然而,显然缺少某些东西,用于将数据从Lua导入/导出到C的当前Lua / API仅仅是一场噩梦。我的代码为Lua&lt; - &gt; json_object提供了简单的导入/导出功能。
// Pop all arguments from Lua and move them in a JsonC object
json_object *argsJ= LuaPopArgs(luaState, LUA_FIST_ARG);
// Pop a single arguments from Lua and make it a JsonC object
json_object *contextJ = LuaPopOneArg(luaState, LUA_FIST_ARG + 2);
// Push C arguments from C to Lua
count+= LuaPushArgument(responseJ);
count+= LuaPushArgument(contextCB->context);
int err=lua_pcall(luaState, count, LUA_MULTRET, 0);
请注意,如果没有Nick的帮助,我可能永远不会成功处理来自Lua&gt; C的嵌套导入表。请参阅here。
我最后关注的最后一个集成点是一个从C例程到Lua的Push / Pop不透明句柄的系统。我实现了三个例程来处理这个问题
这允许实现非常简单的函数,该函数需要由Lua作为不透明句柄携带C上下文。如下面的例子
STATIC int LuaAfbSuccess(lua_State* luaState) {
// retrieve C context carry as opaque handle by Lua
LuaAfbContextT *afbContext= LuaCtxCheck(luaState, LUA_FIST_ARG);
if (!afbContext) goto OnErrorExit;
// import remaining arguments into JsonC
json_object *responseJ= LuaPopArgs(luaState, LUA_FIST_ARG+1);
if (responseJ == JSON_ERROR) return 1;
// send success response to client
afb_req_success(afbContext->request, responseJ, NULL);
// free C context
LuaCtxFree(afbContext);
return 0;
OnErrorExit:
lua_error(luaState);
return 1;
}