文章目录
  1. 1. 简介:
  2. 2. 使用:
    1. 2.1. 绑定C++类::
    2. 2.2. 绑定c++类成员函数:
    3. 2.3. 绑定c++函数:
    4. 2.4. 绑定Lua函数:
    5. 2.5. 绑定Lua中的Table类型:
  3. 3. 注意事项:
  4. 4. 编译使用

  最初写这个东西只是想深入了解一下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
//sample.cpp
#include <stdio.h>
#include <string.h>
#include "ELuna.h"
//define a class
class CPPClass
{
public:
CPPClass(const char* name): m_name(name){
printf("%s Constructor!\n", name);
}
~CPPClass(){
printf("%s Destructor!\n", m_name);
}
//define method
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;
};
//define function
void cppPrint(char* str) {
printf("cppPrint: %s\n", str);
}
int cppSum(int a, int b){
return a + b;
}
void testCPP(lua_State* L) {
//register a class and it's constructor. indicate all constructor's param type
ELuna::registerClass<CPPClass>(L, "CPPClass", ELuna::constructor<CPPClass, const char* >);
//register a method
ELuna::registerMethod<CPPClass>(L, "cppPrint", &CPPClass::cppPrint);
ELuna::registerMethod<CPPClass>(L, "cppSum", &CPPClass::cppSum);
//register a function
ELuna::registerFunction(L, "cppPrint", &cppPrint);
ELuna::registerFunction(L, "cppSum", &cppSum);
}
void testLua(lua_State* L) {
//new a LuaFunction object to bind lua function. indicate return value type
ELuna::LuaFunction<void> luaPrint(L, "luaPrint");
ELuna::LuaFunction<int> luaSum(L, "luaSum");
//run Luafunction's () to call lua function
luaPrint("hello world");
printf("luaSum: %d\n", luaSum(1,2));
//register a lua table
ELuna::LuaTable luaTable(L, "luaTable");
//set table's key and value
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来生成,过程很简单。

  1. 安装lua。
  2. 修改premake4.lua文件中的”links”,使它指向你的lua库地址。
  3. 使用premake4生成工程文件。

如:

1
2
$ cd premake4
$ premake4 action

关于premake的使用可以查看:Premake Quick Start.

文章目录
  1. 1. 简介:
  2. 2. 使用:
    1. 2.1. 绑定C++类::
    2. 2.2. 绑定c++类成员函数:
    3. 2.3. 绑定c++函数:
    4. 2.4. 绑定Lua函数:
    5. 2.5. 绑定Lua中的Table类型:
  3. 3. 注意事项:
  4. 4. 编译使用