20140102-lua binder另一只轮子的雏形

书接上一回,说到要继续丰富对类型的处理。那么如何才能做到呢,应该是要支持自定义的,所以这一回要讲的就是在前面的基础上,增加支持自定义部分,其中包含以下几个部分

  • 函数的默认参数设置,包括有几个默认参数和如何设置这些默认参数;
  • 可以做为返回值的参数下标(Lua支持多值返回);
  • 如何将函数的结果和当做返回值的函数的参数压入到Lua;
  • 如何将函数的结果压入到Lua中(看起来和上一点重复,默认情况下是调用上一条进行压入Lua);
  • 将当做返回值的函数的参数(所有需要返回的)压入Lua;
  • 如何确定中间存放函数参数的值的类型;
  • 从Lua中提取函数的参数;
  • 将中间存放的函数的参数的值打扮成函数需要的实参;

发现没有,关键的一条,函数的调用居然没有支持自定义。

说了这么多,我们先来瞄一眼改装后的打包过程大概是个什么样子:

   lua_register(L, "h", (
                        xLB_CFunc::b<decltype(&h), // type of function
                        &h, // pointer of function
                        0, // count of default parameters for function
                        xLB_dfer, // setter for default parameters for function
                        xLB_idxer<>, // index of parameters for return
                        xLB_pper, // how each value pushed into Lua
                        xLB_rner, // how the result of function pushed into Lua
                        xLB_pter, // how those parameters after function calling pushed into Lua
                        My_vter, // decide what type of value for saving the function parameters
                        My_lver, // how the value load from Lua
                        xLB_vper // make values as function's parameters
                        >()));

感觉象不象一怪物,只是绑定一个小小的函数,居然要写这么多代码。实际上支持了默认参数,如果不做自定义,只要前面两个模板参数就可以了。当然,提供更多更简便的包装办法是必须的,得等下一篇了。

主要的C++11技术前两篇都说得差不多了,这里要提一下的是,怎么设置函数的默认参数,实际上默认参数设置的自定义支持是一个星期天才写的(新鲜)。为什么要说它呢,它和别的自定义支持有啥不同呢,是因为默认参数是值,不是类型,也不是函数,没办法做为模板参数来提供给打包模板(其实你早就知道了对不)。所以C++11提供的选择不过,听说以后的版本会支持将值和对象一类的东东做为模板参数(谁说值不能做为模板参数,象int类型的值不是可以么)。说得太不准确了,一般编译器的提示是none type一类。因为我们是要支持默认参数自定义,默认参数可是啥类型的都有,唯一的选择就是把这一过程打包成一个函数,然后这个函数就可以做为模板参数传过去了(敢不敢来个直接点的)。那么它大概长啥样呢

struct xLB_dfer {
        template<class TUP, int PARAM_COUNT> struct xLB_tir {
                static inline void go(TUP& tuple) {}
        };
};

template<class TUP> struct xLB_dfer::xLB_tir<TUP, 0> {
        static inline void go(TUP& tuple) {}
};

template<class TUP, class T, int Idx>
static inline void xLB_default_assign(TUP& tuple, const T& A) {
        std::get<Idx>(tuple) = A;
}

template<int BaseIdx, class TUP, class Idxer, class...T>
struct xLB_setdp {};

template<int BaseIdx, class TUP, int...Idxs, class...T>
struct xLB_setdp<BaseIdx, TUP, xLB_idxer<Idxs...>, T...> {
        static inline void go(TUP& tuple, T...DA) {
                xLB_each { 
                        (xLB_default_assign<TUP, T, BaseIdx+Idxs>(tuple,DA),1)...
                };
        }
};

template<class TUP, class...T>
static inline void xLB_set_default_param(TUP& tuple, T...DA) {
        using idxer_t = typename xLB_toidxer<T...>::type;
                xLB_setdp<std::tuple_size<TUP>::value - sizeof...(T), TUP, idxer_t, T...>
                        ::go(tuple, DA...);
}

又是一堆模板,看烦了没有,先看看怎么自定义一个设置函数

struct My_dfer : public xLB_dfer {};
template<class TUP>
        struct My_dfer::xLB_tir<TUP, 1> {
                static inline void go(TUP& tuple) {
                        //std::get<0>(tuple) = 9999;
                        xLB_set_default_param(tuple, 8888);
                }
        };

这看起来还差不多,函数过程中的那两句是相同功能的,提供xLB_set_default_param是为了方便代码编写,畅想一下,用一个宏(支持不定参数),上面的代码就会变得非常直观。

从这个设置函数的编写,可以猜到其它的自定义支持过程是怎么样的,没错,跟这个几乎是一模一样,都是从xLB提供的默认类型继承,并提供特化函数。特化类型全部是以xLB_tir命名,其执行函数的名称都叫go。

再来看两个自定义,一个是类型,另一个是从Lua中提取函数参数

struct My_vter : public xLB_vter {};
template<> struct My_vter::xLB_tir<clsA*> { using type = clsA*; };

struct My_lver : public xLB_lver {};
template<int BaseIdx, int idx, class TUP>
        struct My_lver::xLB_tir<BaseIdx, idx, TUP, clsA*> {
                static inline void go(lua_State* L, TUP& tuple, int top) {
                        if (idx+BaseIdx <= top) {
                                auto wrap = reinterpret_cast<xLB_ludwrap<clsA>*>(lua_touserdata(L, idx+BaseIdx));
                                auto obj = wrap->ptr(); assert(obj != nullptr);
                                std::get<idx>(tuple) = obj;
                        }
                }
        };

代码

xlb.h

#ifndef _XLB_H
#define _XLB_H

#include <iostream>
#include <vector>
#include <assert.h>
#include <cstring>
#include <tuple>
#include <memory>
#include <type_traits>
using namespace std;

extern "C" {
#include "lua.h"
#include "lualib.h"
#include "lauxlib.h"
} // lua header files

