介绍LuaPlus: 好用的Lua For C++扩展(修订)

LuaPlus是Lua的C++增强,也就是说,LuaPlus本身就是在Lua的源码上进行增强得来的。用它与C++进行合作,是比较好的一个选择。
LuaPlus目前版本为:LuaPlus for Lua 5.01 Distribution Build 1080 (February 28, 2004)。大家可以到http://luaplus.org/ 站点下载:
源码   (http://wwhiz.com/LuaPlus/LuaPlus50_Build1081.zip)
目标码 (http://wwhiz.com/LuaPlus/LuaPlus50_Build1081_Win32Binaries.zip)

介绍LuaPlus: 好用的Lua For C++扩展      沐枫网志

[由于lua内核升级到5.1,因此,luaplus也同样跟着升级。最新的luaplus可以通过svn获取,地址 svn://svn.luaplus.org/LuaPlus/work51,
同时,luaplus基于5.0的内核仍然在维护更新,也可以通过svn获取,地址 svn://svn.luaplus.org/root/LuaPlus/Dev ]

我将在下面说明,如何使用LuaPlus,以及如何更方便的让LuaPlus与C++的类合作无间。

1. 调用Lua脚本

 

    // 创建Lua解释器:
    LuaStateOwner state; 
    
    
// 执行Lua脚本:
    state->DoString("print('Hello World\\n')");
    
// 载入Lua脚本文件并执行:
    state->DoFile("C:\\test.lua");
    
// 载入编译后的Lua脚本文件并执行:
    state->DoFile("C:\\test.luac");

2. 与Lua脚本互相调用

    // 为Lua脚本设置变量
    state->GetGlobals().SetNumber("myvalue"123456);
    
// 获得Lua变量的值
    int myvalue = state->GetGlobal("myvalue").GetInteger();
    
    
// 调用Lua函数
    LuaFunction<int> luaPrint = state->GetGlobal("print");
    luaPrint(
"Hello World\n");
    
    
// 让Lua调用C语言函数
    int add(int a, int b)return a+b;}
    state
->GetGlobals().RegisterDirect("add", add);
    state
->DoString("print(add(3,4))");
    
    
// 让Lua调用C++类成员函数
    class Test{publicint add(int a, int b){return a+b;}};
    Test test;
    state
->GetGlobals().RegisterDirect("add", test, &Test::add);
    state
->DoString("print(add(3,4))");

   
3. 在Lua脚本中使用C++类
   
    这个稍微有点小麻烦。不过,我包装了一个LuaPlusHelper.h的文件,它可以很轻松的完成这个工作。它的实现也很简单,大家可以从源码上来获得如何用纯LuaPlus实现同样的功能。
    不过,这里仍然有一个限制没有解决:不能使用虚成员函数。不过考虑到我们仅是在Lua调用一下C++函数,并不是要将C++完美的导入到Lua,这个限制完全可以接受。
    另外,类成员变量不能直接在Lua中访问,可以通过类成员函数来访问(比如SetValue/GetValue之类)。 

// 下面是一个简单的C++类:    
 class Logger
 
{
 
public:
  
void LOGMEMBER(const char* message)
  
{
   printf(
"In member function: %s\n", message);
  }

 
  Logger()
  
{
   printf(
"Constructing(%p)\n"this);
   v 
= 10;
  }

  
virtual ~Logger()
  
{
   printf(
"Destructing(%p)\n"this);
  }

 
  Logger(
int n)
  
{
   printf(
" -- Constructing[%d](%p)\n", n, this);
  }

  Logger(Logger
* logger)
  
{
   printf(
" -- Constructing[%p](%p)\n", logger, this);
   logger
->LOGMEMBER(" Call From Constructor\n");
  }

  
int SetValue(int val)
  
{
   v 
= val;
  }

  
int GetValue()
  
{
   
return v;
  }

 
public:
  
int v;
 }
;

 

    // 导入到Lua脚本:
    LuaClass<Logger>(state)
       .create(
"Logger"// 定义构造函数 Logger::Logger()
       .create<int>("Logger2")  // 定义构造函数 Logger::Logger(int)
       .create<Logger*>("Logger3"// 定义构造函数 Logger::Logger(Logger*)
      
 .destroy("Free")  // 定义析构函数 Logger::~Logger()
       .destroy("__gc")  // 定义析构函数 Logger::~Logger()
       .def("lm"&Logger::LOGMEMBER)  // 定义成员函数 Logger::LOGMEMBER(const char*)
       .def("SetValue"&Logger::SetValue)
       .def(
"GetValue"&Logger::GetValue);

 

    // 在Lua中使用Logger类(1):
    state->DoString(
        
"l = Logger();"  // 调用构造函数 Logger::Logger()
        "l:lm('Hello World 1');"  // 调用成员函数 Logger::LOGMEMBER(const char*)
        "l:Free();"  // 调用析构函数 Logger::~Logger()
        );

 

    // 在Lua中使用Logger类(2):
    state->DoString(
        
"m = Logger2(10);" // 调用构造函数 Logger::Logger(int)
        "m:lm('Hello World 2');"  // 调用成员函数 Logger::LOGMEMBER(const char*)
        "n = Logger3(m);" // 调用构造函数 Logger::Logger(Logger*)
        "n:lm('Hello World 3');"  // 调用成员函数 Logger::LOGMEMBER(const char*)
        "m:SetValue(11);"
        
"print(m.GetValue());"
        
"m,n = nil, nil;" // m,n 将由Lua的垃极回收来调用析构函数
        );

4. 将一组C函数归类到Lua模块

 

    //同上面一样,我采用LuaPlusHelper.h来简化:
    LuaModule(state, "mymodule")
       .def(
"add", add)
       .def(
"add2", test, add);
 
    state
->DoString(
        
"print(mymodule.add(3,4));"
        
"print(mymodule.add2(3,4));"
        );

5. 使用Lua的Table数据类型

    // 在Lua中创建Table
    LuaObject table = state->GetGlobals().CreateTable("mytable");
    table.SetInteger(
"m"10);
    table.SetNumber(
"f"1.99);
    table.SetString(
"s""Hello World");
    table.SetWString(
"ch", L"你好");
    table.SetString(
1"What");
    
    
// 相当于Lua中的:
    
// mytable = {m=10, f=1.99, s="Hello World", ch=L"你好", "What"}

  

    // 也可以使用table作为key和value:
    state->GetGlobals().CreateTable("nexttable")
        .SetString(table, 
"Hello")
        .SetObject(
"obj", table);
    
// 相当于Lua中的:
    
// nexttable = {mytable="Hello", obj=mytable}

 

    //获得Table的内容:
    LuaObject t2 = state->GetGlobals("mytable");
    
int m = t2.GetByName("m").GetInteger();
    
    LuaObject t3 
= state->GetGlobals("nexttable");
    std::
string str = t3.GetByObject(t2).GetString();

   
6  遍历Table

 

 LuaStateOwner state;
 state.DoString( 
"MyTable = { Hi = 5, Hello = 10, Yo = 6 }" );
 
 LuaObject obj 
= state.GetGlobals()[ "MyTable" ];
 
for ( LuaTableIterator it( obj ); it; it.Next() )
 
{
     
const char* key = it.GetKey().GetString();
     
int num = it.GetValue().GetInteger();
 }


篇尾

上面我只是简单的举一些例子来说明LuaPlus以及LuaPlusHelper的使用方法,具体文档请参见LuaPlus。

需要下载LuaPlusHelper,请点这里:
http://files.cnblogs.com/ly4cn/LuaPlusHelper.rar

测试程序(VC7.1):
http://files.cnblogs.com/ly4cn/LuaPlusTest.rar

0
0
(请您对文章做出评价)
« 上一篇:如何实现不可继承的类
» 下一篇:C++指针探讨 (一)数据指针
posted @ 2005-11-27 11:04 沐枫 阅读(7672) 评论(21)  编辑 收藏 网摘 所属分类: C++

  回复  引用    
#1楼2006-03-25 14:43 | Terry8210[未注册用户]
LuaClass<Logger>(State)
.create("Logger")
.create<int>("Logger2")
.create<Logger*>("Logger3")
.destroy("Free")
.destroy("__gc")
.def("Show", &Logger::Show)
.def("SetValue", &Logger::SetValue)
.def("GetValue", &Logger::GetValue);


State->DoString(
"l = Logger();" // 调用构造函数 Logger::Logger()
"l.Show('Hello World 1');" // 调用成员函数 Logger::LOGMEMBER(const char*)
"l.Free();" // 调用析构函数 Logger::~Logger()
);
我使用了你提供的LuaPlusHelper来处理LUA使用C++中定义的类,但是为什么类的成语函数总是不能正确调用?只有构造和释构会调用?

  回复  引用  查看    
#2楼[楼主]2006-03-26 14:18 | 沐枫      
@Terry8210

从你的说明中看不出什么问题来,能不能提供以下信息:
1. 我的例子是否运行成功,并达到预期效果?

2. Logger::Show是你增加的函数吧?

3. Logger::Show是如何定义的呢?最好能把整个Logger都列出来让我看一下,我好拷到我的工程中,编译并调试。


  回复  引用    
#3楼2007-05-31 21:01 | 包子西施[未注册用户]
你好:
我下了LuaPlus50_Build1081_Win32Binaries.zip后,看里面的samples的demo后,发现里面没有添加静态链接库,那这样是怎么运行的呢?

  回复  引用  查看    
#4楼[楼主]2007-06-01 10:46 | 沐枫      
没有用静态库,那就是用动态库了。
  回复  引用    
#5楼2007-06-03 16:58 | dc[未注册用户]
首先感谢博主提供这么好的文章,我有个问题想请教,如果注册一个新的类型给lua,然后在lua中定一个变量,如何用luaplus在c中获取这个对象的指针呢?我用如下方法去试:
LuaObject numObj = state->GetGlobal("num");
Test *pNum = (Test*)numObj.GetUserData();
返回的pNum不是真正num的地址,通过调试发现返回的是指向num的指针。如果用如下方法就可以正常访问num。
LuaObject numObj = state->GetGlobal("num");
Test *pNum = (Test*)*(void**)numObj.GetUserData();
pNum->set(1000);
不知道博主怎么在c中使用lua中创建的自定义类型变量。

还有@Terry8210 兄弟调用不成功是因为使用"."来访问成员函数,如果用":"访问成员函数就不会调用不成功。我对这个问题很困惑,不知道博主怎么看。

  回复  引用    
#6楼2007-06-11 19:26 | 包子[未注册用户]
LuaStateOwner state;
state.DoString( "MyTable = { Hi = 5, Hello = 10, Yo = 6 }" );

LuaObject obj = state.GetGlobals()[ "MyTable" ];
for ( LuaTableIterator it( obj ); it; it.Next() )
{
const char* key = it.GetKey().GetString();
int num = it.GetValue().GetInteger();
}
这个迭代的性质是不是和STL类似啊,我发现输出的结果,就像std::map类似,自动对Key值排序了啊。是吗?

  回复  引用    
#7楼2007-06-11 19:41 | 包子西施[未注册用户]
下面是根据我的一个类导入类的代码
// 导入到Lua脚本:
LuaClass<CTest>(state)
.create("Create") // 定义构造函数 CTest::CTest()
.create<int, int>("Create2") // 定义构造函数 CTest::CTest(int, int)
.destroy("Free") // 定义析构函数 CTest::~CTest()
.destroy("__gc")
.def("Add", &CTest::add) // 定义成员函数 CTest::add()
.def("sub", &CTest::sub) // 定义成员函数 CTest::add()
.def("SetValueA", &CTest::SetValueA)
.def("SetValueB", &CTest::SetValueB)
.def("GetValueA", &CTest::GetValueA)
.def("GetValueB", &CTest::GetValueB)
.def("print", &CTest::Print);
但是:
state->DoString(
"m = Create2(10, 5);" // 调用构造函数 CTest::CTest(int, int)
"m:print;" // 调用成员函数 CTest::Print()
"m:Free();"
);
我利用一个非默认构造函数创建类对象的时候,却没有成功;请问是什么原因?

  回复  引用  查看    
#8楼[楼主]2007-06-12 15:38 | 沐枫      
@dc
要想得到最终的值,你必须知道变量是什么类型的。如果不知道,就只好用Isxxx函数来判断,下面这个例子是luaplus官方文档上的:

LuaObject obj = state->GetGlobal("SomeVariable");
if (obj.IsString())
{
const char* str = obj.GetString();
}

  回复  引用  查看    
#9楼[楼主]2007-06-12 15:40 | 沐枫      
@包子
跟STL不一样,跟map也不一样,至于顺序,那是lua的内部处理。
事实上,这种迭代方式,更多的出现在动态语言中。

  回复  引用  查看    
#10楼[楼主]2007-06-12 15:41 | 沐枫      
@包子西施
m:print; --少了一个括号,估计应该是:m:print();

  回复  引用    
#11楼2007-06-15 15:55 | 包子西施[未注册用户]
哈哈,是哦。掉了一个括号就连对象创建都不成功哦。谢谢你的回复:)
  回复  引用    
#12楼2007-06-17 21:19 | 查询表[未注册用户]
怎么查询表中表呢?比如我有一个
MyTable = { {5, 1, 2}, {1, 2,10} }
怎么查询里面的呢?
我尝试再建立一个Luaobject,但是不成功,一直中断
请问如何进行访问呢?

  回复  引用    
#13楼2007-06-17 21:35 | 查询表[未注册用户]
成员函数的参数是不是不能为const 或者&类型呢?
  回复  引用    
#14楼2007-06-18 09:25 | huaner[未注册用户]
如何我希望成员函数返回的是一个对象的话,就会报错。那么有没有什么办法呢?另外,如果希望程序里创建的对象在lua中也能一起交互的用,有没有什么办法呢?
  回复  引用  查看    
#15楼[楼主]2007-06-26 21:27 | 沐枫      
@查询表
表中表与表没有区别。
至于参数是不是不能为const或引用,我也不清楚,你可以参考lua和luaplus的文档,自已研究一下。
@huaner
你要想在lua使用C++的对象,就必须将此对象导入lua。

  回复  引用    
#16楼2007-07-11 13:25 | Tristan[未注册用户]
您好,我试过你的老版本的LuaPlusHelper
其中ConstructorHelper是这样写的
LuaObject obj(state);
obj.AssignUserData(state,pObj);
obj.SetMetaTable(state->GetGlobal(metaname.c_str()));
obj.Push();
而新版本是这样的
LuaObject obj = state->BoxPointer(pObj);
obj.SetMetaTable(state->GetGlobal(metaname.c_str()));
obj.Push();
我试了一下,老版本的确有问题。在调用某个对象的成员函数时,
this指针没有正确地传给lua. 但我不明白老版本为什么会错,新版本
为什么又是正确的? 能麻烦您解释一下吗?

  回复  引用  查看    
#17楼[楼主]2007-07-19 19:30 | 沐枫      
@Tristan
那是因为AssignUserData与BoxPointer这两个函数的功能不一样啊。

  回复  引用    
#18楼2007-08-13 18:35 | 阿福[未注册用户]
谢谢楼主提供这么好的东东!
  回复  引用    
#19楼2007-09-15 11:57 | 路过[未注册用户]
沐枫你好,请问第二人生现在还存在吗?我进入官网,发现最迟的更新在2003年……- -!
  回复  引用  查看    
#20楼[楼主]2007-09-15 16:34 | 沐枫      
第二人生QQ群:212733
  回复  引用    
#21楼2008-12-19 17:00 | hunterxxxx[未注册用户]
这些都是简单数据类型的操作,能不能提供一个操作struct(里面还有struct)的例子啊。