我感兴趣的是Luabind包装器如何能够在没有lua_State *L的情况下传递函数而不使用Lua堆栈。
Luabind如何:
我不想像Luabind那样创建另一个绑定到其他库。我只是想知道他们是怎么做到的。只是个好奇的人。
发布于 2011-05-26 16:15:49
问得好。我对luabind是如何做的有一些模糊的想法,但我不知道如何充分准确地回答问题。使用IDE和调试器,我开始剖析以下非常简单的部分:
struct C
{
int i;
int f(int x, const char* s)
};
lua_State* L = luaL_newstate();
open(L);
module(L)
[
class_<C>("C")
.def_readwrite("index", &C::i)
.def("f", &C::f)
];首先要注意的是,L经常被传递给luabind,对open的调用在Lua状态下创建了几个全局:类型为userdata的__luabind_classes和两个函数class和property。Luabind似乎没有使用全局变量-它所需要的一切都保存在lua环境中。
现在我们到了module(L)[...]。最初的代码是最好的解释,首先是module
inline module_ module(lua_State* L, char const* name = 0)
{
return module_(L, name);
}很简单,这里是module_
class LUABIND_API module_
{
public:
module_(lua_State* L_, char const* name);
void operator[](scope s);
private:
lua_State* m_state;
char const* m_name;
};因此,我们的小程序所做的是在module_类上调用运算符[],其中包含一些定义(这是scope参数),但是module_类知道在哪种Lua状态下操作。scope类也很有趣(有些部分省略了,有些部分稍微简化了一些):
struct LUABIND_API scope
{
//...
explicit scope(detail::registration* reg);
scope& operator,(scope s);
void register_(lua_State* L) const;
private:
detail::registration* m_chain;
};scope正在构建一个detail::registration节点链接列表,该列表来自于使用operator,。因此,当您执行module(L) [class_<...>..., class_<...>...]时,继承自scope的class_使用detail::registration实例初始化其基础,然后scope的逗号运算符构建所有注册的链接列表,这将传递给module_::operator[],后者调用scope::register_,后者将枚举该链并对所有detail::registration对象调用register_。lua_State总是传递给register_。
呼。现在,让我们看看当一个人做class_<C>("C").def("f", &C::f)时会发生什么。这将使用特定的名称构造class_<C>的一个实例,该名称位于class_中的detail::registration成员中。调用class_::def方法会写入reg结构和诸如此类的内容,但是在def调用链中有一条非常有趣的行
object fn = make_function(
L, f, deduce_signature(f, (Class*)0), policies);哦,deduce_signature,我真的很想看看。现在我想取消它,但是它的工作方式是这样的:通过由boost (BOOST_PP_ITERATE和其他一些实用程序)辅助的暗预处理器魔法,为1和LUABIND_MAX_ARITY之间的每个N生成以下内容:
template <class R, class T, class A1, classA2, ..., classAN>
boost::mpl::vectorN_PLUS_2<R, T, A1, A2, ..., AN> // type of return value
deduce_signature(R(T::*)(A1, A2, ..., AN))
{
return boost::mpl::vectorN_PLUS_2<R, T, A1, A2, ..., AN>()
}同样,这样的函数是为1和LUABIND_MAX_ARITY之间的所有N生成的,默认情况下这是10。有几个重载来处理const方法、虚拟包装器和空闲函数等等,这意味着大约有50个deduce_signature函数在预处理器之后和编译开始之前出现在您的源代码中。从这里开始,编译器的任务是为传递给deduce_signature的函数选择正确的def重载,这将返回正确的boost::mpl::vectorX类型。从这里make_function可以做任何事情-它有一个参数类型的编译时间列表,并且通过一些更多的模板魔术,这些是计数,转换到和从Lua值等等。
这就是我要停的地方。调查依据是Luabind 0.8.1。可以随意浏览/调试Luabind的代码以获得更多的答案--这需要一些时间,但在您习惯了这种风格之后,这并不难:)祝您好运。
TL博士:魔术..。黑魔法
发布于 2011-05-25 13:12:51
luabind为C所接受的熟悉的int luafunction(lua_State* L)原型提供了模板包装函数。本质上,lua_CFunction是为您创建的。要调用的实际C或C++函数可以存储为包装器的上值。对于C++成员函数,可以从第一个参数中提取this指针。
使用upvalue包装C函数的示例代码:
template<typename R, typename T1>
int arg1wrapper(lua_State* L)
{
typedef R (*F)(T1);
F func = (F)lua_touserdata(L, lua_upvalueindex(1));
R retValue = func(luaToC<T1>(L, 1));
push(L, retValue);
return 1;
}
// example use
template<typename R, typename T1>
void push(R (*func)(T1))
{
lua_pushlightuserdata(L, func);
lua_pushcclosure(L, &arg1wrapper<R, T1>, 1);
}( luaToC模板函数对于库打算支持的每个C和C++类型都是专门化的。类似地,push函数将被重载。)
您会注意到,上面这对函数只适用于一种特定类型的C函数,即具有非空返回值和单个参数的函数。可以通过将返回值操作分解成第三个专门用于void的模板来处理Void返回,但是要支持其他数量的参数,您需要一堆重载。luabind是这样做的:它对它所支持的每数量的参数都有一个重载,包括一个对于0的参数(最大值是他们选择的一些任意数字)。
(请注意,在C++0x中,可以使用可变模板来支持具有相同模板的任意数量的参数)
发布于 2011-05-25 00:39:04
我对luabind并不熟悉,但是“包装器”的全部思想是,它构建在一些较低级别的抽象之上,封装它。luabind几乎肯定在内部使用lua_State。
https://stackoverflow.com/questions/6114765
复制相似问题