/*---------------------------------------------------------------------------
predeclare
-----------------------------------------------------------------------------*/
template<typename T> struct xLB_xotrait;
template<typename T> struct xLB_function;
template<typename T> struct xLB_xobase;


// tir (Type Information Replenish)
/*---------------------------------------------------------------------------
xLB_ludwrap : light user data wrapp as user data
-----------------------------------------------------------------------------*/
/* @struct xLB_ludwrap
 * This template used to wrapper lightuserdata as userdata, and then we can
 * set metatable on its. It's instance life managered by C++ not by Lua. */
template<typename T> struct xLB_ludwrap {
        T* ptr(){ return __ptr; }
        T* operator->() { return __ptr; }
        xLB_ludwrap(T* ptr, bool del=false) : __ptr(ptr), __del(del) {}
        virtual ~xLB_ludwrap() { if (__del) { delete __ptr; } }
protected:
        T* __ptr; /**< real object */
        bool __del; /**< delete __ptr when xLB_ludwrap was release */
}; // end of xLB_ludwrap

/*---------------------------------------------------------------------------
xLB_creatidxer
-----------------------------------------------------------------------------*/
template<int...> struct xLB_idxer{}; 
template<int...RPI> using xLB_rpi = xLB_idxer<RPI...>; // xLB_idxer alias

template<int, class Idxer, int> 
        struct xLB_creatidxer;

template<int I, int...Idxs, int RM>
        struct xLB_creatidxer<I, xLB_idxer<Idxs...>, RM> {
                using type = typename 
                        xLB_creatidxer<I+1, xLB_idxer<Idxs..., I>, RM-1>::type;
        };

template<int I, int...Idxs>
        struct xLB_creatidxer<I, xLB_idxer<Idxs...>, 0> {
                typedef xLB_idxer<Idxs...> type;
        };

template<typename ...Types> 
        struct xLB_toidxer : 
                xLB_creatidxer<0, xLB_idxer<>, sizeof...(Types)> {};

/*---------------------------------------------------------------------------
xLB_return_void
-----------------------------------------------------------------------------*/
template<typename Tx> struct xLB_return_void {
                static const bool value = true;
        };

template<typename Tx, class R, class ...A> 
        struct xLB_return_void<R (Tx::*)(A...)> {
                static const bool value = std::is_void<R>::value;
        };

template<class R, class ...A>
 struct xLB_return_void<R (*)(A...)> {
         static const bool value = std::is_void<R>::value;
 };


/*---------------------------------------------------------------------------
xLB_each
-----------------------------------------------------------------------------*/
struct xLB_each{ template<class ...T> xLB_each(T...) {} };

/*---------------------------------------------------------------------------
xLB_dfer
function default parameters provider
-----------------------------------------------------------------------------*/
struct xLB_dfer {
        template<class TUP, int PARAM_COUNT> struct xLB_tir {
                static inline void go(TUP& tuple) {}
        };
};

template<class TUP> struct xLB_dfer::xLB_tir<TUP, 0> {
        static inline void go(TUP& tuple) {}
};

template<class TUP, class T, int Idx>
static inline void xLB_default_assign(TUP& tuple, const T& A) {
        std::get<Idx>(tuple) = A;
}

template<int BaseIdx, class TUP, class Idxer, class...T>
struct xLB_setdp {};

template<int BaseIdx, class TUP, int...Idxs, class...T>
struct xLB_setdp<BaseIdx, TUP, xLB_idxer<Idxs...>, T...> {
        static inline void go(TUP& tuple, T...DA) {
                xLB_each { 
                        (xLB_default_assign<TUP, T, BaseIdx+Idxs>(tuple,DA),1)...
                };
        }
};

template<class TUP, class...T>
static inline void xLB_set_default_param(TUP& tuple, T...DA) {
        using idxer_t = typename xLB_toidxer<T...>::type;
                xLB_setdp<std::tuple_size<TUP>::value - sizeof...(T), TUP, idxer_t, T...>
                        ::go(tuple, DA...);
}

/*---------------------------------------------------------------------------
xLB_lver
-----------------------------------------------------------------------------*/
struct xLB_lver {
        template<int BaseIdx, int idx, class TUP, class V> struct xLB_tir {
                        static inline void go(lua_State* L, TUP& tuple, int top) {
                                printf("Warning: xLB_lver(%s) not implemented.\n", typeid(V).name());
                        }
                };
};

template<int BaseIdx, int idx, class TUP>
struct xLB_lver::xLB_tir<BaseIdx, idx, TUP, int> {
        static inline void go(lua_State* L, TUP& tuple, int top) {
                if (idx+BaseIdx <= top) {
                        std::get<idx>(tuple) = lua_tointeger(L, idx+BaseIdx);
                }
        }
};

template<int BaseIdx, int idx, class TUP>
struct xLB_lver::xLB_tir<BaseIdx, idx, TUP, double> {
        static inline void go(lua_State* L, TUP& tuple, int top) {
                if (idx+BaseIdx <= top) {
                        std::get<idx>(tuple) = lua_tonumber(L, idx+BaseIdx);
                }
        }
};

/*---------------------------------------------------------------------------
xLB_fver
-----------------------------------------------------------------------------*/
template<int BaseIdx, class LVER, class IDXER, class...TA>
        struct xLB_fver {};

template<int BaseIdx, class LVER, int...idxs, class...TA>
        struct xLB_fver<BaseIdx, LVER, xLB_idxer<idxs...>, TA...> {
                using TUP = std::tuple<TA...>;
                static inline void go(lua_State* L, TUP& tuple) {
                        xLB_each{ (LVER::template 
                                        xLB_tir<BaseIdx,idxs,TUP,TA>::go(L, tuple, lua_gettop(L)),1)... };
                }
        };

