第十七章 C++与Lua的相互绑定之ELuna

Posted C/C++的编程教室

tags:

篇首语:本文由小常识网(cha138.com)小编为大家整理,主要介绍了第十七章 C++与Lua的相互绑定之ELuna相关的知识,希望对你有一定的参考价值。

上一章节里面我们介绍了ELuna大部分功能,同时我们也增添了很多常用功能,但是我们还没有介绍如何在C++中调用Lua函数。

在C++中调用Lua函数

ELuna 中使用 LuaFunction 这个类来完成 Lua 函数的调用的,他的实现如下:


template<typename RL>
class LuaFunction {
public:
    ~LuaFunction() {
        luaL_unref(m_luaState, LUA_REGISTRYINDEX, m_ref);
        m_luaState = NULL;
    }

    LuaFunction(lua_State* L, const char* funcName): m_luaState(L) {
        lua_getglobal(L, funcName);

        if (lua_isfunction(L, -1)) {
            m_ref = luaL_ref(L, LUA_REGISTRYINDEX);
            b_isvalid = true;
        } else {
            printf("%s is not a lua function!\n", funcName);
            b_isvalid = false;
        }
    };


    bool IsValid(){
        return b_isvalid;
    }

    RL operator()() {
        if (!b_isvalid){
            return RL();
        }

        lua_pushcclosure(m_luaState, error_log, 0);               
        int stackTop = lua_gettop(m_luaState);

        lua_rawgeti(m_luaState, LUA_REGISTRYINDEX, m_ref);
        lua_pcall(m_luaState, 01, stackTop);                  

        RL result = read2cpp<RL>(m_luaState, -1);
        lua_settop(m_luaState, -3);                                
        return result;
    }

    template<typename P1>
    RL operator()(P1 p1) {
        if (!b_isvalid){
            return RL();
        }
        lua_pushcclosure(m_luaState, error_log, 0);
        int stackTop = lua_gettop(m_luaState);

        lua_rawgeti(m_luaState, LUA_REGISTRYINDEX, m_ref);
        push2lua(m_luaState, p1);
        lua_pcall(m_luaState, 11, stackTop);

        RL result = read2cpp<RL>(m_luaState, -1);
        lua_settop(m_luaState, -3);
        return result;
    }

    .....
};



同其他的操作一样,ELuna 最多能够支持九个参数的函数调用,通常来说这已经是足够的,但是其实在实际项目开发中有些时候为了能够把更多信息带到Lua中来处理可能会使用更多的参数,嗯,当然可以通过其他的方式解决这个问题,比如我们可以一些额外的信息作为全局变量带到lua中,自然使用local变量也是可以的。


现在我们可以在C++中使用 LuaFunction 来执行Lua 脚本函数:

--
-- test.lua
--

function test_fun(a,b,c)
    print("hello lua")
    print(a,b,c)
    return a + b + c
end


lua_State* L = ELuna::openLua();
ELuna::doFile(L, "test.lua");
ELuna::LuaFunction<double> test_fun(L, "test_fun");
if (test_fun.IsValid()){
    std::cout << test_fun(10.0,100.0,1000.0) << std::endl;
}



总的来说 ELuna 还是挺不错的,只需要一个头文件就能够解决大部分 C++ 和 Lua 之间的相互绑定,Eluna 使用了大量的宏定义来完成和模板的特化来完成整个绑定过程,这是优点其实也是一个缺陷,有点就是代码逻辑清晰,一看就懂,缺点就是调式无法追踪,再者扩展需要重新添加相应的宏定义实为不便,所以我们可以使用更加优雅(更加晦涩难懂)的方式来替代这些大量的宏定义。


扩展普通成员函数的注册方式

下面是 ELuna 对普通成员函数的注册方式


