Lua将句柄传递给使用newlib创建的函数

时间:2017-08-15 19:16:22

标签: c json lua

我使用luaL_newlib(luaState, afbFunction) afbFunction是静态luaL_Reg数组。很遗憾,luaL_Reg仅支持两个字段:namefunc。因此,无法将句柄传递给函数并实现类似:

static const luaL_Reg afbFunction[] = {
    {"notice" , LuaPrintMsg, MSG_NOTICE},
    {"info"   , LuaPrintMsg, MSG_INFO},
    {"warning", LuaPrintWar, MSG_WARN},
    {NULL, NULL}  /* sentinel */
};

我有两个动机,因为没有像Lua函数那样多的C入口点:

  1. 我有一个通用的包装器,用于处理输入/输出的所有C函数 参数值从Lua(表)到C(jsonc)。
  2. 接口的Lua函数数量来自配置文件,可能无需重建代码即可更改。简而言之,配置文件有一个sharelib路径,后跟要公开的函数列表。
  3. 有人有解决方案为多个Lua函数提供唯一的C入口点吗?当使用luaL_newlib()时,C函数接收lib表作为第二个参数,但它似乎接收到完整的表而不是相应的条目,甚至也无法测试标签。

2 个答案:

答案 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以及之后需要的任何内容。此示例仅使用字符串,但您可以使用intvoid*,...甚至可以使用多个值。

*/
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"]
            }

其中:

  1. sharelib是我的用户插件名称,包含Lua命令
  2. lu2c插件公开的命令列表为Lua命令。
  3. 标记Lua命令的命名空间。在我的示例中,“MyPlug:Lua2cHelloWorld1 + MyPlug:Lua2cHelloWorld2命令。
  4. 我的插件使用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是我的宏
    • Lua2cHelloWorld2我的C例程和Lua命令
    • 标记包含“Lua2cHelloWorld2”
    • 的字符串
    • argsJ包含导入Lua表的json-c对象作为输入参数提供。
    • 使用CTLP_ONLOAD例程在插件初始化时创建的句柄。

    技术上CTLP_LUA2C在我的示例中创建了一个静态C函数(Lua2cHelloWorld2)。然后它创建一个公共包装函数名称luac_Lua2cHelloWorld2,包装器是向前的。

    return((*Lua2cWrap)(luaState, MACRO_STR_VALUE(FuncName), FuncName))
    

    其中:

    • 在我的例子中,FuncName将被Lua2cHelloWorld2取代。
    • 在CTLP_REGISTER的插件初始化时间内提供Lua2cWrap。大多数人可能在这里直接使用luawrapper公共功能名称。然而,在我的项目(汽车级Linux音频控制器)中,出于安全动机,守护程序不会公开任何公共入口点,并且需要特定的导入机制。

    我的代码可用here.

    1. 宏在ctl-binding.h中定义
    2. ctl-plugin-sample.c中的插件示例
    3. 在ctl-dispatch.c中解析JSON配置文件(搜索pluginJ
    4. ctl-lua.c中的Lua包装器(搜索Lua2cWrapper)
    5. 关于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不透明句柄的系统。我实现了三个例程来处理这个问题

      1. LuaCtxPush创建上下文句柄
      2. LuaCtxCheck检索上下文句柄并验证其有效
      3. LuaCtxFree发布句柄
      4. 这允许实现非常简单的函数,该函数需要由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;
        }