/*---------------------------------------------------------------------------
xLB_pper
push data(parameters of function) into lua_State
-----------------------------------------------------------------------------*/
struct xLB_pper {
        template<typename T> struct xLB_tir {
                static inline void go(lua_State* L, T tuple_val, int& return_count) {
                        lua_pushnumber(L, tuple_val); ++return_count; 
                }
        };
};

template<> struct xLB_pper::xLB_tir<const char*> {
        static inline void go(lua_State* L, const char* tuple_val, int& return_count) { 
                lua_pushstring(L, tuple_val); ++return_count; 
        }
};

template<> struct xLB_pper::xLB_tir<double> { 
        static inline void go(lua_State* L, double tuple_val, int& return_count) { 
                lua_pushnumber(L, tuple_val); ++return_count; 
        }
};

template<> struct xLB_pper::xLB_tir<long> { 
        static inline void go(lua_State* L, long tuple_val, int& return_count) { 
                lua_pushnumber(L, tuple_val); ++return_count; 
        }
};

template<> struct xLB_pper::xLB_tir<bool> { 
        static inline void go(lua_State* L, bool tuple_val, int& return_count) { 
                lua_pushnumber(L, tuple_val); ++return_count; 
        }
};

/*---------------------------------------------------------------------------
xLB_pter
push addition parameters of function into lua_State
-----------------------------------------------------------------------------*/
struct xLB_pter { template<class,class,class> struct xLB_tir {}; };

template<int...RPI,class...A,class PPER> 
        struct xLB_pter::xLB_tir<xLB_idxer<RPI...>,std::tuple<A...>,PPER> {
                static inline void go(lua_State* L, std::tuple<A...>& tuple, int& return_count) {
                        xLB_each{ (PPER::template xLB_tir<
                                        typename std::tuple_element<RPI, std::tuple<A...>>::type>
                                ::go(L, std::get<RPI>(tuple), return_count), 1)... 
                        };
                }
        };

/*---------------------------------------------------------------------------
xLB_rner
push result of function into lua_State
-----------------------------------------------------------------------------*/
struct xLB_rner {
        template<class R, class PPER> struct xLB_tir {
                static inline void go(lua_State* L, const R& result_of_function, int& return_count) {
                        PPER::template xLB_tir<R>::go(L, result_of_function, return_count);
                }
        };
};

/*---------------------------------------------------------------------------
xLB_vper
change type of value according to parameter's type for calling function
-----------------------------------------------------------------------------*/
struct xLB_vper {
        template<class To, class From> struct xLB_tir {
                static inline const To& go(const From& tuple_val) { return tuple_val; } 
        };
};

template<class T>struct xLB_vper::xLB_tir<T*, T*> {
        static inline T* go(T* tuple_val) { return tuple_val; } 
};

template<>struct xLB_vper::xLB_tir<int&, int> {
        static inline int& go(int& tuple_val) { return tuple_val; } 
};

template<>struct xLB_vper::xLB_tir<int*, int> {
        static inline int* go(int& tuple_val) { return &tuple_val; } 
};

/*---------------------------------------------------------------------------
xLB_vter
decide type of value for saving data come from lua_State
-----------------------------------------------------------------------------*/
struct xLB_vter {
        template<class PARAM_TYPE> struct xLB_tir { 
                using type = PARAM_TYPE;
                static_assert(std::is_pod<PARAM_TYPE>::value, "Warning: xLB_vter no implement\n"); 
        };
};

template<class PARAM_TYPE> struct xLB_vter::xLB_tir<PARAM_TYPE&> {
        using type = typename xLB_vter::xLB_tir<PARAM_TYPE>::type;
};

template<> struct xLB_vter::xLB_tir<int*> { using type = int; };
template<> struct xLB_vter::xLB_tir<double*> { using type = double; };

/*---------------------------------------------------------------------------
xLB_caler
-----------------------------------------------------------------------------*/
struct xLB_caler {
        template<class FT,class,class,class...A> struct xLB_tir {};
};

template<class R, class T, class VPER, class...A, class...B, int...idxs> 
        struct xLB_caler::xLB_tir<R (T::*)(A...), xLB_idxer<idxs...>, VPER, B...> {
                static inline R go(T* obj, R (T::*f)(A...), std::tuple<B...>& tuple) {
                        return (obj->*f)(VPER::template xLB_tir<A,B>::go(std::get<idxs>(tuple))...);
                }
        };

template<class T, class VPER, class...A, class...B, int...idxs> 
        struct xLB_caler::xLB_tir<void (T::*)(A...), xLB_idxer<idxs...>, VPER, B...> {
                static inline void go(T* obj, void (T::*f)(A...), std::tuple<B...>& tuple) {
                        (obj->*f)(VPER::template xLB_tir<A,B>::go(std::get<idxs>(tuple))...);
                }
        };

template<class VPER, class...A, class...B, int...idxs> 
        struct xLB_caler::xLB_tir<void (*)(A...), xLB_idxer<idxs...>, VPER, B...> {
                static inline void go(void (*f)(A...), std::tuple<B...>& tuple) {
                        f(VPER::template xLB_tir<A,B>::go(std::get<idxs>(tuple))...);
                }
        };

template<class R, class VPER, class...A, class...B, int...idxs> 
        struct xLB_caler::xLB_tir<R (*)(A...), xLB_idxer<idxs...>, VPER, B...> {
                static inline R go(R (*f)(A...), std::tuple<B...>& tuple) {
                        return f(VPER::template xLB_tir<A,B>::go(std::get<idxs>(tuple))...);
                }
        };
/*---------------------------------------------------------------------------
xLB_adter
-----------------------------------------------------------------------------*/
template<class R, class T, class F, F f, int DFCOUNT, class DFER, class RPIdxer,
        class PPER, class RNER, class PTER, class VTER, class LVER, class VPER, class...A>
