zoukankan      html  css  js  c++  java
  • 注册C函数与类成员函数到lua

    lua中调用c函数,我们通常要将c函数转换成相应的注册函数,也就是如下形式

           int function(lua_State *L) {}

    可是如果我们每个都函数都这么写,既重复了太多的工作量,又容易出错,所以自然想到了用一层代理来连接注册函数与本来的c函数。于是我们可以这样

           int function(lua_State *L)

           {

                  //do something

                  return callfun();  //这里callfun()就调用了我们本来的c函数

    }

     

    可是对于callfun我们怎么实现呢。在c语言里面,我们可以使用函数指针来实现我们的函数功能。如对于函数 int myprint(),我们可以声明一个 int(*fn)() = myprint指针,然后通过fu()调用,这样就跟直接调用myprint()一样了,所以我们考虑callfun函数如下:

    template <typename RT>

    int callfun(RT (*func)(), lua_State *L, int index )

    {

           RT ret = func();   //调用函数,得到返回值

           Push(L, ret);     //将返回值压入lua堆栈

           return 1;

    }

     

    这里实现的是形如RT fun() 的函数调用,对于有多个参数的情况,我们可以定义不同的callfun函数重载实现,如

     

    template <typename RT, typename P1>

    int callfun(RT (*func)(P1), ……)

     

    这样定义有虽然实现起来比较简单,但是对于参数较多的情况就比较难以书写了,不过对于一般的函数,参数都不可能太多(如果太多了我想应该就要重写该函数了),所以我们就按照这样实现。

     

    由于考虑到简易说明,声明要处理的函数类型为 int fun(),既参数为0返回值为int

          

    于是我们可以定义我们的代理注册函数如下

           template <typename Func>

    int registry_function(lua_State *L);

    现在我们需要做的就是怎样把我们的函数指针在代理注册里面传给callfun,这里我们需要用到一些lua的实现方法,当lua在把c函数压入堆栈的时候,会把参数存储到upvalue里面,而我们会把registry_function压入堆栈,所以很自然的我们将把upvalue的第一个值作为函数指针的值。而对于函数指针来说,在lua里面就是一个userdata,所以我们可以这样实现:

    template <typename Func>

    int registry_function(lua_State *L)

    {

           //取出函数指针

    unsigned char* buffer = (unsigned char*)lua_touserdata(L, lua_upvalueindex(1));

         //调用函数

    return callFunc(*(Func*)(buffer), L, 1);

    }

     

    下面我们的问题就在于怎么样把函数指针放入upvalue中以及将我们的代理注册函数注册进lua,我们定义如下函数:

     

    template <typename Func>

    void lua_pushdirectclosure(lua_State *L, Func func, unsigned int nupvalues)

    {

         //创建userdata并把func指针的值拷贝进去

         unsigned char* buffer = (unsigned char*)lua_newuserdata(L, sizeof(func));

         memcpy(buffer, &func, sizeof(func));

     

         lua_pushcclosure(L, registry_function <Func>, nupvalues + 1);

    }

     

         首先我们创建一个userdata并把函数指针赋值给它,然后将registry_function注册给lua,这时由于userdata为第一个参数,所以我们可以在调用registry_function的时候取出使用。在通过call函数调用原本的c函数。

     

         为了考虑到方便实现,我们定义一个宏:

         #define lua_directregistry_function(L, func) /

         lua_pushstring(L, #func); /

         lua_pushdirectclosure(L, func, 0); /

         lua_settable(L, LUA_GLOBALSINDEX);

        

         在程序里面,假设我们的函数为 int myprint() {}

         则我们在c语言里面使用如下

         lua_directregistry_function(L, myprint);

         然后在lua里面就可以通过myprint()调用该函数了。

     

         以上是实现的在lua里面对不同类型的c语言函数进行封装调用,其实重点就是通过改函数的函数指针来进行操作,其实对于类里面的成员函数,我们同样可以注册进入lua,然后像一般函数进行调用。

         对于类的成员函数,我们需要类的成员函数指针来操作,假设有一个类

     

         class A

         {

         public:

             int test();  

    };

     

    我们可以这样定义test的函数指针

     

    int (A::*fun)();

    fun = &A::test;

     

    使用的时候我们可以这样:

     

    A a;

    (a.*fun)();

     

    对以对我们来说,实现类的成员函数注册重点就是操作类的成员函数的函数指针。我们仍然把该函数指针存放到upvalue的第一个值处。

    首先是call函数声明,由于有了类,所以如下:

     

    template <typename Cla, typename RT>

    int callfunc(Cla &cla, RT (Cla::*func)(), )

    {

         RT ret = (cla.*func)();

         //do something

    }

     

    然后就是把成员函数指针的值拷入userdata中:

         unsigned char* buffer = (unsigned char*)lua_newuserdata(L, sizeof(Cla) + sizeof(func));

         memcpy(buffer, &cla, sizeof(Cla));

    memcpy(buffer + sizeof(Cla), &func, sizeof(func));

     

         而在我们的代理注册函数里面,调用call函数如下:

         callfunc(*(Cla*)buffer, *(Func*)(buffer + sizeof(Cla)), L, 1);

     

         由于对于一般的类成员函数来说(静态除外),我们的调用方式是class.function(),所以在userdata中我们需要保存两个值,一个是该类的地址,一个是类的函数的地址。

     

         这样我么就可以把类的成员函数也注册给了lua

     

         以下为C函数与类成员函数封装代码(为了简便,函数都是 int fun() 形式):

        

         /*

         调用真正的C函数,现已int func()作为特例。

        

         参数 func 函数指针,指向参数为返回值为int类型的函数

                  L lua变量

                  index lua栈中索引

        

         对于其他的类型,可用模板实现

         如对于一个参数的函数,实现如下

         template <typname RT, typename P1>

         int callFunc(RT (*func)(P1), lua_state *L, int index)

         {

             //Get 通过index索引得到在lua栈中的值并转换成P1类型

             //Push 把函数的返回值压入堆栈

             RT ret = func(Get(Type<P1>(), L, index + 0));

             Push(L, ret);

             return 1;

         }

         */

     

         int callFunc(int (*func)(), lua_State *L, int index)

         {

              int ret = func();

              lua_pushnumber(L, ret);

              return 1;

         }

     

         //函数指针相关数据会存到UpValue的第一个值中,此处从upvalue中取出

         unsigned char* getFirstUpValue(lua_State *L)

         {

              unsigned char* buffer = (unsigned char*)lua_touserdata(L, lua_upvalueindex(1));

              return buffer;

         }

     

         /*

              实现callFunclua调用形式封装

         */

         template <typename Func>

         int directCallFunc(lua_State *L)

         {

              //得到函数指针

              unsigned char* buffer = getFirstUpValue(L);

              //转换成相应的函数调用

              return callFunc(*(Func*)(buffer), L, 1);

         }

     

         /*

              directCallFunc注册进lua

         */

         template <typename Func>

         void lua_pushdirectclosure(lua_State *L, Func func, unsigned int nupvalues)

         {

              //创建userdata并把func指针的值拷贝进去

              unsigned char* buffer = (unsigned char*)lua_newuserdata(L, sizeof(func));

              memcpy(buffer, &func, sizeof(func));

     

              lua_pushcclosure(L, directCallFunc<Func>, nupvalues + 1);

         }

     

     

     

         /*

              实现对class里面memeber function的调用

              参数cla 要调用的类的实例

                   Cla::*func 类的函数指针

         */

         template <typename Cla>

         int callMemFunc(Cla &cla, int (Cla::*func)(), lua_State *L, int index)

         {

              int ret = (cla.*func)();

              lua_pushnumber(L, ret);

              return 1;

         }

     

         /*

              实现callMemFunclua调用形式封装

         */

         template <typename Cla, typename Func>

         int directCallMemFunc(lua_State *L)

         {

              //得到函数指针

              unsigned char* buffer = getFirstUpValue(L);

              //转换成相应的函数调用

              return callMemFunc(*(Cla*)(buffer), *(Func*)(buffer + sizeof(Cla)), L, 1);

         }

     

         /*

              directCallMemFunc注册进lua

         */

         template <typename Cla, typename Func>

         void lua_pushdirectmemclosure(lua_State *L, Cla &cla, Func func, unsigned int nupvalues)

         {

              //创建userdata并把clafunc指针的值拷贝进去

              unsigned char* buffer = (unsigned char*)lua_newuserdata(L, sizeof(Cla) +            sizeof(func));

              memcpy(buffer, &cla, sizeof(Cla));

              memcpy(buffer + sizeof(Cla), &func, sizeof(func));

              lua_pushcclosure(L, directCallMemFunc<Cla, Func>, nupvalues + 1);

         }

     

         #define lua_directregistry_function(L, func) /

              lua_pushstring(L, #func); /

              lua_pushdirectclosure(L, func, 0); /

              lua_settable(L, LUA_GLOBALSINDEX);

     

         #define lua_directregistry_memfunction(L, name, cla, func) /

              lua_pushstring(L, name); /

              lua_pushdirectmemclosure(L, cla, func, 0); /

              lua_settable(L, LUA_GLOBALSINDEX);

     

         使用的时候我们通过lua_directregistry_function()注册c函数,
        
    通过lua_directregistry_memfunction()注册类成员函数,其中name为该成员函数在lua中使用的函数名。

        

        

     
  • 相关阅读:
    JS Dom_API
    JS 动态表格(添加、删除行)
    将本地网页上传到 apache2 及 github 的步骤
    软件工程之美 第一周
    树莓派安装芯片驱动并测试
    Visoul Studio 2019 远程调试 中文乱码
    Visoul Studio 2019 远程调试 RaspberryPi C 项目
    课设提纲
    PHP PDO 一 : 常用方法
    设置子域名及申请其证书
  • 原文地址:https://www.cnblogs.com/xiaowangba/p/6313802.html
Copyright © 2011-2022 走看看