///////////////////////////////////////////////////////////////////////////////
// bind cpp method
///////////////////////////////////////////////////////////////////////////////
#define ELUNA_METHODCLASSES_PARAM_LIST_0 typename T
#define ELUNA_METHODCLASSES_PARAM_LIST_1 ELUNA_METHODCLASSES_PARAM_LIST_0, typename P1
#define ELUNA_METHODCLASSES_PARAM_LIST_2 ELUNA_METHODCLASSES_PARAM_LIST_1, typename P2
#define ELUNA_METHODCLASSES_PARAM_LIST_3 ELUNA_METHODCLASSES_PARAM_LIST_2, typename P3
#define ELUNA_METHODCLASSES_PARAM_LIST_4 ELUNA_METHODCLASSES_PARAM_LIST_3, typename P4
#define ELUNA_METHODCLASSES_PARAM_LIST_5 ELUNA_METHODCLASSES_PARAM_LIST_4, typename P5
#define ELUNA_METHODCLASSES_PARAM_LIST_6 ELUNA_METHODCLASSES_PARAM_LIST_5, typename P6
#define ELUNA_METHODCLASSES_PARAM_LIST_7 ELUNA_METHODCLASSES_PARAM_LIST_6, typename P7
#define ELUNA_METHODCLASSES_PARAM_LIST_8 ELUNA_METHODCLASSES_PARAM_LIST_7, typename P8
#define ELUNA_METHODCLASSES_PARAM_LIST_9 ELUNA_METHODCLASSES_PARAM_LIST_8, typename P9
#define ELUNA_METHODCLASSES_SP_PARAM_LIST_0 T
#define ELUNA_METHODCLASSES_SP_PARAM_LIST_1 ELUNA_METHODCLASSES_SP_PARAM_LIST_0, P1
#define ELUNA_METHODCLASSES_SP_PARAM_LIST_2 ELUNA_METHODCLASSES_SP_PARAM_LIST_1, P2
#define ELUNA_METHODCLASSES_SP_PARAM_LIST_3 ELUNA_METHODCLASSES_SP_PARAM_LIST_2, P3
#define ELUNA_METHODCLASSES_SP_PARAM_LIST_4 ELUNA_METHODCLASSES_SP_PARAM_LIST_3, P4
#define ELUNA_METHODCLASSES_SP_PARAM_LIST_5 ELUNA_METHODCLASSES_SP_PARAM_LIST_4, P5
#define ELUNA_METHODCLASSES_SP_PARAM_LIST_6 ELUNA_METHODCLASSES_SP_PARAM_LIST_5, P6
#define ELUNA_METHODCLASSES_SP_PARAM_LIST_7 ELUNA_METHODCLASSES_SP_PARAM_LIST_6, P7
#define ELUNA_METHODCLASSES_SP_PARAM_LIST_8 ELUNA_METHODCLASSES_SP_PARAM_LIST_7, P8
#define ELUNA_METHODCLASSES_SP_PARAM_LIST_9 ELUNA_METHODCLASSES_SP_PARAM_LIST_8, P9
#define ELUNA_PARAM_LIST_0
#define ELUNA_PARAM_LIST_1 ELUNA_PARAM_LIST_0  P1
#define ELUNA_PARAM_LIST_2 ELUNA_PARAM_LIST_1, P2
#define ELUNA_PARAM_LIST_3 ELUNA_PARAM_LIST_2, P3
#define ELUNA_PARAM_LIST_4 ELUNA_PARAM_LIST_3, P4
#define ELUNA_PARAM_LIST_5 ELUNA_PARAM_LIST_4, P5
#define ELUNA_PARAM_LIST_6 ELUNA_PARAM_LIST_5, P6
#define ELUNA_PARAM_LIST_7 ELUNA_PARAM_LIST_6, P7
#define ELUNA_PARAM_LIST_8 ELUNA_PARAM_LIST_7, P8
#define ELUNA_PARAM_LIST_9 ELUNA_PARAM_LIST_8, P9
#define ELUNA_READ_METHOD_PARAM_LIST_0
#define ELUNA_READ_METHOD_PARAM_LIST_1 ELUNA_READ_METHOD_PARAM_LIST_0  read2cpp<P1>(L,2)
#define ELUNA_READ_METHOD_PARAM_LIST_2 ELUNA_READ_METHOD_PARAM_LIST_1, read2cpp<P2>(L,3)
#define ELUNA_READ_METHOD_PARAM_LIST_3 ELUNA_READ_METHOD_PARAM_LIST_2, read2cpp<P3>(L,4)
#define ELUNA_READ_METHOD_PARAM_LIST_4 ELUNA_READ_METHOD_PARAM_LIST_3, read2cpp<P4>(L,5)
#define ELUNA_READ_METHOD_PARAM_LIST_5 ELUNA_READ_METHOD_PARAM_LIST_4, read2cpp<P5>(L,6)
#define ELUNA_READ_METHOD_PARAM_LIST_6 ELUNA_READ_METHOD_PARAM_LIST_5, read2cpp<P6>(L,7)
#define ELUNA_READ_METHOD_PARAM_LIST_7 ELUNA_READ_METHOD_PARAM_LIST_6, read2cpp<P7>(L,8)
#define ELUNA_READ_METHOD_PARAM_LIST_8 ELUNA_READ_METHOD_PARAM_LIST_7, read2cpp<P8>(L,9)
#define ELUNA_READ_METHOD_PARAM_LIST_9 ELUNA_READ_METHOD_PARAM_LIST_8, read2cpp<P9>(L,10)

#define ELUNA_MAKE_METHODCLASSX(N)\
template<typename RL, ELUNA_METHODCLASSES_PARAM_LIST_##N >\