int xLB_adter(lua_State* L) {
        using wrap_t = xLB_ludwrap<T>;
        using tuple_t = std::tuple<typename VTER::template xLB_tir<A>::type...>;
        using idxer_t = typename xLB_toidxer<A...>::type;
        tuple_t tuple;
        DFER::template xLB_tir<tuple_t, DFCOUNT>::go(tuple);
        xLB_fver<2,LVER,idxer_t,typename VTER::template xLB_tir<A>::type...>
                ::go(L, tuple);
        auto wrap = reinterpret_cast<wrap_t*>(lua_touserdata(L, 1));
        assert(wrap != nullptr);
        auto obj = wrap->ptr(); assert(obj != nullptr);
        R r = xLB_caler::template xLB_tir<F,idxer_t,VPER,typename VTER::template xLB_tir<A>::type...>
                ::go(obj, f, tuple);
        int rcnt = 0;
        RNER::template xLB_tir<R,PPER>::go(L,r,rcnt);
        PTER::template xLB_tir<RPIdxer,tuple_t,PPER>
                ::go(L, tuple, rcnt);
        return rcnt;
}

/*---------------------------------------------------------------------------
xLB_adter_void
-----------------------------------------------------------------------------*/
template<class T, class F, F f, int DFCOUNT, class DFER, class RPIdxer,
        class PPER, class RNER, class PTER, class VTER, class LVER, class VPER, class...A>
int xLB_adter_void(lua_State* L) {
        using wrap_t = xLB_ludwrap<T>;
        using tuple_t = std::tuple<typename VTER::template xLB_tir<A>::type...>;
        using idxer_t = typename xLB_toidxer<A...>::type;
        tuple_t tuple;
        DFER::template xLB_tir<tuple_t, DFCOUNT>::go(tuple);
        xLB_fver<2,LVER,idxer_t,typename VTER::template xLB_tir<A>::type...>
                ::go(L, tuple);
        auto wrap = reinterpret_cast<wrap_t*>(lua_touserdata(L, 1));
        assert(wrap != nullptr);
        auto obj = wrap->ptr(); assert(obj != nullptr);
        xLB_caler::template xLB_tir<F,idxer_t,VPER,typename VTER::template xLB_tir<A>::type...>
                ::go(obj, f, tuple);
        int rcnt = 0;
        PTER::template xLB_tir<RPIdxer,tuple_t,PPER>
                ::go(L, tuple, rcnt);
        return rcnt;
}

/*---------------------------------------------------------------------------
xLB_CFunc
-----------------------------------------------------------------------------*/
struct xLB_CFunc {
        template<class F, F f, bool, int DFCOUNT, class DFER,
                        class RPIdxer, class PPER, class RNER, 
                        class PTER, class VTER, class LVER, class VPER> struct xLB_tir {};

        template<class FT, FT f,
                        int DFCOUNT=0,
                        class DFER=xLB_dfer,
                        class RPIdxer=xLB_idxer<>, 
                        class PPER=xLB_pper,
                        class RNER=xLB_rner,
                        class PTER=xLB_pter,
                        class VTER=xLB_vter, 
                        class LVER=xLB_lver, 
                        class VPER=xLB_vper
                > static inline lua_CFunction b() {
                                return xLB_CFunc::template xLB_tir<
                                                FT,f,xLB_return_void<FT>::value,DFCOUNT,DFER,
                                                RPIdxer,PPER,RNER,PTER,VTER,LVER,VPER
                                        >::func;
                        }
};

template<class...A, void (*f)(A...), int DFCOUNT, class DFER,
                class RPIdxer, class PPER, class RNER, 
                class PTER, class VTER, class LVER, class VPER
> struct xLB_CFunc::xLB_tir<void (*)(A...), f, true, DFCOUNT, DFER, RPIdxer, PPER, RNER, PTER, VTER, LVER, VPER> {
                static int func(lua_State* L) {
                        using tuple_t = std::tuple<typename VTER::template xLB_tir<A>::type...>;
                        using idxer_t = typename xLB_toidxer<A...>::type;
                        tuple_t tuple;
                        DFER::template xLB_tir<tuple_t, DFCOUNT>::go(tuple);
                        xLB_fver<1,LVER,idxer_t,typename VTER::template xLB_tir<A>::type...>
                                ::go(L, tuple);
                        xLB_caler::template xLB_tir<decltype(f),idxer_t,VPER,typename VTER::template xLB_tir<A>::type...>
                                ::go(f, tuple);
                        int rcnt = 0;
                        PTER::template xLB_tir<RPIdxer,tuple_t,PPER>
                                ::go(L, tuple, rcnt);
                        return rcnt;
                };
        };

template<class...A, class R, R (*f)(A...), int DFCOUNT, class DFER,
                class RPIdxer, class PPER, class RNER, 
                class PTER, class VTER, class LVER, class VPER
> struct xLB_CFunc::xLB_tir<R (*)(A...), f, false, DFCOUNT, DFER, RPIdxer, PPER, RNER, PTER, VTER, LVER, VPER> {
                static int func(lua_State* L) {
                        using tuple_t = std::tuple<typename VTER::template xLB_tir<A>::type...>;
                        using idxer_t = typename xLB_toidxer<A...>::type;
                        tuple_t tuple;
                        DFER::template xLB_tir<tuple_t, DFCOUNT>::go(tuple);
                        xLB_fver<1,LVER,idxer_t,typename VTER::template xLB_tir<A>::type...>
                                ::go(L, tuple);
                        R r = xLB_caler::template xLB_tir<decltype(f),idxer_t,VPER,typename VTER::template xLB_tir<A>::type...>
                                ::go(f, tuple);
                        int rcnt = 0;
                        RNER::template xLB_tir<R,PPER>::go(L,r,rcnt);
                        PTER::template xLB_tir<RPIdxer,tuple_t,PPER>
                                ::go(L, tuple, rcnt);
                        return rcnt;
                };
        };

