最初写这个东西只是想深入了解一下C++和Lua的交互细节,顺便当做自己业余练练手,所以也不想把它做得很庞大,只是实现了一些我在项目中常会用到的功能。算然不想做的太庞大,但却想试着让这个库更简单,更高效些。在此目的下我花了很长的时间才写完这个简单的库,一直以自己很忙为借口不去写,非常惭愧啊。。。不过还好,最终还是完成了,没有半途而废。
目前这个工程已经从google code迁到了github中,主页为:https://github.com/radiotail/eluna
下面就简单介绍一下这个库。
简介:
ELuna是一个简单的,轻量级的用来完成C++和Lua之间相互绑定的工程。它不依赖于任何第三方库,主要思想来源于一个简单的工程Luna,其最开始的目的也是在Luna做一些扩展,让使用者在编写代码时不必再关注C++和Lua之间交互时的细节,只需调用Eluna提供的简单的API便能轻松的完成绑定c++类,成员函数,函数或绑定Lua函数,Table的任务。
1.1版本之前,ELuna采用了和Luna完全相同的绑定C++成员函数的方法,既在创建对象时返回一个对象的Table,并将对象所有的成员函数复制到对象Table中的方法。这种方法有一个优势是在调用成员方法时不必查找元表,来找到这个成员方法,所以调用速度很快。但却也有一个明显的缺点就是在创建对象时需要创建一个table,并复制所有的函数到table中,因此对象的创建速度会很慢。当然,如果你的对象很少被创建,大量时间都是在调用它的成员函数的话,用这种方法可能是比较不错的选择。因为这个方法在创建对象时效率不高,所以在之后的版本我做了一些调整,不再在创建对象的时候创建一个table,并复制所有的成员函数到table中了,而是在注册成员函数时,将成员函数放到对象的元表中,通过元表的__index元方法来找到这个函数并调用。这样就大大提高了创建对象的速度,不过成员方法的调用速度会略有些慢,到底怎样取舍就看自己的需求了。
目前ELuna经过多个版本的修改和改进之后已经稳定下来了,但也许还存在着一些我没有发现的bug,如果大家发现什么问题请与我联系。当然现在的Eluna还不能支持所有的功能,比如说绑定成员数据的功能。没有加入这些功能的原因主要是因为在决定都开发哪些功能时,我参考了以往自己在项目开发中的经验,那些没有开发的功能基本很少会被用到,或是可以简单地通过其他途径来实现,如绑定成员数据这种功能,我们完全可以通过注册一个get和set函数来解决这个问题,当然如果您真的需要这个功能,自己添加也比较简单。
使用:
下面主要展示几种简单的功能:
绑定C++类::
使用ELuna::registerClass( lua_State, className, constructor) 函数绑定一个C++类。第一个参数是lua_state, 第二个参数是类在Lua中的名字。第三个参数constructor是这个类要用到的构造函数,通过传递具体的ELuna::constructor 函数来为这个类设置好一个构造函数,这个函数模板参数中的第一个参数是构造函数所属的类,后面的参数为构造函数的参数类型列表。如你需要为类Test设置一个参数为int的构造函数,则须传递ELuna::constructor 到registerClass函数中,模板参数中的int就是这个类的构造函数所需的参数类型。在Lua中创建对象时需这样写: test = Test(1)。
绑定c++类成员函数:
使用ELuna::registerMethod(lua_State, funcName, func) 函数绑定一个c++类成员函数。第一个参数同上面的函数。第二个参数是函数在Lua中的名字。第三个参数是这个函数的地址。如你要注册Test类的foo函数到Lua中,应利用ELuna::registerMethod(L,”foo”, &Test::foo)来注册函数。在Lua中的调用为test:foo()。
绑定c++函数:
使用ELuna::registerFunction(lua_State, funcName, func)函数c++函数。这个函数中参数的意义同registerMethod函数。例如C++中有一函数foo,利ELuna::registerFunction(L,“foo”, &foo)注册这个函数,在Lua调用为foo()。
绑定Lua函数:
使用ELuna::LuaFunction类为Lua函数生成一个相应的C++中的对象,调用函数通过使用类的”()”方法来实现。比如在Lua中有一个函数foo(a),其中参数a是number型,且无返回值,则在C++中的绑定方法为先利用 ELuna::LuaFunction luaFoo(L, “foo”)创建一个C++对象,其中模板参数是这个Lua函数的返回值类型,因为foo无返回值,所以为void,LuaFunction的第一个参数是lua_state,第二个参数是这个Lua函数的名字。在C++中的调用方式为luaFoo(1)。
绑定Lua中的Table类型:
使用ELuna::LuaTable类生成一个Lua Table在c++中的对象,读取插入元素通过”get”, “set”方法。比如Lua中有一个Table变量luaTable = {“hello”},在C++中使用ELuna::LuaTable luaTable(L, “luaTable”)来为luaTable在C++中生成一个对象,调用luaTable.get(1)来取得luaTable中key为1的字符串”hello”,其中模板参数char*为get返回的value类型。调用luaTable.set(2, “world”)来为luaTable插入一个key=2,value=”world”的键值对。
java面是一个实际的例子:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81
| #include <stdio.h> #include <string.h> #include "ELuna.h" class CPPClass { public: CPPClass(const char* name): m_name(name){ printf("%s Constructor!\n", name); } ~CPPClass(){ printf("%s Destructor!\n", m_name); } void cppPrint(const char* word) { printf("%s: %s\n", m_name, word); } int cppSum(int a, int b){ return a + b; } private: const char* m_name; }; void cppPrint(char* str) { printf("cppPrint: %s\n", str); } int cppSum(int a, int b){ return a + b; } void testCPP(lua_State* L) { ELuna::registerClass<CPPClass>(L, "CPPClass", ELuna::constructor<CPPClass, const char* >); ELuna::registerMethod<CPPClass>(L, "cppPrint", &CPPClass::cppPrint); ELuna::registerMethod<CPPClass>(L, "cppSum", &CPPClass::cppSum); ELuna::registerFunction(L, "cppPrint", &cppPrint); ELuna::registerFunction(L, "cppSum", &cppSum); } void testLua(lua_State* L) { ELuna::LuaFunction<void> luaPrint(L, "luaPrint"); ELuna::LuaFunction<int> luaSum(L, "luaSum"); luaPrint("hello world"); printf("luaSum: %d\n", luaSum(1,2)); ELuna::LuaTable luaTable(L, "luaTable"); luaTable.set(2, "world"); printf("LuaTable: %s %s\n", luaTable.get<char*>(1), luaTable.get<char*>(2)); } const char *fileName = "sample.lua"; int main() { lua_State *L = ELuna::openLua(); testCPP(L); ELuna::doFile(L, fileName); testLua(L); ELuna::closeLua(L); system("pause"); return 0; }
|
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23
| ///////////////////////////////////////////////////////////////////////// //sample.lua --new a class local cppClass = CPPClass("cppClass") --call cpp method cppClass:cppPrint("Hello world!") print("CPPClass's cppSum:", cppClass:cppSum(1,2)) --call cpp function cppPrint("Hello world!") print("cppSum:", cppSum(1,2)) --define lua function function luaPrint(str) print("luaPrint:", str) end function luaSum(a, b) return a + b end --define table luaTable = {"hello"}
|
注意事项:
传递对象或对象引用给LuaFunction的效果一样,都会创建临时对象。如果你不希望传递参数给LuaFunction时创建临时对象,可以传递指针。
编译使用
Eluna支持Windows, Unix, OS X。测试工程文件使用premake4来生成,过程很简单。
- 安装lua。
- 修改premake4.lua文件中的”links”,使它指向你的lua库地址。
- 使用premake4生成工程文件。
如:
关于premake的使用可以查看:Premake Quick Start.