社区
工具平台和程序库
帖子详情
散分推荐:通过 LuaBind 让 C++函数返回Lua表
jadedrip
2010-06-12 10:28:45
http://blog.csdn.net/jadedrip/archive/2010/06/12/5665828.aspx
评论有分(记得在本贴留名)
呵呵,试试这样能不能提高访问量。
...全文
176
11
打赏
收藏
散分推荐:通过 LuaBind 让 C++函数返回Lua表
http://blog.csdn.net/jadedrip/archive/2010/06/12/5665828.aspx 评论有分(记得在本贴留名) 呵呵,试试这样能不能提高访问量。
复制链接
扫一扫
分享
转发到动态
举报
AI
作业
写回复
配置赞助广告
用AI写文章
11 条
回复
切换为时间正序
请发表友善的回复…
发表回复
打赏红包
barech
2010-06-28
打赏
举报
回复
必须得接。
现在都是跨平台了
jokey514
2010-06-23
打赏
举报
回复
学习接分
budweiser
2010-06-22
打赏
举报
回复
jf的
摩尔信使MThings
2010-06-22
打赏
举报
回复
那就特来留名的。。
cattycat
2010-06-22
打赏
举报
回复
标题吓到人了,听过Lua,跟Ruby,python之类的语言一样。
liutengfeigo
2010-06-22
打赏
举报
回复
看不懂,但是也去要顶
「已注销」
2010-06-22
打赏
举报
回复
Lua不错!python也更强!!
solarmm
2010-06-22
打赏
举报
回复
前来学习 并接分
jadedrip
2010-06-17
打赏
举报
回复
散分都没人接啊……
wade_2003
2010-06-12
打赏
举报
回复
留名,看到LS太强悍了,仰望一会儿!!!!!!!!!
大熊猫侯佩
2010-06-12
打赏
举报
回复
各种动态语言都想和C/C++有一腿,lua,ruby也不例外
Lua
Bind
帮助你绑定
C++
和
Lua
的库
Lua
Bind
是一个帮助你绑定
C++
和
Lua
的库.她有能力暴露
C++
函数
和类到
Lua
. 她也有 能力支持
函数
式的定义一个
Lua
类,而且使之继承自
C++
或者
Lua
.
Lua
类可以覆写从
C++
基类 继承来的虚
函数
. 她的目标平台是
Lua
5.0 ,不能支持
Lua
4.0 .
Lua
Bind
源码 (
Lua
增强库)
1 介绍
Lua
Bind
是一个帮助你绑定
C++
和
Lua
的库.她有能力暴露
C++
函数
和类到
Lua
. 她也有 能力支持
函数
式的定义一个
Lua
类,而且使之继承自
C++
或者
Lua
.
Lua
类可以覆写从
C++
基类 继承来的虚
函数
. 她的目标平台是
Lua
5.0 ,不能支持
Lua
4.0 . 她利用模板原编程技术实现.这意味着,你不需要额外的预处理过程去编译你的工程(编译器 会替你完成全部的工作).这还意味着,你也不需要(通常)知道你注册的每一个
函数
的精确的签名. 因为,
Lua
Bind
库会在编译时生成所需的代码.这样做的不利点是,编译时间会随着需要注册的 文件的数目增加而增加.因此建议你把所有的需要注册的东西放到一个cpp文件里面.
Lua
Bind
遵循 MIT 协议 发布. 我们非常希望听说有工程使用了
Lua
Bind
, 请告诉我们,如果你的工程使用了
Lua
Bind
. 主要的反馈渠道是
Lua
Bind
邮件列
表
.在 irc.freenode.net还可以找到一个IRC频道 #
lua
bind
. 2 功能
Lua
Bind
支持: * 重载自由
函数
*
C++
类导入
Lua
* 重载成员
函数
* 操作符 * 属性 * 枚举 *
Lua
函数
导入
C++
*
Lua
类导入
C++
*
Lua
类(单继承) * 从
Lua
或
C++
类继承 * 覆写
C++
类的虚
函数
* 注册类型间隐式的类型转换 * 最好匹配式签名匹配 *
返回
值策略和参数策略 3 可移植性
Lua
Bind
已经通过下面的编译器环境的测试: Visual Studio 7.1 Visual Studio 7.0 Visual Studio 6.0 (sp 5) Intel
C++
6.0 (Windows) GCC 2.95.3 (cygwin) GCC 3.0.4 (Debian/Linux) GCC 3.1 (SunOS 5.8) GCC 3.2 (cygwin) GCC 3.3.1 (cygwin) GCC 3.3 (Apple, MacOS X) GCC 4.0 (Apple, MacOS X)
Lua
Bind
被确认不能在 GCC 2.95.2 (SunOS 5.8) 下工作. Metrowerks 8.3 (Windows) 可以编译
Lua
Bind
,但是通不过常量测试.这就意味着常量 成员
函数
被视同非常量成员
函数
. 如果你测试了
Lua
Bind
和其他未列出的编译器的兼容性,请告诉我们你的结果. 4 构建
Lua
Bind
为了抑制
Lua
Bind
的编译时间最好是将其编译为一个库. 这意味着你要不编译并连接
Lua
Bind
库要不就添加其所有源码到你的工程里面.你必须确保
Lua
Bind
目录在你的编译器包含目录中.
Lua
Bind
需要Boost 1.32.0 或者 1.33.0 (只需要头文件即可).
Lua
Bind
还需要
Lua
. 官方的构建
Lua
Bind
的方式是通过 Boost.Build V2 . 为此,你需要设置两个环境变量: BOOST_ROOT 指向你的Boost安装目录
LUA
_PATH 指向你的
Lua
目录.编译系统将假定包含文件和库文件分别放在 $(
LUA
_PATH)/include/ 和 $(
LUA
_PATH)/lib/. 为了向后兼容性,
Lua
Bind
在根目录下还保留了一个makefile.这可以构建库和测试程序.如果 你正在使用一个UNIX系统(或者 cygwin),他们将使得构建
Lua
Bind
静态库变得很简单.如果 你正在使用 Visual Studio ,很简单的包含 src 目录下的文件到你的工程即可. 构建
Lua
Bind
的时候,你可以设定一些选项来使得库更加符合你的需求.特别重要的是,你的应用 程序也必须使用和库一样的设定.可用的选项的介绍参见 Build options 章节. 如果你希望改变缺省的设置,
推荐
你通过修改命令行参数的方式来实现.(在Visual Studio 的工程设置项里面). 5 基本使用 为了使用
Lua
Bind
, 你必须包含
lua
.h 和
Lua
Bind
的主要头文件: extern "C" { #include "
lua
.h" } #include <
lua
bind
/
lua
bind
.hpp> 这些头文件提供了注册
函数
和类的功能. 如果你只是想获得
函数
或者类的支持,你可以分开 包含
lua
bind
/function.hpp 和
lua
bind
/class.hpp: #include <
lua
bind
/function.hpp> #include <
lua
bind
/class.hpp> 你需要去做的第一件事是 调用
lua
bind
::open(
lua
_State*), 由此注册可以在
Lua
创建类 的
函数
并初始化
Lua
Bind
需要使用的 状态机全局结构. 如果你不调用这个
函数
, 你会在后面 触发一个 断言 . 不没有一个对应的关闭
函数
.因为,一旦一个类被注册到
Lua
,真没有什么好 的方法去移除它.部分原因是任何剩余的类实例都将依赖其类. 当状态机被关闭的时候,所有 的一切都将被清理干净.
Lua
Bind
的头文件不会直接包含
Lua
.h , 而是透过 <
lua
bind
/
lua
_include.hpp> . 如果你 出于某种原因需要包含其他的
Lua
头文件,你可以修改此文件. 5.1 Hello World 新建一个控制台DLL工程, 名字是
lua
bind
_test. #include
#include <
lua
bind
/
lua
bind
.hpp> #include <
lua
bind
/
lua
_include.hpp> extern "C" { #include "
lua
.h" #include "lauxlib.h" } void greet() { std::cout << "hello world!\n"; } extern "C" int
lua
open_
lua
bind
_test(
lua
_State* L) { using namespace
lua
bind
; open(L); module(L) [ def("greet", &greet) ]; return 0; } 把生成的DLL和
lua
.exe/
lua
51.dll放在同一个目录下.
Lua
5.1.2 Copyright (C) 1994-2007
Lua
.org, PUC-Rio > require "
lua
bind
_test" > greet() Hello world! > 6 作用域 注册到
Lua
里面的所有东西要不注册于一个名空间下(
Lua
table)要不注册于全局作用域(
lua
module). 所有注册的东西必须放在一个作用域里面.为了定义一个模块,
lua
bind
::module 类必须被使用. 使用方式如下: module(L) [ // declarations ]; 这将会注册所有的
函数
或者类到
Lua
全局作用域. 如果你想要为你的模块设定一个名空间(类似标准模块), 你可以给构造
函数
设定一个名字,例如: module(L, "my_library") [ // declarations ]; 这里所有的申明都将被放置在 my_libary
表
. 如果你想要嵌套名空间,你可以用
lua
bind
::namespace_ 类. 它和
lua
bind
::module 类似,除了构造器 没有
lua
_State* 输入参数.用例如下: module(L, "my_library") [ // declarations namespace_("detail") [ // library-private declarations ] ]; 你可能会想到,下面两个声明是等价的: module(L) [ namespace_("my_library") [ // declarations ] ]; module(L, "my_library") [ // declarations ]; 每一个声明必须用逗号分隔,例如: module(L) [ def("f", &f), def("g", &g), class_
("A") .def(constructor
), def("h", &h) ]; 更多实际的例子请参阅 绑定
函数
到
Lua
和 绑定类到
Lua
章节. 请注意, (如果你对性能有很高的需求)把你的
函数
放到
表
里面将增加查找
函数
的时间. 7 绑定
函数
到
Lua
为了绑定
函数
到
Lua
,你可以使用
函数
lua
bind
::def(). 它的声明如下: template
void def(const char* name, F f, const Policies&); * name 是该
函数
在
Lua
里面的名字 * F 是该
函数
的指针 * 策略参数是用来描述怎样处理该
函数
参数和
返回
值的.这是一个可选参数,参见 策略 章节. 下面的例子演示注册
函数
float std::sin(float): module(L) [ def("sin", &std::sin) ]; 7.1 重载
函数
如果你有同名
函数
需要注册到
Lua
, 你必须显示的给定
函数
的签名. 这可以让
C++
知道你指定的是哪一个
函数
. 例如, 如果你有两个
函数
, int f(const char*) 和 void f(int). module(L) [ def("f", (int(*)(const char*)) &f), def("f", (void(*)(int)) &f) ]; 7.2 签名匹配
Lua
Bind
将会生成代码来检查
Lua
栈的内容是否匹配你的
函数
的签名. 它会隐式的在 派生类之间进行类型转换,并且它会按照尽量少进行隐式类型转换的原则经行匹配.在 一个
函数
调用中,如果
函数
是重载过的,并且重载
函数
的参数匹配分不出好坏的话 (都经行同样次数的隐式类型转换),那么将产生一个二义性错误.这将生成一个运行时 错误,程序挂起在产生二义性调用的地方.一个简单的例子是,注册两个
函数
,一个
函数
接受一个int参数,另外一个
函数
接受一个float参数. 因为
Lua
将不区别浮点数和整形数, 所以他们都是匹配的. 因为所有的重载是被测试过的,这将总是找到最好的匹配(不是第一个匹配).这样意味着,
Lua
Bind
可以处理签名的区别只是const和非const的重载
函数
. 例如,如果如下的
函数
和类被注册: struct A { void f(); void f() const; }; const A* create_a();所有权转移 为了正确处理所有权转移问题,create_a()将用来适配
返回
值策略. 参见 策略 章节. -Linker Lin 4/5/08 6:32 PM struct B: A {}; struct C: B {}; void g(A*); void g(B*); 执行以下
Lua
代码即结果: a1 = create_a() a1:f() -- 常量版本被调用 a2 = A() a2:f() -- 非常量版本被调用 a = A() b = B() c = C() g(a) -- calls g(A*) g(b) -- calls g(B*) g(c) -- calls g(B*) 7.3 调用
Lua
函数
为了调用一个
Lua
函数
, 你可以或者用 call_function() 或者用 一个对象(object). template
Ret call_function(
lua
_State* L, const char* name, ...) template
Ret call_function(object const& obj, ...) call_function()
函数
有两个重载版本.一个是根据
函数
的名字来调用
函数
, 另一个是调用一个可以作为
函数
调用的
Lua
值. 使用
函数
名来调用的版本只能调用
Lua
全局
函数
. "..."代
表
传递给
Lua
函数
的 可变个数的参数. 这使得你可以指定调用的策略.你可以通过 operator[] 来实现 这个功鞥.你可以同过方括号来指定策略,例如: int ret = call_function
( L , "a_
lua
_function" , new complex_class() )[ adopt(_1) ]; 如果你想通过引用方式传递参数,你必须用Boost.Ref来包装一下. 例如: int ret = call_function(L, "fun", boost::ref(val)); 如果你想给一个
函数
调用指定自己的错误捕获处理
函数
(error handler),可以参阅 pcall errorfunc 章节的 set_pcall_callback . 7.4 使用
Lua
协程 为了使用
Lua
协程,你必须调用
lua
_resume(),这就意味着你不能用先前介绍的
函数
call_function()来开始一个协程.你必须用这个: template
Ret resume_function(
lua
_State* L, const char* name, ...) template
Ret resume_function(object const& obj, ...) 和: template
Ret resume(
lua
_State* L, ...) 第一次开始一个协程的时候,你必须给它一个入口
函数
. 当一个协程
返回
(yield)的时候, resume_fucntion()调用的
返回
值是
lua
_yield()的第一个传入参数.当你想要继续一个 协程的时候,你只需要调用 resume() 在你的
lua
_State() 上,因为它已经在执行一个
函数
(即先前出入的入口
函数
),所以你不需要再次传入
函数
.resume()的传入参数将作为
Lua
侧的 yield()调用的
返回
值. 为了暂停(yielding)
C++
函数
,(不支持在
C++
侧和
Lua
侧传送数据块),你可以使用 yield 策略. 接受 object 参数的resume_function()的重载版本要求对象必须是一个协程对象.(thread)
lua
_State* thread =
lua
_newthread(L); object fun = get_global(thread)["my_thread_fun"]; resume_function(fun); 8 绑定类到
Lua
为了注册一个类,你可以用 class_ 类. 它的名字和
C++
关键字类似是为了比较直观.它有一个重载 过的成员
函数
def() .这个
函数
被用来注册类的成员
函数
,操作符,构造器,枚举和属性.它将
返回
this 指针,从而方便你直接注册更多的成员. 让我们开始一个简单的例子.考虑下面的
C++
类: class testclass { public: testclass(const std::string& s): m_string(s) {} void print_string() { std::cout << m_string << "\n"; } private: std::string m_string; }; 为了注册这个类到
Lua
环境,可以像下面这样写(假设你使用了名空间): module(L) [ class_
("testclass") .def(constructor
()) .def("print_string", &testclass::print_string) ]; 这将注册 testclass 类以及接受一个string参数的构造器以及一个成员叫print_string()的
函数
.
Lua
5.0 Copyright (C) 1994-2003 Tecgraf, PUC-Rio > a = testclass('a string') > a:print_string() a string 还可以注册自由
函数
作为成员
函数
.对这个自由
函数
的要求是,它必须接受该类的一个指针或常量指针或 引用或常量引用作为
函数
的第一个参数.该
函数
的剩下的参数将在
Lua
侧可见,而对象指针将被赋值给第一个 参数.如果我们有如下的
C++
代码: struct A { int a; }; int plus(A* o, int v) { return o->a + v; } 你可以注册 plus() 作为A的一个成员
函数
,如下: class_
("A") .def("plus", &plus) plus() 现在能够被作为A的一个接受一个int参数的成员
函数
来调用.如果对象指针(this指针)是const, 这个
函数
也将
表
现的像一个常量成员
函数
那样(它可以通过常量对象来调用). 8.1 重载成员
函数
当绑定超过一个以上的重载过的成员
函数
的时候,或只是绑定其中的一个的时候,你必须消除你传递给 def() 的 成员
函数
指针的歧义.为此,你可以用普通C风格的类型转换来转型匹配正确的重载
函数
. 为此,你必须知道怎么去 描述
C++
成员
函数
的类型.这里有一个简短的教程(更多信息请查阅你的
C++
参考书): 成员
函数
指着的语法如下: return-value (class-name::*)(arg1-type, arg2-type, ...) 例如: struct A { void f(int); void f(int, int); }; class_
() .def("f", (void(A::*)(int))&A::f) A的第一个成员
函数
f(int)被绑定了,而第二个没哟被绑定. 8.2 属性 很容易注册类的全局数据成员.考虑如下的类: struct A { int a; }; 这个类可以这样注册: module(L) [ class_
("A") .def_readwrite("a", &A::a) ]; 这使得成员变量 A::a 获得了读写访问权. 还可以注册一个只读的属性: module(L) [ class_
("A") .def_readonly("a", &A::a) ]; 当绑定成员是一个非原始数据类型的时候,自动生成的 getter
函数
将会
返回
一个它引用. 这就允许你可以链式使用 . 操作符.例如,当有一个结构体包含另外一个结构体的时候.如下: struct A { int m; }; struct B { A a; }; 当绑定B到
Lua
的时候,下面的
表
达式应该可以工作: b = B() b.a.m = 1 assert(b.a.m == 1) 这要求 a 属性必须
返回
一个A的引用, 而不是一个拷贝. 这样,
Lua
Bind
将会自动使用依赖策略来 确保
返回
值依赖于它所在的对象.所以,如果
返回
的引用的生命长于该对象的所有的引用(这里是b). 它将保持对象是激活的,从而避免出现悬挂指针. 你还可以注册 getter 或者 setter
函数
来使得它们看上去像一个 public 的成员.考虑下面的类: class A { public: void set_a(int x) { a = x; } int get_a() const { return a; } private: int a; }; 可以这样注册成一个公共数据成员: class_
("A") .property("a", &A::get_a, &A::set_a) 这样 set_a() 和 get_a() 将取代简单的数据成员操作.如果你想使之只读,你只需要省略最后一个参数. 请注意, get
函数
必须是 const 的,否则不能通过编译. 8.3 枚举 如果你的类包含枚举,你可以注册它们到
Lua
. 注意,它们不是类型安全的,所有的枚举在
Lua
侧都是整型的, 并且所有接受枚举参数的
函数
都将接受任何整型.你可以像这样注册它们: module(L) [ class_
("A") .enum_("constants") [ value("my_enum", 4), value("my_2nd_enum", 7), value("another_enum", 6) ] ]; 在
Lua
侧,他们可以像数据成员那样被操作,除了它们是只读的而且属于类本身而不是类的实例.
Lua
5.0 Copyright (C) 1994-2003 Tecgraf, PUC-Rio > print(A.my_enum) 4 > print(A.another_enum) 6 8.4 操作符 为了绑定操作符,你需要包含头文件 <
lua
bind
/operator.hpp>. 注册你的类的操作符的机制非常的简单.你通过一个全局名字
lua
bind
::self 来引用类自己,然后你就 可以在def()调用里面直接用操作符
表
达式. 类如下: struct vec { vec operator+(int s); }; 可以这样注册: module(L) [ class_
("vec") .def(self + int()) ]; 不管你的 + 操作符是定义在类里面还是自由
函数
都可以工作. 如果你的操作符是常量的(const)(或者,是一个自由
函数
, 接受一个类的常量的引用)你必须用 const_self 替代 self. 如下: module(L) [ class_
("vec") .def(const_self + int()) ]; 支持如下操作符: + - * / == < <= 这意味着,没有"就地操作符"(in-place)(++ --). 相等操作符(==)有些敏锐;如果引用是相等的就不会 被调用. 这意味着, 相等操作符的效率非常好.
Lua
不支持操作符包括: !=,>和<=.这是为什么你只能注册上面那些操作符. 当你调用这些操作符的时候,
Lua
会把调用转换到支持的操作符上.(译注:例如:==和!=有逻辑非得关系) -Linker Lin 4/6/08 11:09 PM 在上面的示例中,操作数的类型是 int().如果操作数的类型是复杂类型,就不是那么简单了,你需要用 other<> 来包装下.例如: 为了注册如下的类,我们不想用一个string的实例来注册这个操作符. struct vec { vec operator+(std::string); }; 取而代之的是,我们用 other<> 包装下,如下: module(L) [ class_
("vec") .def(self + other
()) ]; 注册一个应用程序操作符(
函数
调用): module(L) [ class_
("vec") .def( self(int()) ) ]; 这里有个特殊的操作符.在
Lua
里,它叫做 __tostring,它不是一个真正的操作符.它是被用来转换一个对象到 string的标准
Lua
方法.如果你注册之,可以通过
Lua
的标准
函数
tostring() 来转换你的对象到一个string. 为了在
C++
里实现这个操作符,你需要为 std::ostream 提供 operator<< .像这样: class number {}; std::ostream& operator<<(std::ostream&, number&); ... module(L) [ class_
("number") .def(tostring(self)) ]; 8.5 嵌套作用域和静态
函数
可以添加嵌套的作用域到一个类.当你需要包装一个嵌套类或者一个静态
函数
的时候就会很有用. class_
("foo") .def(constructor<>() .scope [ class_
("nested"), def("f", &f) ]; 在上面的例子里, f 将
表
现的像一个类 foo 的静态
函数
,而 类 nested 将
表
现的像类 foo 的嵌套类. 还可以用同样的语法添加名空间到类里面. 8.6 继承类 如果你想要注册一个继承自其它类的类到
Lua
, 你可以指定一个模板参数 bases<> 给 class_ 的构造器. 如下的继承关系: struct A {}; struct B : A {}; 可以这样注册: module(L) [ class_
("A"), class_
("B") ]; 如果你使用了多继承,你可以指定多于一个的基类.如果 B 还继承了类 C , 它可以这样注册: module(L) [ class_
>("B") ]; 注意,你可以省去 bases<> 当你用的是单继承的时候. 注意 如果你不指定类的继承关系,
Lua
Bind
将不能在相关的继承类型间进行隐式类型转换. 8.7 智能指针 当你注册一个类的时候,你可以告诉
Lua
Bind
所有的该类的实例应该被某种智能指针持有.(例如: boost::shared_ptr) 你可通过把一个 持有器类型模板参数 给 class_ 类的构造器来实现该功能.例如: module(L) [ class_
>("A") ]; 你还必须为你的智能指针提供两个
函数
.一个
返回
常量版本的智能指针类型(这里是: boost:shared_ptr< const A >). 另一个
函数
要可以从智能指针萃取流指针(raw pointer). 之所以需要第一个
函数
是因为,
Lua
Bind
允许 非常量 -> 转换在传递
Lua
值到
C++
的时候.之所以需要第二个
函数
是因为,当
Lua
调用一个被智能指针持有 的类的成员
函数
的时候,this 指针必须是一个流指针.还有一个原因是,从
Lua
转换到
C++
的时候,需要实现 智能指针到普通指针的转换.看上去像这样: namespace
lua
bind
{ template
T* get_pointer(boost::shared_ptr
& p) { return p.get(); } template
boost::shared_ptr
* get_const_holder(boost::shared_ptr
*) { return 0; } } 第二个
函数
只在编译时用于映射 boost::shared_ptr
到其常量版本 boost::shared_ptr
. 它从来不会被调用,所以
返回
值是无所谓的(
返回
值的类型才是关键). 这个转换将这样工作(假定 B 是A的基类): 从
Lua
到
C++
Source Target holder_type
A* holder_type
B* holder_type
A const* holder_type
B const* holder_type
holder_type
holder_type
holder_type
holder_type
A const* holder_type
B const* holder_type
holder_type
C++到
Lua
Source Target holder_type
holder_type
holder_type
holder_type
holder_type
const& holder_type
holder_type
const& holder_type
当使用持有器类型的时候,知道指针是不是合法(例如:非空)是很有用的.例如,当使用 std::auto_ptr 的时候, 持有器通过一个参数传递给
函数
的时候将会变得无效. 为了这个目的,所有的对象实例都有一个成员叫: __ok. struct X {}; void f(std::auto_ptr
); module(L) [ class_
>("X") .def(constructor<>()), def("f", &f) ];
Lua
5.0 Copyright (C) 1994-2003 Tecgraf, PUC-Rio > a = X() > f(a) > print a.__ok false 当注册一个继承树的时候,所有的实例被智能指针持有的地方,所有的类必须包含持有器类型.例如: module(L) [ class_
>("base") .def(constructor<>()), class_
>("base") .def(constructor<>()) ]; 在内部,
Lua
Bind
将会做必要的转换于萃取自持有器的流指针之上. 8.8 拆分类注册 在某些情况下,可能需要分开注册一个类在不同的编译单元. 部分原因可能是节约重编译时间,而某些编译器的 限制可能要求不得不分开注册一个类.其实很简单.考虑下面的示例代码: void register_part1(class_
& x) { x.def(/*...*/); } void register_part2(class_
& x) { x.def(/*...*/); } void register_(
lua
_State* L) { class_
x("x"); register_part1(x); register_part2(x); module(L) [ x ]; } 这里,类X被分两步注册.两个
函数
register_part 和 register_part2 可能被放到不同的编译单元里. 关于分开注册一个模块的信息请参阅: 分开注册 章节. 9 对象 因为
函数
必须能够接受
Lua
值作为参数,我们必须包装之. 这个包装被称作
lua
bind
::object. 如果你注册的
函数
接受一个对象,那它就可以匹配任何
Lua
值.为了使用它,你需要包含头文件: <
lua
bind
/object.hpp>. 摘要 class object { public: template
object(
lua
_State*, T const& value); object(from_stack const&); object(object const&); object(); ~object();
lua
_State* interpreter() const; void push() const; bool is_valid() const; operator safe_bool_type () const; template
implementation-defined operator[](Key const&); template
object& operator=(T const&); object& operator=(object const&); bool operator==(object const&) const; bool operator<(object const&) const; bool operator<=(object const&) const; bool operator>(object const&) const; bool operator>=(object const&) const; bool operator!=(object const&) const; template
implementation-defined operator[](T const& key) const void swap(object&); implementation-defined operator()(); template
implementation-defined operator()(A0 const& a0); template
implementation-defined operator()(A0 const& a0, A1 const& a1); /* ... */ }; 当你需要一个
Lua
对象的时候,你可以通过=操作符给它赋一个新值.当你这么做的时候,default_policy 会被用来转换
C++
值到
Lua
. 如果你的
lua
bind
::object 是一个table,你可以通过 []操作符或者迭代器 来访问它的成员.[]操作符的
返回
值是一个代理对象,这个对象可以用于读写
表
里的值(通过=操作符). 注意,没有办法知道一个
Lua
对象是否可以索引化访问(
lua
_gettable 不会失败,要不成功,要不崩溃 ). 这意味着,如果你在一个不可以索引化访问的东西上进行索引,你就只能靠自己了.
Lua
将会调用它的 panic()
函数
. 还有一些自由
函数
可以用来索引一张table,参阅 相关
函数
章节. 那个接受 from_stack 对象作为参数的构造器是用来初始化一个关联
Lua
栈值的对象的. from_stack 类型 有如下的构造器: from_stack(
lua
_State* L, int index); index参数就是原始的
Lua
栈的索引,负值是从栈顶开始索引的.你可以这样用: object o(from_stack(L, -1)); 这将会创建一个 object的实例 o,并拷贝
Lua
栈顶的对象的值. interpreter()
函数
返回
保存object实例的
Lua
状态机.如果你想要直接用
Lua
函数
操作object对象的实例,你 可以通过调用 push() 来把它压入
Lua
栈. ==操作符将会在操作数上调用
lua
_equal()并
返回
它的结果. is_valid()
函数
会告诉你object的实例是否已经初始化过了.通过默认构造器来初始化的实例是非法的.要使之 合法,你可以给其赋一个值.如果你想使一个 object 不合法,最简单的办法就是给它赋一个非法的 object. operator safe_bool_type() 和 to is_valid() 是等价的.这意味着,下面的代码片段是等价的: object o; // ... if (o) { // ... } ... object o; // ... if (o.is_valid()) { // ... } 应用程序操作符() 将会像对待一个
函数
那样来调用绑定的值. 你可以给它任何数量的参数 (目前, default_policy 将被用于转换 ).
返回
的对象将代
表
函数
的
返回
值(当前只支持一个
返回
值).该操作符 可能会抛出
lua
bind
::error ,如果
函数
调用失败.如果你想指定一个特殊的
函数
调用策略,你可以通过在
函数
调用时使用 []操作符来指定策略.像这样: my_function_object( 2 , 8 , new my_complex_structure(6) ) [ adopt(_3) ]; 这告诉
Lua
Bind
让
Lua
接受所有权和负责传入给
lua
函数
的指针. 重要的是当
Lua
状态机关闭的时候,所有的 object 的实例都会被析构.object实例会持有
Lua
状态机的指针,并在 自己析构的时候释放它的
Lua
对象. 这里有一个
函数
怎样使用 table 的例子: void my_function(object const& table) { if (type(table) ==
LUA
_TTABLE) { table["time"] = std::clock(); table["name"] = std::rand() < 500 ? "unusual" : "usual"; std::cout << object_cast
(table[5]) << "\n"; } } 如果
函数
接受一个object作为参数,那么任何
Lua
值都将匹配这个参数.这就是为什么,我们必须保证入参是一个table 的原因. std::ostream& operator<<(std::ostream&, object const&); 流操作符可以把object实例借由 boost::lexical_cast 转换到string或者方便打印输出.这将会使用
Lua
的string 转换
函数
.如果你用 tostring 去转换一个
C++
对象,对应类型的流操作符将会被使用. 9.1 迭代器 有两种迭代器. 普通迭代器将会使用对象的原方法(如果存在)来获取值. 普通迭代器被称为
lua
bind
::iterator. 另一个 迭代器被称为
lua
bind
::raw_iterator ,它将忽略原方法而直接给出
表
里的真实内容. 它们具有相同的接口, 都实现了 ForwardIterator 概念.大部分标准迭代器都有如下的成员和构造器: class iterator { iterator(); iterator(object const&); object key() const; standard iterator members }; 接受一个
lua
bind
::object 的构造器实际上是一个用于操作 object 的模板.通过传入一个 object 给构造器来构造出 一个指向 object 里的第一个元素的迭代器. 缺省的构造器将会初始化迭代器为一个指向最后一个元素的后面位置的迭代器.这可以用来测试是否抵达了序列的末端. 迭代器的值类型是一个支持和
lua
bind
::object 相同的操作的代理类型.这意味着,大部分情况下你可以当它就是一个原始 的 object 实例. 它们之间的不同之处在于,任何对代理的赋值操作都会导致值被插入到
表
中迭代器所指的位置. key() 成员
返回
迭代器用于索引
表
的键. 一个迭代器的例子如下: for (iterator i(globals(L)["a"]), end; i != end; ++i) { *i = 1; } end 迭代器是一个缺省的指向序列末尾的迭代器.在这个例子里,我们简单的迭代了
表
a 里面所有的实体,并将之赋值为 1. 9.2 相关
函数
这里介绍些用于 对象 和
表
操作的
函数
. int type(object const&); 这个
函数
将会
返回
lua
类型索引.例如: .
LUA
_TNIL,
LUA
_TNUMBER 等. template
void settable(object const& o, K const& key, T const& value); template
object gettable(object const& o, K const& key); template
void rawset(object const& o, K const& key, T const& value); template
object rawget(object const& o, K const& key); 这些
函数
是用来索引 table 用的. settable 和 gettable
函数
分别翻译调用到
lua
_settable 和
lua
_gettable
函数
. 这意味着,你可以在对象上使用索引操作符. rawset 和 rawget 将会翻译调用到
lua
_rawset 和
lua
_rawget. 所以他们可以绕开任何原方法而给你
表
里实体的 真实值. template
T object_cast
(object const&); template
T object_cast
(object const&, Policies); template
boost::optional
object_cast_nothrow
(object const&); template
boost::optional
object_cast_nothrow
(object const&, Policies); object_cast
函数
转型对象的值到
C++
值.你可以给这个从
lua
到
C++
的转换提供一个转换策略.如果转型失败, cast_failed 异常将被抛出. 如果你已经定义了
LUA
BIND
_NO_ERROR_CHECKING (参阅 编译选项)宏,就不会 进行任何检查,如果转型非法,应用程序将会彻底崩溃. 不抛出异常的版本会
返回
一个没有初始化的 boost::optional
对象,由此来指出转型不能进行. 上面的
函数
的签名确实是模板化的 object 参数,但是这里你应该只传递 object 对象. object globals(
lua
_State*); object registry(
lua
_State*); 这些
函数
分别
返回
全局环境
表
和
Lua
注册
表
. object newtable(
lua
_State*); 这个
函数
创建一个新的 table 并以一个 object 来
返回
它. 10 在
Lua
里定义类 作为一个附加功能,
Lua
Bind
还提供了一个
Lua
侧OO系统来绑定
C++
函数
和对象. class '
lua
_testclass' function
lua
_testclass:__init(name)-- 译注:这个风格类似Python的OO语法 self.name = name end function
lua
_testclass:print() print(self.name) end a =
lua
_testclass('example') a:print() 在
Lua
类之间可以使用继承: class 'derived' (
lua
_testclass) function derived:__init() super('derived name') end function derived:print() print('Derived:print() -> ')
lua
_testclass.print(self)-- 译注:注意这里 : 和 . 的区别 end 这里的 super 关键字用来初始化基类.用户必须在构造器里面第一个调用 super. 正如你在这个例子里看到的,你可以调用基类的成员
函数
.你可以找到所有的基类成员,但是你必须把 this指针(self) 做为
函数
的第一个参数. 10.1 在
Lua
里继承 你还可以从
Lua
侧继承一个
C++
类,并用
Lua
函数
来覆写虚
函数
.为了实现这个,我们必须为
C++
基类创建一个封装类. 当我们实例化一个
Lua
类的时候,这个封装类将持有
Lua
对象. class base { public: base(const char* s) { std::cout << s << "\n"; } virtual void f(int a) { std::cout << "f(" << a << ")\n"; } }; struct base_wrapper : base,
lua
bind
::wrap_base { base_wrapper(const char* s) : base(s) {} virtual void f(int a) { call
("f", a); } static void default_f(base* ptr, int a) { return ptr->base::f(a); } }; ... module(L) [ class_
("base") .def(constructor
()) .def("f", &base::f, &base_wrapper::default_f) ]; 重要 因为MSVC6.5不支持成员
函数
的显示模板参数化,作为成员
函数
call()的替代, 你可以调用自由
函数
call_member()并把 this指针作为第一个参数传入该
函数
. 注意,如果你同时绑定 base 类 和 base类封装,你必须把基类和基类的封装一起作为模板参数提供给 class_ (就像上面的例子中所做的一样).你指定它们的顺序并不重要.你必须还要从wrapper注册静态版本的和虚
函数
版 本的封装
函数
,这是让
Lua
Bind
实现动态和静态分派
函数
调用的必须. 重要 极其重要的是静态(缺省)
函数
的签名必须和虚
函数
一致.
lua
_test.rar_
C++
_c#
lua
_
c++
lua
_
lua
_
lua
调用
C++
lua
调用
c++
函数
的实例,
C++
工程里使用
LUA
脚本,
lua
脚本调用
c++
工程的
函数
lua
bind
、
lua
测试
用于
lua
脚本测试,
lua
bind
,里面可以自己加脚本
lua
和
c++
交互
很适合初学者学习,这是一个
Lua
和
C++
交互的一个简单例子,简单明了,容易理解
工具平台和程序库
24,860
社区成员
27,333
社区内容
发帖
与我相关
我的任务
工具平台和程序库
C/C++ 工具平台和程序库
复制链接
扫一扫
分享
社区描述
C/C++ 工具平台和程序库
社区管理员
加入社区
获取链接或二维码
近7日
近30日
至今
加载中
查看更多榜单
社区公告
暂无公告
试试用AI创作助手写篇文章吧
+ 用AI写文章