/*---------------------------------------------------------------------------
xLB_agent
-----------------------------------------------------------------------------*/
template<class Tx,Tx,bool,int,class,class,class,class,class,class,class,class> 
        struct xLB_agent {};

template<class Tx, class R, class ...A, R (Tx::*f)(A...), int DFCOUNT, class DFER, int...RPI,
                class PPER, class RNER, class PTER, class VTER, class LVER, class VPER>
        struct xLB_agent<R (Tx::*)(A...), f, false, DFCOUNT, DFER, xLB_idxer<RPI...>,
                        PPER,RNER,PTER,VTER,LVER,VPER> {
                static inline void b(const char fn[]) { 
                        xLB_xobase<Tx>::regfunc(fn,
                                        xLB_adter<R, Tx, R (Tx::*)(A...), f, DFCOUNT, DFER, xLB_idxer<RPI...>,
                                                PPER,RNER,PTER,VTER,LVER,VPER, A...>);
                }
        };

template<class Tx, class ...A, void (Tx::*f)(A...), int DFCOUNT, class DFER, int...RPI,
                class PPER, class RNER, class PTER, class VTER, class LVER, class VPER>
        struct xLB_agent<void (Tx::*)(A...), f, true, DFCOUNT, DFER, xLB_idxer<RPI...>,
                        PPER,RNER,PTER,VTER,LVER,VPER> {
                static inline void b(const char fn[]) {
                        xLB_xobase<Tx>::regfunc(fn,
                                        xLB_adter_void<Tx, void (Tx::*)(A...), f, DFCOUNT, DFER, xLB_idxer<RPI...>,
                                                        PPER,RNER,PTER,VTER,LVER,VPER, A...>);
                }
        };


/*---------------------------------------------------------------------------
xlb function
-----------------------------------------------------------------------------*/
#define luaL_reg luaL_Reg

/** This function create metatable with lua api and set __index and metatable 
 * to itself and make it have key weak feature. */
void xLB_newmetatable(lua_State* L, const char* LibName, luaL_Reg* Lreg);

/** This function set metatable named LibName to userdata at top of stack. */
void xLB_userdata(lua_State* L, const char* LibName, lua_Number N = 0);


/*---------------------------------------------------------------------------
lua userdata and C++ object
-----------------------------------------------------------------------------*/
/** if the object specify by index is userdata then
 * 1. if it is instance of type xLB_ludwrap<T> then get T* address from it;
 * 2. if it is derived from T* then get T* from userdata;
 * otherwise return nullptr; */
template<typename T, const char meta[]>
T* xLB_getuserdata(lua_State* L, int index, xLB_ludwrap<T>** pWp = 0) {
        using w_t = xLB_ludwrap<T>;
        T* r = 0;
        if (lua_isuserdata(L, index)) {
                auto wp = reinterpret_cast<w_t*>(luaL_checkudata(L, index, meta));
                //auto wp = reinterpret_cast<w_t*>(lua_touserdata(L, index));
                if (wp) { r = wp->ptr(); if (pWp) { *pWp = wp; } }
        } //else { nb_warn(true, "userdata expected"); }
        return r;
}

/* Wrap xo as Lua ud, and Lua do not charge object's life. */
template<typename T>
void xLB_wrapxo(lua_State* L, T* obj) {
        typedef xLB_ludwrap<T> w_t;
        auto place = (w_t*)(lua_newuserdata(L, sizeof(w_t)));
        new(place) w_t(obj/*,false*/);
        xLB_userdata(L, xLB_xotrait<T>::meta_name, 0);
}

template<typename T, const char meta[]>
void xLB_objasud(lua_State* L, T* obj) {
        typedef xLB_ludwrap<T> w_t;
        auto place = (w_t*)(lua_newuserdata(L, sizeof(w_t)));
        new(place) w_t(obj, true);
        xLB_userdata(L, meta, 0);
}

/* Wrap xo as Lua ud, means Lua charge object's life. */
template<typename T>
void xLB_xoasud(lua_State* L, T* obj) {
        typedef xLB_ludwrap<T> w_t;
        w_t* place = static_cast<w_t*>(lua_newuserdata(L, sizeof(w_t)));
        new(place) w_t(obj, true);
        xLB_userdata(L, xLB_xotrait<T>::meta_name, 0);
}

template<typename T, const char meta[]>
int xLB_gcobj(lua_State* L) {
        xLB_ludwrap<T>* wp = 0;
        xLB_getuserdata<T, meta>(L, 1, &wp);
        if (wp) { wp->~xLB_ludwrap<T>();}
        return 0;
}

template<typename T, typename...A>
T* xLB_newxo(lua_State* L, A...arg_metas) {
        T* pobj = new T(arg_metas...);
        xLB_objasud<T, xLB_xotrait<T>::meta_name>(L, pobj);
        return pobj;
}

template<typename T>
int xLB_gcxo(lua_State* L) {
        xLB_ludwrap<T>* wp = nullptr;
        xLB_getuserdata<T, xLB_xotrait<T>::meta_name>(L, 1, &wp);
        if (wp) { wp->~xLB_ludwrap<T>(); }
        return 0;
}

/*---------------------------------------------------------------------------
xLB binder
-----------------------------------------------------------------------------*/
template<typename T>
        unique_ptr<xLB_xotrait<T>> xLB_newxobinder() {
                return unique_ptr<xLB_xotrait<T>>(new xLB_xotrait<T>());
        }

template<typename T>
        void xLB_bindxo(lua_State* L, const char* ns=nullptr) {
                xLB_xotrait<T>::reg(L, ns);
        }

template<typename T>
        void xLB_xoglobal(lua_State* L, T* obj, const char* name) {
                xLB_wrapxo(L, obj);
                lua_setglobal(L, name);
        }