struct MethodClass##N : GenericMethod {\
    typedef RL (T::* TFUNC)(ELUNA_PARAM_LIST_##N);\
    TFUNC m_func;\
    MethodClass##N(const char* name, TFUNC func): GenericMethod(name), m_func(func) {};\
    ~MethodClass##N() {};\
    inline virtual int call(lua_State *L) {\
        T* obj = read2cpp<T*>(L, 1);\
        push2lua(L, (obj->*m_func)(ELUNA_READ_METHOD_PARAM_LIST_##N));\
        return 1;\
    };\
};

#define ELUNA_MAKE_REF_RL_METHODCLASSX(N)\
template<typename RL, ELUNA_METHODCLASSES_PARAM_LIST_##N >\

struct MethodClass##N<RL&, ELUNA_METHODCLASSES_SP_PARAM_LIST_##N> : GenericMethod {\
    typedef RL& (T::* TFUNC)(ELUNA_PARAM_LIST_##N);\
    TFUNC m_func;\
    MethodClass##N(const char* name, TFUNC func): GenericMethod(name), m_func(func) {};\
    ~MethodClass##N() {};\
    inline virtual int call(lua_State *L) {\
        T* obj = read2cpp<T*>(L, 1);\
        push2lua<RL&>(L, (obj->*m_func)(ELUNA_READ_METHOD_PARAM_LIST_##N));\
        return 1;\
    };\
};

#define ELUNA_MAKE_VOID_RL_METHODCLASSX(N) \
template<ELUNA_METHODCLASSES_PARAM_LIST_##N >\

struct MethodClass##N<void, ELUNA_METHODCLASSES_SP_PARAM_LIST_##N> : GenericMethod {\
    typedef void (T::* TFUNC)(ELUNA_PARAM_LIST_##N);\
    TFUNC m_func;\
    MethodClass##N(const char* name, TFUNC func): GenericMethod(name), m_func(func) {};\
    ~MethodClass##N() {};\
    inline virtual int call(lua_State *L) {\
        T* obj = read2cpp<T*>(L, 1);\
        (obj->*m_func)(ELUNA_READ_METHOD_PARAM_LIST_##N);\
        return 0;\
    };\
};



这是 ELuna 实现C++ 普通成员函数绑定的基础,下面还有十个注册函数,分别是参数从0个到9个的函数。虽然看上去代码比较繁多,但是逻辑还是很清晰的,这里就不一一例举,所以如果我们要在原来的基础上扩展的话那么就是根据上面的例子添加需要扩展的宏最后再增加相应的注册函数即可,当然这里我们采用更加优雅的方式,如下:


template<int N, class Obj,class RLclass T>
struct LuaMemFunHelpRead;


//
// 有返回值
//
template<class Objclass RLclass...ArgsType>
struct LuaMemFunHelpRead<0, Obj, RL, mjTL::MTypeList<ArgsType...>>{

    template<class...Args>
    static RL Apply(lua_StateLObjobjRL(Obj:
:*Fun)(ArgsType...), Args...args){
        return ApplyRun(obj, Fun, args...);
    }
    template<class...Args>
    static RL ApplyRun(ObjobjRL(Obj:
:*Fun)(ArgsType...), Args...args){
        return (obj->*Fun)(args...);
    }
};

template<class Objclass RLclass...ArgsType>
struct LuaMemFunHelpRead<1, Obj,RL, mjTL::MTypeList<ArgsType...>>{

    template<class...Args>
    static RL Apply(lua_StateLObjobjRL(Obj:
:*Fun)(ArgsType...), Args...args){
        typedef typename mjTL::index_type<mjTL::MTypeList<ArgsType...>, 0>::type __type;
        return ApplyRun(obj,Fun, read2cpp<__type>(L, 2), n...);
    }
    template<class...Args>
    static RL ApplyRun(ObjobjRL(Obj:
:*Fun)(ArgsType...), Args...args){
        return (obj->*Fun)(args...);
    }
};

template<int N, class Objclass RLclass...ArgsType>
struct LuaMemFunHelpRead<N, Obj, RL, mjTL::MTypeList<ArgsType...>>{

    template<class...Args>
    static RL Apply(lua_StateLObjobjRL(Obj:
:*Fun)(ArgsType...), Args...args){
        typedef typename mjTL::index_type<mjTL::MTypeList<ArgsType...>, N - 1>::type __type;
        return LuaMemFunHelpRead<N - 1, Obj, RL, mjTL::MTypeList<ArgsType...>>::Apply(L, obj, Fun, read2cpp<__type>(L, N + 1), args...);
    }
};


//
// 无返回值
//

template<class Objclass...ArgsType>
struct LuaMemFunHelpRead<0, Obj, void, mjTL::MTypeList<ArgsType...>>{

    template<class...Args>
    static void Apply(lua_StateLObjobjvoid(Obj:
:*Fun)(ArgsType...), Args...args){
        ApplyRun(obj, Fun, args...);
    }
    template<class...Args>
    static void ApplyRun(Objobjvoid(Obj:
:*Fun)(ArgsType...), Args...args){
        (obj->*Fun)(args...);
    }
};


template<class Objclass...ArgsType>
struct LuaMemFunHelpRead<1, Obj,void, mjTL::MTypeList<ArgsType...>>{

    template<class...Args>
    static void Apply(lua_StateLObjobjvoid(Obj:
:*Fun)(ArgsType...), Args...args){
        typedef typename mjTL::index_type<mjTL::MTypeList<ArgsType...>, 0>::type __type;
        ApplyRun(obj,Fun, read2cpp<__type>(L, 2), args...);
    }
    template<class...Args>
    static void ApplyRun(Objobjvoid(Obj:
:*Fun)(ArgsType...), Args...args){
        (obj->*Fun)(args...);
    }
};

template<int N,class Obj,class...ArgsType>
struct LuaMemFunHelpRead<N, Obj,void, mjTL::MTypeList<ArgsType...>>{

    template<class...Args>
    static void Apply(lua_StateLObjobjvoid(Obj:
:*Fun)(ArgsType...), Args...args){
        typedef typename mjTL::index_type<mjTL::MTypeList<ArgsType...>, N - 1>::type __type;
        LuaMemFunHelpRead<N - 1, Obj, void, mjTL::MTypeList<ArgsType...>>::Apply(L, obj,Fun, read2cpp<__type>(L, N + 1), args...);
    }
};

template<class T,class RL,class...ArgsType>
struct MMethodClass :
 GenericMethod {
    typedef RL(T::* TFUNC)(ArgsType...);
    TFUNC m_func;
    MMethodClass(const char* name, TFUNC func) : GenericMethod(name), m_func(func) {};
    ~MMethodClass() {};
    inline virtual int call(lua_State *L) {
        T* obj = read2cpp<T*>(L, 1);
        push2lua<RL>(L, LuaMemFunHelpRead<sizeof...(ArgsType), T, RL, mjTL::MTypeList<ArgsType...>>::Apply(L, obj, m_func));
        return 1;
    };
};

template<class T,  class...ArgsType>
struct MMethodClass<T,void,ArgsType...> :
 GenericMethod {
    typedef void(T::* TFUNC)(ArgsType...);
    TFUNC m_func;
    MMethodClass(const char* name, TFUNC func) : GenericMethod(name), m_func(func) {};
    ~MMethodClass() {};
    inline virtual int call(lua_State *L) {
        T* obj = read2cpp<T*>(L, 1);
        LuaMemFunHelpRead<sizeof...(ArgsType), T, void, mjTL::MTypeList<ArgsType...>>::Apply(L, obj, m_func);
        return 0;
    };
};


template<typename T, typename RL, typename...ArgsType>
inline void registerMethodEx(lua_State* L, const char* name, RL(T::*func)(ArgsType...){
    luaL_getmetatable(L, ClassName<T>::getName());

    if (lua_istable(L, -1)) {
        lua_pushstring(L, name);
        new (lua_newuserdata(L, sizeof(MMethodClass<T, RL, ArgsType...>)))
            MMethodClass<T, RL, ArgsType...>(name, func);
        lua_pushcclosure(L, &proxyMethodCall, 1);
        lua_rawset(L, -3);
    }
    else {
        printf("please register class %s\n", ClassName<T>::getName());
    }
    lua_pop(L, 1);
}


registerMethodEx  和原来的比起就是不受参数个数的限制,直到编译器无法支持。


扩展const成员函数的注册方式

在扩展了普通成员函数的注册后对于const的扩展其实很简单了,ELuna 原本是不支持注册const成员函数的,下面是const成员函数的绑定实现方式

template<int N, class Objclass RLclass T>
struct LuaConstMemFunHelpRead;


template<class Objclass RLclass...ArgsType>
struct LuaConstMemFunHelpRead<0, Obj, RL, mjTL::MTypeList<ArgsType...>>{

    template<class...Args>
    static RL Apply(lua_StateLObjobjRL(Obj:
:*Fun)(ArgsType...)const, Args...args){
        return ApplyRun(obj, Fun, args...);
    }
    template<class...Args>
    static RL ApplyRun(ObjobjRL(Obj:
:*Fun)(ArgsType...)const, Args...args){
        return (obj->*Fun)(args...);
    }
};

template<class Objclass RLclass...ArgsType>
struct LuaConstMemFunHelpRead<1, Obj, RL, mjTL::MTypeList<ArgsType...>>{

    template<class...Args>
    static RL Apply(lua_StateLObjobjRL(Obj:
:*Fun)(ArgsType...)const, Args...args){
        typedef typename mjTL::index_type<mjTL::MTypeList<ArgsType...>, 0>::type __type;
        return ApplyRun(obj, Fun, read2cpp<__type>(L, 2), args...);
    }
    template<class...Args>
    static RL ApplyRun(ObjobjRL(Obj:
:*Fun)(ArgsType...)const, Args...args){
        return (obj->*Fun)(args...);
    }
};

template<int N, class Objclass RLclass...ArgsType>
struct LuaConstMemFunHelpRead<N, Obj, RL, mjTL::MTypeList<ArgsType...>>{

    template<class...Args>
    static RL Apply(lua_StateLObjobjRL(Obj:
:*Fun)(ArgsType...)const, Args...args){
        typedef typename mjTL::index_type<mjTL::MTypeList<ArgsType...>, N - 1>::type __type;
        return LuaConstMemFunHelpRead<N - 1, Obj, RL, mjTL::MTypeList<ArgsType...>>::Apply(L, Fun, read2cpp<__type>(L, N + 1), args...);
    }
};

template<class Objclass...ArgsType>
struct LuaConstMemFunHelpRead<0, Obj, void, mjTL::MTypeList<ArgsType...>>{

    template<class...Args>
    static void Apply(lua_StateLObjobjvoid(Obj:
:*Fun)(ArgsType...)const, Args...args){
        ApplyRun(obj, Fun, args...);
    }
    template<class...Args>
    static void ApplyRun(Objobjvoid(Obj:
:*Fun)(ArgsType...)const, Args...args){
        (obj->*Fun)(args...);
    }
};

template<class Objclass...ArgsType>
struct LuaConstMemFunHelpRead<1, Obj, void, mjTL::MTypeList<ArgsType...>>{

    template<class...Args>
    static void Apply(lua_StateLObjobjvoid(Obj:
:*Fun)(ArgsType...)const, Args...args){
        typedef typename mjTL::index_type<mjTL::MTypeList<ArgsType...>, 0>::type __type;
        ApplyRun(obj, Fun, read2cpp<__type>(L, 2), args...);
    }
    template<class...Args>
    static void ApplyRun(Objobjvoid(Obj:
:*Fun)(ArgsType...)const, Args...args){
        (obj->*Fun)(args...);
    }
};

template<int N, class Objclass...ArgsType>
struct LuaConstMemFunHelpRead<N, Obj, void, mjTL::MTypeList<ArgsType...>>{

    template<class...Args>
    static void Apply(lua_StateLObjobjvoid(Obj:
:*Fun)(ArgsType...)const, Args...args){
        typedef typename mjTL::index_type<mjTL::MTypeList<ArgsType...>, N - 1>::type __type;
        LuaConstMemFunHelpRead<N - 1, Obj, void, mjTL::MTypeList<ArgsType...>>::Apply(L, obj, Fun, read2cpp<__type>(L, N + 1), args...);
    }
};

template<class Tclass RLclass...ArgsType>
struct MMethodConstClass :
 GenericMethod {
    typedef RL(T::* TFUNC)(ArgsType...)const;
    TFUNC m_func;
    MMethodConstClass(const char* name, TFUNC func) : GenericMethod(name), m_func(func) {};
    ~MMethodConstClass() {};
    inline virtual int call(lua_State *L) {
        T* obj = read2cpp<T*>(L, 1);
        push2lua<RL>(L, LuaConstMemFunHelpRead<sizeof...(ArgsType), T, RL, mjTL::MTypeList<ArgsType...>>::Apply(L, obj, m_func));
        return 1;
    };
};

template<class Tclass...ArgsType>
struct MMethodConstClass<T, void, ArgsType...> :
 GenericMethod{
    typedef void(T::* TFUNC)(ArgsType...)const;
    TFUNC m_func;
    MMethodConstClass(const char* name, TFUNC func) : GenericMethod(name), m_func(func) {};
    ~MMethodConstClass() {};
    inline virtual int call(lua_State *L) {
        T* obj = read2cpp<T*>(L, 1);
        LuaConstMemFunHelpRead<sizeof...(ArgsType), T, void, mjTL::MTypeList<ArgsType...>>::Apply(L, obj, m_func);
        return 0;
    };
};


template<typename T, typename RL, typename...ArgsType>
inline void registerConstMethodEx(lua_State* L, const char* name, RL(T::*func)(ArgsType...)const{
    luaL_getmetatable(L, ClassName<T>::getName());

    if (lua_istable(L, -1)) {
        lua_pushstring(L, name);
        new (lua_newuserdata(L, sizeof(MMethodConstClass<T, RL, ArgsType...>)))
            MMethodConstClass<T, RL, ArgsType...>(name, func);
        lua_pushcclosure(L, &proxyMethodCall, 1);
        lua_rawset(L, -3);
    }
    else {
        printf("please register class %s\n", ClassName<T>::getName());
    }
    lua_pop(L, 1);
}



我们可以看到const 和 普通的成员函数绑定方式的实现是差不多的,唯一的区别就是成员函数的声明方式上,下面就是自由函数的绑定了,自由函数和类的成员函数比起其实就是少了一个this参数而已,所以实现起来参考成员函数即可。


注册自由函数的扩展

好吧,直接看代码吧:


template<int N, class RLclass T>
struct LuaFreeFunHelpRead;


template<class RLclass...ArgsType>
struct LuaFreeFunHelpRead<0, RL, mjTL::MTypeList<ArgsType...>>{

    template<class...Args>
    static RL Apply(lua_StateLRL(*Fun)(ArgsType...), Args... args){

        return ApplyRun(Fun, args...);
    }
    template<class...Args>
    static RL ApplyRun(RL(*Fun)(ArgsType...), Args... args){

        return (*Fun)(args...);
    }
};

template<class RLclass...ArgsType>
struct LuaFreeFunHelpRead<1, RL, mjTL::MTypeList<ArgsType...>>{

    template<class...Args>
    static RL Apply(lua_StateLRL(*Fun)(ArgsType...), Args...args){

        typedef typename mjTL::index_type<mjTL::MTypeList<ArgsType...>, 0>::type __type;
        return ApplyRun(Fun, read2cpp<__type>(L, 1), args...);
    }
    template<class...Args>
    static RL ApplyRun(RL(*Fun)(ArgsType...), Args...args){

        return (*Fun)(args...);
    }
};

template<int N, class RLclass...ArgsType>
struct LuaFreeFunHelpRead<N, RL, mjTL::MTypeList<ArgsType...>>{

    template<class...Args>
    static RL Apply(lua_StateLRL(*Fun)(ArgsType...), Args...args){

        typedef typename mjTL::index_type<mjTL::MTypeList<ArgsType...>, N - 1>::type __type;
        return LuaFreeFunHelpRead<N - 1, RL, mjTL::MTypeList<ArgsType...>>::Apply(L, Fun, read2cpp<__type>(L, N), args...);
    }
};


template<class...ArgsType>
struct LuaFreeFunHelpRead<1, void, mjTL::MTypeList<ArgsType...>>{

    template<class...Args>
    static void Apply(lua_StateLvoid(*Fun)(ArgsType...), Args...args){

        typedef typename mjTL::index_type<mjTL::MTypeList<ArgsType...>, 0>::type __type;
        ApplyRun(Fun, read2cpp<__type>(L, 1), args...);
    }
    template<class...Args>
    static void ApplyRun(void(*Fun)(ArgsType...), Args...args){

        (*Fun)(args...);
    }
};

template<int N, class...ArgsType>
struct LuaFreeFunHelpRead<N, void, mjTL::MTypeList<ArgsType...>>{

    template<class...Args>
    static void Apply(lua_StateLvoid(*Fun)(ArgsType...), Args...args){

        typedef typename mjTL::index_type<mjTL::MTypeList<ArgsType...>, N - 1>::type __type;
        LuaFreeFunHelpRead<N - 1void, mjTL::MTypeList<ArgsType...>>::Apply(L, Fun, read2cpp<__type>(L, N), args...);
    }
};

template<typename RL,typename...ArgsType >
struct MFunctionClass : GenericFunction{
    typedef RL(*TFUNC)(ArgsType...);
    TFUNC m_func;
    MFunctionClass(const char* name, TFUNC func) : GenericFunction(name), m_func(func) {};
    ~MFunctionClass() {};
    inline virtual int call(lua_State *L) {
        push2lua<RL>(L, LuaFreeFunHelpRead<sizeof...(ArgsType), RL, mjTL::MTypeList<ArgsType...>>::Apply(L, m_func));
        return 1;
    };
};

template<typename...ArgsType >
struct MFunctionClass<void, ArgsType...> : GenericFunction{
    typedef void(*TFUNC)(ArgsType...);
    TFUNC m_func;
    MFunctionClass(const char* name, TFUNC func) : GenericFunction(name), m_func(func) {};
    ~MFunctionClass() {};
    inline virtual int call(lua_State *L) {
        LuaFreeFunHelpRead<sizeof...(ArgsType), void, mjTL::MTypeList<ArgsType...>>::Apply(L, m_func);
        return 0;
    };
};

template<typename RL,typename...ArgsType>
inline void registerFunctionEx(lua_State* L, const char* name, RL(*func)(ArgsType...){
    new (lua_newuserdata(L, sizeof(MFunctionClass<RL, ArgsType...>))) MFunctionClass<RL, ArgsType...>(name, func);
    lua_pushcclosure(L, proxyFunctionCall, 1);
    lua_setglobal(L, name);
}



发现什么了吗?其实这些代码的相似度都很高,那么是不是可以将其抽象出来只需要写一个模板就可以了呢?也许吧!有兴趣的同学可以下去尝试一下,虽然我没这么去做,但总觉得应该是可以的,至少可以更简化一些。


C++操作Lua函数的扩展


开篇的时候我们说了LuaFunction局限性,所以现在我们来扩展这个操作

template<class Tclass...ArgsType>
struct LuaFunHelp;


template<class T>
struct LuaFunHelp<T>{

    static void Apply(lua_State* L, T val){
        push2lua(L, val);
    }
};

template<class Tclass...ArgsType>
struct LuaFunHelp
{

    static void Apply(lua_State* L, const T& val, ArgsType... args){
        push2lua(L, val);
        LuaFunHelp<ArgsType...>::Apply(L, args...);
    }
};


template<class R>
struct MLuaFunEx{

    MLuaFunEx(lua_State* L, const char* funName) :m_luaState(L), mFunName(funName){
        lua_getglobal(L, funName);
        if (lua_isfunction(m_luaState, -1)) {
            m_ref = luaL_ref(L, LUA_REGISTRYINDEX);
            b_isvalid = true;
        }
        else {
            printf("%s is not a lua function!\n", funName);
            b_isvalid = false;
        }
    }

    ~MLuaFunEx() {
        luaL_unref(m_luaState, LUA_REGISTRYINDEX, m_ref);
        m_luaState = NULL;
    }

    bool IsValid(){
        return b_isvalid;
    }

    operator()(){
        if (!b_isvalid){
            return R();
        }
        lua_pushcclosure(m_luaState, error_log, 0);
        int stackTop = lua_gettop(m_luaState);
        lua_rawgeti(m_luaState, LUA_REGISTRYINDEX, m_ref);
        lua_pcall(m_luaState, 01, stackTop);
        R result = read2cpp<R>(m_luaState, -1);
        lua_settop(m_luaState, -3);
        return result;
    }

    template<class...ArgsType>
    R operator()(ArgsType... args){

        if (!b_isvalid){
            return R();
        }
        lua_pushcclosure(m_luaState, error_log, 0);
        int stackTop = lua_gettop(m_luaState);
        lua_rawgeti(m_luaState, LUA_REGISTRYINDEX, m_ref);
        LuaFunHelp<ArgsType...>::Apply(m_luaState, args...);
        lua_pcall(m_luaState, sizeof...(ArgsType), 1, stackTop);
        R result = read2cpp<R>(m_luaState, -1);
        lua_settop(m_luaState, -3);
        return result;
    }

private:
    const char*  mFunName;
    bool         b_isvalid{ false };
    int             m_ref{ 0 };
    lua_State*     m_luaState{ nullptr };
};

template<>
struct MLuaFunEx<void>{
    MLuaFunEx(lua_State* L, const char* funName) :m_luaState(L), mFunName(funName){
        lua_getglobal(L, funName);
        if (lua_isfunction(m_luaState, -1)) {
            m_ref = luaL_ref(L, LUA_REGISTRYINDEX);
            b_isvalid = true;
        }
        else {
            printf("%s is not a lua function!\n", funName);
            b_isvalid = false;
        }
    }

    ~MLuaFunEx() {
        luaL_unref(m_luaState, LUA_REGISTRYINDEX, m_ref);
        m_luaState = NULL;
    }

    bool IsValid(){
        return b_isvalid;
    }

    void operator()(){
        if (!b_isvalid){
            return;
        }
        lua_pushcclosure(m_luaState, error_log, 0);
        int stackTop = lua_gettop(m_luaState);
        lua_rawgeti(m_luaState, LUA_REGISTRYINDEX, m_ref);
        lua_pcall(m_luaState, 01, stackTop);
        lua_settop(m_luaState, -3);
    }

    template<class... ArgsType>
    void operator()(ArgsType... args){

        if (!b_isvalid){
            return;
        }
        lua_pushcclosure(m_luaState, error_log, 0);
        int stackTop = lua_gettop(m_luaState);
        lua_rawgeti(m_luaState, LUA_REGISTRYINDEX, m_ref);
        LuaFunHelp<ArgsType...>::Apply(m_luaState, args...);
        lua_pcall(m_luaState, sizeof...(ArgsType), 1, stackTop);
        lua_settop(m_luaState, -3);
    }

private:
    const char*  mFunName;
    bool         b_isvalid{ false };
    int             m_ref{ 0 };
    lua_State*     m_luaState{ nullptr };
};


LuaObject


为了更好的交互所以这里增加一个LuaObject的东西,简单的用于表示Lua和C++的一些常规类型,比如:


--
-- test.lua
--

abc = 100
obj = {}
mp = {}
mp.hello = "world"
mp.nihao = "nihao"

ab = BaseClass()
function show()
    print("obj len = ",#obj,ab:get_a(),ab:get_b(),#mp)
    for i=1,#obj do
        print(obj[i])
    end

    for i,v in pairs(mp) do
        print(i,v)
    end

    print(abc)
end

function test_fun(a,b,c)
    print("hello lua")
    print(a,b,c)
    return a + b + c
end



下面我们来简单的测试一下:

struct BaseClass
{

    double a{ 10 };
    double b{ 20 };
}; 

int main(int argc, char* argv[])
{

    lua_State* L = ELuna::openLua();
    ELuna::registerClass<BaseClass>(L, "BaseClass", ELuna::constructor<BaseClass>);
    ELuna::registerProperty<BaseClass, double>(L, "a", &BaseClass::a);
    ELuna::registerProperty<BaseClass, double>(L, "b", &BaseClass::b);

    ELuna::doFile(L, "test.lua");
    ELuna::MLuaFunEx<double> test_fun(L, "test_fun");
    if (test_fun.IsValid()){
        std::cout << test_fun(10.0,100.0,1000.0) << std::endl;;
    }

    ELuna::LuaObject mObj(L, "obj");
    ELuna::LuaObject mObj2(L, "ab");
    ELuna::LuaObject mObj3(L, "mp");
    ELuna::LuaObject mObj4(L, "abc");
    std::cout << mObj4.get<int>() << std::endl;
    BaseClass* obj2 = mObj2.get<BaseClass*>();
    std::vector<int> vec = { 123456789 };
    mObj.set(vec);
    ELuna::MLuaFunEx<void> mFun(L, "show");
    if (mFun.IsValid()){
        mFun();
    }

    //
    // 同步更新lua中的值
    //
    obj2->a = 100.2;
    obj2->b = 78956.1547;
    if (mFun.IsValid()){
        mFun();
    }

    //
    // 修改Lua中的值
    //
    BaseClass abc;
    abc.a = 78953.12;
    abc.b = 4578862.65;
    mObj2.set(abc);

    std::map<std::stringstd::string> m;
    m["hello"] = "hehehee";
    m["nihao"] = "3213131";
    mObj3.set(m);
    mObj4.set(10000);
    std::vector<int> vec2 = { 12,16,15,85,34 };
    mObj.set(vec2);
    if (mFun.IsValid()){
        mFun();
    }

    ELuna::closeLua(L);
    system("pause");
    return 0;
}



其实LuaObject的实现很简单,就提供了一个get和set函数:

template<class T>
struct __value_lua__{

    static void to_lua(lua_State* L, const T& val){
        push2lua(L, val);
    }

    static T to_cpp(lua_State* L, int index){
        return read2cpp<T>(L, index);
    }
};

template<>
struct __value_lua__<std::string>{
    static void to_lua(lua_State* L, const std::string& val){
        push2lua(L, val.c_str());
    }

    static std::string to_cpp(lua_State* L, int index){
        return read2cpp<const char*>(L, index);
    }
};


template<class T>
struct MTLLuaToValue{

    static T ToValue(lua_State* L){
        if (lua_isuserdata(L, -1)){
            UserData<T>* ud = static_cast<UserData<T>*>(lua_touserdata(L, -1));
            return *ud->m_objPtr;
        }
        else{
            return T();
        }
    }

    static void FromValue(lua_State* L, T val, const char* name){
        if (lua_isuserdata(L, -1)){
            UserData<T>* ud = static_cast<UserData<T>*>(lua_touserdata(L, -1));
            *ud->m_objPtr = val;
        }
    }
};

template<class T>
struct MTLLuaToValue<T*>{

    static T* ToValue(lua_State* L){
        if (lua_isuserdata(L, -1)){
            UserData<T>* ud = static_cast<UserData<T>*>(lua_touserdata(L, -1));
            return ud->m_objPtr;
        }
        else{
            return nullptr;
        }
    }

    static void FromValue(lua_State* L, T* val, const char* name){
        if (lua_isuserdata(L, -1)){
            UserData<T>* ud = static_cast<UserData<T>*>(lua_touserdata(L, -1));
            ud->m_objPtr = val;
        }
    }
};


template<>
struct MTLLuaToValue<unsigned __int64>{
    static unsigned __int64 ToValue(lua_State* L){
        return lua_tointeger(L, -1);
    }
    static void FromValue(lua_State* L, unsigned __int64 val, const char* name){
        lua_pushinteger(L, val);
        lua_setglobal(L, name);
    }
};

template<>
struct MTLLuaToValue<__int64> : MTLLuaToValue<unsigned __int64>{};

template<>
struct MTLLuaToValue<unsigned __int32> : MTLLuaToValue<unsigned __int64>{};

template<>
struct MTLLuaToValue<__int32> : MTLLuaToValue<unsigned __int64>{};

template<>
struct MTLLuaToValue<unsigned __int16> : MTLLuaToValue<unsigned __int64>{};

template<>
struct MTLLuaToValue<__int16> : MTLLuaToValue<unsigned __int64>{};

template<>
struct MTLLuaToValue<unsigned long> : MTLLuaToValue<unsigned __int64>{};

template<>
struct MTLLuaToValue<long> : MTLLuaToValue<unsigned __int64>{};


template<>
struct MTLLuaToValue<double>{
    static double ToValue(lua_State* L){
        return lua_tonumber(L, -1);
    }
    static void FromValue(lua_State* L, double val, const char* name){
        lua_pushnumber(L, val);
        lua_setglobal(L, name);
    }
};

template<>
struct MTLLuaToValue<float> : MTLLuaToValue<double>{};

template<>
struct MTLLuaToValue<const char*>{
    static const charToValue(lua_State* L){
        return lua_tostring(L, -1);
    }
    static void FromValue(lua_State* L, const char* val, const char* name){
        lua_pushstring(L, val);
        lua_setglobal(L, name);
    }
};

template<>
struct MTLLuaToValue<char*> {
    static charToValue(lua_State* L){
        return const_cast<char*>(lua_tostring(L, -1));
    }
    static void FromValue(lua_State* L, const char* val, const char* name){
        lua_pushstring(L, val);
        lua_setglobal(L, name);
    }
};

template<>
struct MTLLuaToValue<std::string>{
    static std::string ToValue(lua_State* L){
        return lua_tostring(L, -1);
    }
    static void FromValue(lua_State* L, const std::string& val, const char* name){
        lua_pushstring(L, val.c_str());
        lua_setglobal(L, name);
    }
};

template<>
struct MTLLuaToValue<bool>{
    static bool ToValue(lua_State* L){
        return lua_toboolean(L, -1);
    }
    static void FromValue(lua_State* L, bool val, const char* name){
        lua_pushboolean(L, val);
        lua_setglobal(L, name);
    }
};

template<class T>
struct MTLLuaToValue<std::vector<T>>{

    static std::vector<T> ToValue(lua_State* L){
        std::vector<T> res;
        int n = luaL_len(L, -1);
        for (int i = 0; i < n; ++i){
            lua_rawgeti(L, -1, i + 1);
            T val = MTLLuaToValue<T>::ToValue(L);
            res.push_back(val);
            lua_pop(L, 1);
        }
        return res;
    }

    static void FromValue(lua_State* L, const std::vector<T>& val, const char* name){
        if (!lua_istable(L, -1)){
            return;
        }
        int n = luaL_len(L, -1);
        if (n > val.size()){
            for (int i = 0; i < n; ++i){
                lua_pushinteger(L, i + 1);
                lua_pushnil(L);
                lua_settable(L, -3);
            }
        }
        for (int i = 0; i < val.size(); ++i){
            lua_pushinteger(L, i + 1);
            __value_lua__<T>::to_lua(L, val.at(i));
            lua_settable(L, -3);
        }
    }
};

template<class K,class V>
struct MTLLuaToValue<std::map<K,V>>{

    static std::map<K, V> ToValue(lua_State* L){
        std::map<K, V> res;
        return res;
    }

    static void FromValue(lua_State* L, const std::map<K, V>& val, const char* name){
        if (!lua_istable(L, -1)){
            return;
        }
        for (auto& m : val){
            __value_lua__<K>::to_lua(L, m.first);
            __value_lua__<V>::to_lua(L, m.second);
            lua_settable(L, -3);
        }
    }
};


struct LuaObject{
    lua_State* m_luaState;
    const char* mName;
    LuaObject(lua_State* L, const char* name) :m_luaState(L), mName(name){

    }

    ~LuaObject(){
        m_luaState = NULL;
    }

    template<class T>
    T get(){

        lua_getglobal(m_luaState, mName);
        return MTLLuaToValue<T>::ToValue(m_luaState);
    }

    template<class T>
    void set(const Tval){

        lua_getglobal(m_luaState, mName);
        MTLLuaToValue<T>::FromValue(m_luaState, val, mName);
    }
};



LuaObject 不仅能够表示常规的简单类型,还能够表示已经注册了的 userdata ,对于标准库容器仅仅支持 std::vector 的读写,对于 std::map 支持写不支持读。



关于 ELuna 的介绍和扩展就到这里吧,现在的ELuna的功能其实已经很强大了。


以上是关于第十七章 C++与Lua的相互绑定之ELuna的主要内容,如果未能解决你的问题,请参考以下文章

《构建之法》读第十七章收获

《构建之法》——第第十七章

谈谈我对构建之法第四章与第十七章的理解

Week4-作业1:《构建之法》第四章第十七章 阅读笔记与思考

《构建之法》(第十七章)读书笔记

读《构建之法》第四章 第十七章