在lua中使用c函数 需要预先进行注册 ,lua只接受 lua_CFuntion 类型 (lua.h中的定义typedef int (*lua_CFunction) (lua_State *L) ).返回值为函数实际返回的返回值个数,lua与c的交互式通过lua stack进行,通过lua CAPI 在lua和c中进行参数传递.
//ex1 注册c函数到lua中
int fun(lua_State* l)
{
//do some thind
return 0; //返回0个值
}
//将fun注册到lua中去
lua_register(l,"fun",&fun);
//lua中的调用
fun()
lua_register 做两步工作 lua_pushcfunction 和lua_setglobal
lua_pushcfuntion 即 lua_pushcclousre 并且 upvalue 为0
同时使用lua_setglobal 将 lua中 的全局变量 fun指向刚才压入的c闭包
问题: 每一个与lua交互的c函数 必须改为lua_CFuntion 类型
解决方案:
1.使用一个代理c函数来作为与lua的交互 真正的c函数指针 以lightuserdata的形式压入堆栈 并作为代理函数的一个upvalue存在
//用代码说明
//将全局函数注入lua
template<typename Func> //此处的Func为c函数的型别
void def(lua_State* l,char* name,Func func)
{
//压入函数名字 作为global table的key
lua_pushstring(l,name);
lua_pushlightuserdata(l,func);
push_functor(l,func); //此处让编译器进行template 匹配
lua_settable(l,LUA_GLOBALSINDEX);
}
//负责压c函数 无参版本
template <typename RVal>
void push_functor(lua_State* l,RVal (*)())
{
//代理函数为functor::invoke 一个static函数
lua_pushcclosure(l,functor<void>::invoke<RVal>,1);//此处的1为一个upvalue 指先前压入的函数指针
}
//仿造可写出一参版本
template <typename RVal,typename Arg1>
void push_functor(lua_State* l,RVal (*)(Arg1))
//无参调用的c函数
template<>
struct functor<void>
{
//functor中的invoek函数 为宿主程序在lua中的代理
//lua只能注册 luaCFunction类型 int (*func)(lua_State* l)
template<typename RVal>
static int invoke(lua_State* l)
{
//将以upvalue保存的函数指针拿出来执行
//push c函数的返回值到lua stack
push(l,upvalue<RVal (*)()>(l) ());
return 1;
}
//特化版本 函数无返回
template<>
static int invoke<void>(lua_State* l)
{
//无返回的
upvalue<void (*)()>(l)();
return 0;
}
};
//以制定类型读取upvalue值
template <typename T>
T upvalue(lua_State* l)
{
return read<T>(l,lua_upvalueindex(1));//c函数指针在upvalueindex 1处
}
//泛化版本的read函数
//在指定索引处读取lua stack中的函数
template <typename T>
T read(lua_State* l,int index)
{
return lua2type<T>(l,index);
}
//一些简单的特化版本
template <>
int read<int>(lua_State* l,int index)
{
return lua_tonumber(l,index);
}
template<>
bool read<bool>(lua_State* l,int index)
{
if(lua_isboolean(l,index))
{
return lua_toboolean(l,index)!=0;
}
else
{
return lua_tonumber(l,index);
}
}
template<>
char* read<char*>(lua_State* l,int index)
{
return (char*)lua_tostring(l,index);
}
//将保存在lua中的值 以宿主程序的型别返回回来
template<typename T>
T lua2type(lua_State* l,int index)
{
return lua2object<T>(l,index);
}
//将lua中的userdata 返回为class object
template<typename T>
T lua2object(lua_State* l,int index)
{
//先检测是否userdata
if(!lua_isuserdata(l,index))
{
//非userdata
lua_pushstring(l,"no class at index argument");
lua_error(l);
}
//yes userdata为void* 是一块raw内存
//从这里开始使用invoke函数 将void* 类型转换为T
return void2type<T>::invoke(lua_touserdata(l,index));
}
//将void*转换为T的型别
template <typename T>
struct void2type
{
static T invoke(void* input)
{
//将void*转换为T 依据T的型别做不同转换
//分为三部分
//1 T为class* 转换为 return (class*)(input)
//2 T为class 转换为 return *((class*)(input))
//3 T为class& 转换为 return *((class*)(input))
//使用type traits技法
return if_<isptr<T>::value,
void2ptr<base_type<T>::type>,
if_<isref<T>::value,
void2ref<base_type<T>::type>,
void2val<base_type<T>::type>
>::type
>::type::invoke(input);
}
};
//type traits 的简单使用
//型别T是否ptr
template <typename T>
struct isptr
{
static bool value=false;
};
template <typename T>
struct isptr<T*> //偏特化版本 型别为T* 则value为true
{
static bool value=true;
};
template <typename T>
struct isref
{
static const bool value=false;
};
template <typename T>
struct isref<T&>
{
static const bool value=true;
};
//一种型别选择器
template <typename A,typename B,bool>
struct if_{};
template <typename A,typename B,true>
struct if_
{
typedef A type;
};
template <typename A,typename B,false>
struct if_
{
typedef B type;
};
if_<isptr<T>::value,
void2ptr<base_type<T>::type>,
if_<isref<T>::value,
void2ref<base_type<T>::type>,
void2val<base_type<T>::type>
>::type
>::type::invoke(input);
首先检查型别T是否 ptr 如果是void* 转为 T* ,其次检查型别T是否ref 如果是void* 转为T& ,最后void* 转为 T
//base_type 萃取T的型别
template <typename T>
struct base_type
{
typedef T type;
};
template <typename T>
struct base_type<T*>
{
typedef T type;
};
template <typename T>
struct base_type<T&>
{
typedef T type;
};
//void* to class*
template <typename T>
struct void2ptr
{
static T* invoke(void* input)
{
return (T*)(input);
}
};
//void* to val
template <typename T>
struct void2val
{
static T invoke(void* input)
{
return *((T*)(input));
}
};
//void* to ref
template <typename T>
struct void2ref
{
static T& invoke(void* input)
{
return *((T*)(input));
}
};
当初的pushlightuserdata压入函数指针时 c函数指针转为了void* 如今通过正确的型别转换 c函数指针又再次转型为了正确的函数类型
自push_functor时 c函数的转型类型就已经被编译器确定了 这全部仰赖与template
template<typename Func> //此处的Func为c函数的型别
void def(lua_State* l,char* name,Func func)
不需要写一个wrap function ,无论什么样的c函数 向lua中压入c函数变为了轻松的一句
def(l,"cAdd",&cAdd)
template 的模版推倒会推出函数指针的型别
lua中的调用也是
i = cAdd(1,2)
这其中仍然包括的两个问题是:1. lua传向c 的参数 尚未读出 2.c函数 返回值压入lua stack
这里的c函数是正儿八经的 c 函数 只会返回一个值 所以代理函数 functor::invoke中只有一条压栈指令
并不是为了lua的调用而写的wrap函数 会向 lua stack 压入几个返回值
//lua中c 代理函数 无参版本
template<>
struct functor<void>
{
template<typename RVal>
static int invoke(lua_State* l)
{
push(l,upvalue<RVal (*)()>(l) ( ));
return 1;
}
template<>
static int invoke<void>(lua_State* l)
{
//无返回的
upvalue<void (*)()>(l)();
return 0;
}
};
//lua中c 代理函数 一参版本
template<typename Arg1>
struct functor<Arg1>
{
template<typename RVal>
static int invoke(lua_State* l)
{
push(l,upvalue<RVal (*)(Arg1)>(l)(read<Arg1>(l,1)));//此处用read<T> 读出index 1 处的唯一一个参数
return 1;
}
template<>
static int invoke<void>(lua_State* l)
{
upvalue<RVal (*)(Arg1)>(l)(read<Arg1>(l,1));
return 0;
}
};
如此二参 ,三参 和多参 也可以写了
此处的push为将c 中的值压入lua 中的堆栈 是read的逆过程
//将c的值传入lua中 泛化版本
template <typename T>
void push(lua_State* l,T t)
{
type2lua<T>::invoke(l,t);
}
//几个简单的特化版本
template <>
void push<int>(lua_State* l,int t)
{
lua_pushnumber(l,t);
}
template<>
void push<bool>(lua_State* l,bool b)
{
lua_pushboolean(l,b);
}
template<>
void push<const char*>(lua_State* l,const char* s)
{
lua_pushstring(l,s);
}
//将class类型的值传入lua中
template<typename T>
struct type2lua
{
static void invoke(lua_State* l,T val)
{
object2lua<T>::invoke(l,val);
}
};
//class object 传入lua
template <typename T>
struct object2lua
{
static void invoke(lua_State* l,T val)
{
if_<isptr<T>::value,
ptr2lua<base_type<T>::type>, //指针转lua
if_<isref<T>::value,
ref2lua<base_type<T>::type>, //ref转lua
val2lua<base_type<T>::type> //val转lua
>::type
>::type::invoke(l,val);
//查找class object 存根在lua中的class metatable
meta_push<base_type<T>::type>(l);
lua_setmetatable(l,-2);
}
};
//将ptr ref val三种不同类型值压入lua中不同做法
template <typename T>
struct ptr2lua
{
static void invoke(lua_State* l,T* val)
{
if(val) //不为NULL指针
{
// new(lua_newuserdata(l,sizeof(T)) T(val);这是错误做法吗
//将ptruser作为lua中的class object 在宿主中存根
new(lua_newuserdata(l,sizeof(ptr2user<T>))) ptr2user<T>(val);
}
else
{
lua_pushnil(l);
}
}
};
//压入lua的是一个指针 以userdata方式压入
//宿主程序向lua返回三种class 类型
//这三个user类型的设计 主要还是为了从c中返回class val类型到lua中
//在lua中并不存储整个class obj 这是一个原则 lua只以userdata存储object ptr
//如果返回ref 也照存ptr
//如果返回val 就在Rua中 照原样给他存一份 给lua一个指向的ptr 以val2user形式存贮
struct user
{
void* m_p;
user(void* p):m_p(p){}
virtual ~user(){}
};
//如果向lua中返回值 则重new一个object 出来 将ptr交给lua
template <typename T>
struct val2user : public user
{
val2user() : user(new T){}
template<typename Arg1>
val2user(Arg1 arg1) : user(new T(arg1)){}
template<typename Arg1, typename Arg2>
val2user(Arg1,Arg2) : user(new T(arg1,arg2)){}
template<typename Arg1,typename Arg2,typename Arg3>
val2user(Arg1,Arg2,Arg3) : user(new T(arg1,arg2,arg3)){}
//这个object是自向lua中返回时声称的 必须自己销毁
~val2user(){delete (T*)(m_p);}
};
template<typename T>
struct ptr2user : public user
{
ptr2user(T* t):user((void*)t){}
};
template<typename T>
struct ref2user : public user
{
ref2user(T& t):user((void*)&t){}
};
template<typename T>
struct ref2lua
{
static void invoke(lua_State* l,T& val)
{
new(lua_newuserdata(l,sizeof(ref2user))) ref2user<T>(val);
}
};
template<typename T>
struct val2lua
{
static void invoke(lua_State* l,T& val)
{
new(lua_newuserdata(l,sizeof(val2user))) val2user<T>(val);
}
}