/*---------------------------------------------------------------------------
xLB ns table
-----------------------------------------------------------------------------*/
#define xLB_pushtable(L, tbl, name, ns) tbl().reg(L, name, ns);
void xLB_newnstable(lua_State* L, const char* name, const char* ns);
void xLB_newnstable(lua_State* L, const char* tn, int index);
//void xLB_getnstable(lua_State* L, const char* name, const char* ns);

/*---------------------------------------------------------------------------
xo macro
-----------------------------------------------------------------------------*/
#define xLB_xoinitmember(xo_t) \
template<> const char xLB_xobase<xo_t>::meta_name[]="xLB_"#xo_t; \
template<> const char xLB_xobase<xo_t>::type_name[]=#xo_t; \
template<> xLB_pchars xLB_xobase<xo_t>::super_name_list=xLB_pchars(); \
template<> std::vector<luaL_reg> xLB_xobase<xo_t>::rg_meta={{nullptr,nullptr}}; \
template<> std::vector<luaL_reg> xLB_xobase<xo_t>::rg_type={{nullptr,nullptr}}; \

#define xLB_xodefineobj(xo_t) xLB_xotrait<xo_t> xLB_xo##xo_t; \

#define xLB_xodefine(xo_t) \
xLB_xoinitmember(xo_t) \
xLB_xodefineobj(xo_t) \

#define xLB_xodeclare(xo_t) \
template<> struct xLB_xotrait<xo_t> : public xLB_xobase<xo_t> \

/*---------------------------------------------------------------------------
xLB_method
-----------------------------------------------------------------------------*/
template<typename T> struct xLB_method {
        using type = int (*)(lua_State*, T*);
};

template<typename R, typename T, typename... A>
struct xLB_method<R (T::*)(A...) > {
        using type = R(T::*)(A...);
};

template<typename R, typename T, typename... A>
struct xLB_method<R (T::*)(A...) const > {
        using type = R(T::*)(A...) const;
};

template<typename T>
T* xLB_getxo(lua_State* L, int index) {
        return xLB_getuserdata<T, xLB_xotrait<T>::meta_name>(L, index);
}

template<typename T, typename xLB_method<T>::type f>
int xLB_xomethod(lua_State* L) {
        int rc = 0;
        T* obj = xLB_getxo<T>(L, 1);
        if (obj) rc = f(L, obj);
        return rc;
}

/*---------------------------------------------------------------------------
xLB_xotrait
-----------------------------------------------------------------------------*/
template<typename T> struct xLB_xotrait{ static char meta[]; };

typedef std::vector<const char*> xLB_pchars;
int xLB_search(lua_State* L, const char* method_name, const xLB_pchars& nlst);
int xLB_rawsearch(lua_State* L, const char* method_name, const char* meta_name);

/*---------------------------------------------------------------------------
xLB_xobase
-----------------------------------------------------------------------------*/
template<typename X>
struct xLB_xobase {
        typedef X T;
        typedef xLB_xobase self_t;
        typedef xLB_xobase* this_t;
        typedef xLB_xotrait<T> trait_t;
        typedef std::vector<luaL_reg> regs_t;
        static const char meta_name[];
        static const char type_name[];
        static xLB_pchars super_name_list;
        static regs_t rg_meta;
        static regs_t rg_type;
        //virtual ~xLB_xobase() = delete;

        static inline void regfunc(const char fn[], lua_CFunction f) {
                auto it = begin(rg_meta);
                rg_meta.insert(it, {fn,f});
        }

        static int index_implement(lua_State* L) { //__index(t,k)
                auto method_name = luaL_optlstring(L, 2, "", nullptr);
                int nFound = xLB_rawsearch(L, method_name, meta_name);
                if (!nFound) {
                        nFound = xLB_search(L, method_name, super_name_list);
                }
                return nFound;
        }

        static void newxometatable(lua_State* L) {
                luaL_newmetatable(L, trait_t::meta_name);
                lua_pushstring(L, "__index");
                lua_pushcfunction(L, index_implement);
                lua_rawset(L, -3);
                // set metatable.metatable to itself
                lua_pushvalue(L, -1);
                lua_setmetatable(L, -2);
                luaL_setfuncs(L, &rg_meta[0], 0);
                lua_pop(L, 1); // pop metatable
        }

        template<class SUPER_XO> static void super() { 
                super_name_list.push_back(xLB_xotrait<SUPER_XO>::meta_name); 
        }

        template<lua_CFunction f> static void c(const char fn[]="new") { 
                auto it = begin(rg_type);
                rg_type.insert(it, {fn,f});
        }

        static void d() { b<xLB_gcxo<T>>("__gc"); }

        template<typename xLB_method<T>::type f> static void b(const char fn[]) { 
                        regfunc(fn, xLB_xomethod<T, f>);
                }

        template<lua_CFunction f> static void b(const char fn[]) { 
                        regfunc(fn, f);
                }

        template<class FT, FT f, 
                int DFCOUNT = 0,
                class DFER=xLB_dfer,
                class RPIs=xLB_idxer<>, 
                class PPER=xLB_pper,
                class RNER=xLB_rner,
                class PTER=xLB_pter,
                class VTER=xLB_vter, 
                class LVER=xLB_lver, 
                class VPER=xLB_vper> 
        static void bx(const char fn[]) {
                xLB_agent<FT, f, xLB_return_void<FT>::value, DFCOUNT, DFER, RPIs,
                                PPER,RNER,PTER,VTER,LVER,VPER
                >::b(fn);
        }

        static void reg(lua_State* L, const char* ns = nullptr) {
                if (1 < rg_meta.size()) { newxometatable(L); }
                if (1 < rg_type.size()) {
                        luaL_newlib(L, &rg_type[0]);
                        if (ns && strlen(ns)) {
                                lua_getglobal(L, ns);
                                if (LUA_TTABLE != lua_type(L, -1)) {
                                        lua_pop(L, 1);
                                        lua_newtable(L);
                                        lua_setglobal(L, ns);
                                        lua_getglobal(L, ns);
                                }
                                lua_pushstring(L, trait_t::type_name);
                                lua_pushvalue(L, -3);
                                lua_rawset(L, -3);
                                lua_pop(L, 2);
                        }
                }
        }
}; // end of xLB_xobase

/*---------------------------------------------------------------------------
xLB_table
-----------------------------------------------------------------------------*/
template<typename X>
struct xLB_table {
        typedef X T;
        typedef xLB_table self_t;
        typedef xLB_table* this_t;
        typedef std::vector<luaL_reg> regs_t;
        virtual ~xLB_table() {}
        regs_t rg_table;
        template<lua_CFunction f> void c(const char fn[]) { 
                rg_table.push_back({fn,f});
        }
        template<lua_CFunction f>
                void b(const char fn[]) { 
                        rg_table.push_back({fn,f});
                }
        void reg(lua_State* L, const char* tn, const char* ns = 0) {
                if (0 < rg_table.size()) {
                        rg_table.push_back({nullptr,nullptr});
                        luaL_newlib(L, &rg_table[0]);
                        if (ns && strlen(ns)) {
                                lua_getglobal(L, ns);
                                int t = lua_type(L, -1);
                                if (LUA_TNIL == t || LUA_TTABLE != t ) {
                                        lua_pop(L, 1);
                                        lua_newtable(L);
                                        lua_setglobal(L, ns);
                                        lua_getglobal(L, ns);
                                }
                                lua_pushstring(L, tn);
                                lua_pushvalue(L, -3);
                                lua_rawset(L, -3);
                                lua_pop(L, 2);
                        }
                }
        }
        void reg(lua_State* L, const char* tn, int index = 0) {
                if (0 < rg_table.size()) {
                        rg_table.push_back({nullptr,nullptr});
                        lua_pushstring(L, tn);
                        luaL_newlib(L, &rg_table[0]);
                        if (index < 0) { index-=2; }
                        if (index) { lua_rawset(L, index); }
                }
        }
}; // end of xLB_table

#endif // end of __XLB_H__

xlb.cpp

#include <ns/xlb.h>

/*---------------------------------------------------------------------------
lux_
-----------------------------------------------------------------------------*/
void xLB_newmetatable(lua_State* L, const char* LibName, luaL_Reg* Lreg) {
        luaL_newmetatable(L, LibName);
        lua_pushstring(L, "__index");
        lua_pushvalue(L, -2);
        lua_rawset(L, -3);
        // set metatable to "key" weak table
        //lua_pushstring(L, "__mode");
        //lua_pushstring(L, "k");
        //lua_rawset(L, -3);
        // set metatable.metatable to itself
        lua_pushvalue(L, -1);
        lua_setmetatable(L, -2);
        //luaL_register(L, nullptr, Lreg);
        luaL_setfuncs(L, Lreg, 0/*no upvalue for funcs to share*/);
        lua_pop(L, 1); // pop metatable
}

int xLB_search(lua_State* L, 
const char* method_name, const xLB_pchars& nlst) {
        int nFound = 0;
        for (auto meta_name : nlst) {
                lua_getfield(L, LUA_REGISTRYINDEX, meta_name);
                if (!lua_istable(L, -1)) { 
                        //nb_warn(true, "%s not registered", meta_name); 
                        return 0;
                } else {
                        lua_pushstring(L, method_name);
                        lua_gettable(L, -2);
                        lua_replace(L, -2);
                        nFound = (lua_isnil(L, -1) ? 0 : 1);
                        if (nFound) break;
                }
        }
        return nFound;
}

int xLB_rawsearch(lua_State* L, 
const char* method_name, const char* meta_name) {
        //nb_warn(true, "search: %s in %s", method_name, meta_name);
        lua_getfield(L, LUA_REGISTRYINDEX, meta_name);
        if (!lua_istable(L, -1)) { 
                //nb_warn(true, "%s not registered", meta_name); 
                return 0;
        } else {
                lua_pushstring(L, method_name);
                lua_rawget(L, -2);
                lua_replace(L, -2);
                return (lua_isnil(L, -1) ? 0 : 1);
        }
}

void xLB_userdata(lua_State* L, const char* LibName, lua_Number N) {
        // s: 1(ud)
        //int inew = 
                luaL_newmetatable(L, LibName); // s: 1(ud), 2(metatable)
        //nb_warn(inew, "xLB-Error: %d %s", inew, LibName);
        lua_setmetatable(L, -2); // userdata.metatable = metatable
}


void xLB_newnstable(lua_State* L, const char* name, const char* ns) {
        lua_getglobal(L, ns);
        if (LUA_TTABLE == lua_type(L, -1)) {
                lua_pushstring(L, name);
                lua_newtable(L); 
                lua_rawset(L, -3);
        }
        lua_pop(L, 1);
}

void xLB_newnstable(lua_State* L, const char* tn, int index) {
        lua_pushstring(L, tn);
        lua_newtable(L);
        if (index < 0) { index-=2; }
        lua_rawset(L, index);
}

/*
void xLB_getnstable(lua_State* L, const char* name, const char* ns) {
        lua_getglobal(L, ns);
        lua_pushlstring(L, name, strlen(name));
        lua_rawget(L, -2);
        lua_insert(L, -2);
        lua_pop(L, 1);
}
*/

测试的一段代码

xlbinder.h

#ifndef _XLBINDER_H
#define _XLBINDER_H

#include <ns/xlb.h>
#include <type_traits>


struct clsB {
        void b() { printf("base class clsB::b() method called\n"); }
};

struct clsA : public clsB {
        int Add(int a, int& b);
        int Del(int* a);
        int Modify(int& a);
        void NoReturn(int a);
        void Complex(clsA* pa);
        int x = 999;
}; // end of clsA

xLB_xodeclare(clsB) {
        xLB_xotrait() {
                bx<decltype(&clsB::b), &clsB::b>("b");
        }
};

struct My_vter : public xLB_vter {};
template<> struct My_vter::xLB_tir<clsA*> { using type = clsA*; };

struct My_lver : public xLB_lver {};
template<int BaseIdx, int idx, class TUP>
        struct My_lver::xLB_tir<BaseIdx, idx, TUP, clsA*> {
                static inline void go(lua_State* L, TUP& tuple, int top) {
                        if (idx+BaseIdx <= top) {
                                auto wrap = reinterpret_cast<xLB_ludwrap<clsA>*>(lua_touserdata(L, idx+BaseIdx));
                                auto obj = wrap->ptr(); assert(obj != nullptr);
                                std::get<idx>(tuple) = obj;
                        }
                }
        };

struct My_dfer : public xLB_dfer {};
template<class TUP>
        struct My_dfer::xLB_tir<TUP, 1> {
                static inline void go(TUP& tuple) {
                        //std::get<0>(tuple) = 9999;
                        xLB_set_default_param(tuple, 8888);
                }
        };


xLB_xodeclare(clsA) {
        xLB_xotrait() {
                super<clsB>();
                bx<decltype(&clsA::Add), &clsA::Add, 0, xLB_dfer, xLB_idxer<1,0>>("Add");
                bx<decltype(&clsA::NoReturn), &clsA::NoReturn, 1, My_dfer, xLB_idxer<0>>("NoReturn");
                bx<decltype(&clsA::Complex), &clsA::Complex, 0, xLB_dfer, xLB_idxer<>,
                        xLB_pper,
                        xLB_rner,
                        xLB_pter,
                        My_vter, //xLB_vter, 
                        My_lver, //xLB_lver, 
                        xLB_vper
                                >("Complex");
        }
};

struct mytype {
        int a;
};

void foo(int a);
int g(int a);
int h(clsA* a);

#endif

xlbinder.cpp

#include "xlbinder.h"

//----------------------------------------------------------------------
int clsA::Add(int a, int& b) { 
        printf("clsA::Add(%d,%d):%d called\n", a,b,a+b);
        b+=123;
        return a+b; 
}
int clsA::Del(int* a) { printf("obj:Del called: %d\n", *a); return *a = 2; };
int clsA::Modify(int& a) { return a*=2; }
void clsA::NoReturn(int a) { printf("obj:NoReturn called: %d\n", a*3); }
void clsA::Complex(clsA* pa) { printf("Complex %d\n", pa->x); }

//----------------------------------------------------------------------
void foo(int a) { printf("foo called: %d\n", a); }
int g(int a) { printf("g called: %d\n", a); return a+2; }
int h(clsA* a) { printf("h called: %d\n", a->x); return 0; }


//----------------------------------------------------------------------
xLB_xodefine(clsA);
xLB_xodefine(clsB);
//----------------------------------------------------------------------------


//----------------------------------------------------------------------------
int main() {
        std::cout << "xLB test ... \n";
        auto L = luaL_newstate();
        luaL_openlibs(L);
        xLB_bindxo<clsB>(L);
        xLB_bindxo<clsA>(L);
        clsA obj;
        obj.x = 99;
        xLB_xoglobal<clsA>(L, &obj, "obj");
        luaL_dostring(L, "print('Lua{');");
        luaL_dostring(L, "print('getmetatable(obj)={');");
        luaL_dostring(L, "for k,v in pairs(getmetatable(obj)) do print('  ' .. k .. '=' .. tostring(v)); end");
        luaL_dostring(L, "print('}');");
        luaL_dostring(L, "print(obj:Complex(obj));");
        luaL_dostring(L, "print(obj:Add(99,2));");
        
        luaL_dostring(L, "x = obj:NoReturn(8); ");
        luaL_dostring(L, "print('obj:NoReturn(8)=' .. tostring(x));");
        luaL_dostring(L, "x = obj:NoReturn(); ");
        luaL_dostring(L, "print('obj:NoReturn(default_param)=' .. tostring(x));");
        
        //---
        lua_register(L, "foo", (xLB_CFunc::b<decltype(&foo), &foo, 1, My_dfer>()));
        luaL_dostring(L, "x = foo(1901);");
        luaL_dostring(L, "print('foo(1901)=' .. tostring(x));");
        luaL_dostring(L, "x = foo();");
        luaL_dostring(L, "print('foo(default_param)=' .. tostring(x));");

        lua_register(L, "g", (xLB_CFunc::b<decltype(&g), &g, 0, xLB_dfer, xLB_idxer<0>>()));
        luaL_dostring(L, "print(g(1999));");

        lua_register(L, "h", (
                        xLB_CFunc::b<decltype(&h), // type of function
                        &h, // pointer of function
                        0, // count of default parameters for function
                        xLB_dfer, // setter for default parameters for function
                        xLB_idxer<>, // index of parameters for return
                        xLB_pper, // how each value pushed into Lua
                        xLB_rner, // how the result of function pushed into Lua
                        xLB_pter, // how those parameters after function calling pushed into Lua
                        My_vter, // decide what type of value for saving the function parameters
                        My_lver, // how the value load from Lua
                        xLB_vper // make values as function's parameters
                        >()));
        luaL_dostring(L, "h(obj);");
        luaL_dostring(L, "obj:b()"); // call base class method

        lua_close(L);
        printf("}\n");
        return 0;
}

输出结果:

xLB test ...
Lua{
getmetatable(obj)={
  Add=function: 004094D0
  __index=function: 004096A0
  Complex=function: 00409214
  NoReturn=function: 00409300
}
Complex 99

clsA::Add(99,2):101 called
224     99      125
obj:NoReturn called: 24
obj:NoReturn(8)=8
obj:NoReturn called: 26664
obj:NoReturn(default_param)=8888
foo called: 1901
foo(1901)=nil
foo called: 8888
foo(default_param)=nil
g called: 1999
2001    1999
h called: 99
base class clsB::b() method called
}