用LuaBridge为Lua绑定C/C++对象

最近为了总结Lua绑定C/C++对象的各种方法、第三方库和原理,学习了LuaBridge库为Lua绑定C/C++对象,下面是学习笔记,实质是对该库的 Reference Manual 基本上翻译了一遍,学习过程中测试代码,放在 我的github 上。

LuaBridge的主要特点 
     源码只有头文件,没有.cpp文件,没有MakeFile,使用时只需一个#include即可。 
     支持不同的对象生命周期管理模式。 
     对Lua栈访问方便并且是类型安全的(type-safe)。 
     Automatic function parameter type binding. 
     Easy access to Lua objects like tables and functions. 
     LuaBridge的API是基于C++模板元编程(template metaprogramming)的。在编译时这些模板自动生成各种Lua API调用,从而可以再Lua脚本中使用C++程序中的类和函数。 为了能在C++中使用Lua的数据,比如number,string,table以及方便调用Lua的函数,使用LuaBridge中的LuaRef类,可以方便做到。 
LuaBridge设计原则 
     由于LuaBridge的设计目标尽可能方便使用,比如只有头文件、没有用到高级C++的语法、不需要配置。因此LuaBridge性能虽足够好,但并不是最好的,比如 OOLua(https://code.google.com/p/oolua/)执行效率就比它好,并且它也不像LuaBind(http://www.rasterbar.com/products/luabind.html)那样功能全面。 LuaBridge不支持下面特性: 
     枚举型常量 
     不支持8个以上的函数或方法的调用 
     重载函数、方法和构造函数(Overloaded functions, methods, or constructors) 
     全局变量(变量必须被包装在命名空间里) 
     自动地转换STL容器类型和Table 
     在Lua中继承C++类(Inheriting Lua classes from C++ classes)。 
     Passing nil to a C++ function that expects a pointer or reference 
     Standard containers like std::shared_ptr 
在Lua访问C++ 
     为了在Lua中使用C++中的数据和函数,LuaBridge要求任何需要使用的数据的都需要注册。LuaBridge可以注册下面五种类型数据: 
     Namespaces  一个Lua table包含了其他注册信息 
     Data  全局变量或静态变量、数据成员或静态数据成员 
     Functions  一般函数、成员函数或静态成员函数 
     CFunctions  A regular function, member function, or static member function that uses the lua_CFunction calling convention 
     Properties  Global properties, property members, and static property members. These appear like data to Lua, 
     but are implemented in C++ using functions to get and set the values.


     Data和Properties在注册时被标记为只读(read-only)。这不同于const,这些对象的值能在C++中修改,但不能在Lua脚本中修改。


Namespaces 
     LuaBridge索引的注册都是在一个namespace中,namespace是从lua角度来看的,它实质上就是table,注意这里的namespace不是C++中的namespace,C++的namespace 不是一定需要的。LuaBridge的namespace是对Lua脚本来说的,它们被作为逻辑组合工具(logical grouping tool)。为了访问Lua的全局命名空间(global namespace),可以在C++ 中,这样调用: 
getGlobalNamespace (L);
上面的调用会返回一个对象(实质是table)可用来进一步注册,比如: 
     getGlobalNamespace (L).beginNamespace ("test");
上面的调用就会在Lua的_G中创建一个名为"test"的table,现在这个table还是空的。LuaBridge保留所有以双下划线开头命名的标识,因此__test是无效的命名, 尽管这样命名LuaBridge不会报错。我们可以进一步扩展上面的注册: 
    getGlobalNamespace (L).beginNamespace ("test").beginNamespace ("detail").endNamespace ().beginNamespace ("utility").endNamespace ().endNamespace ();
这样注册后,我们就可以在Lua中使用test, test.detail,和test.utility。这里的引入的endNamespace函数,也会返回一个对象(实质也是table),该对象实质就是上一层namespace, 表示当前namespace注册完成。 All LuaBridge functions which create registrations return an object upon which subsequent registrations can be made, allowing for an unlimited number of registrations to be chained together using the dot operator。在一个namespace中,注册相同命名的对象,对于LuaBridge来说是没有 定义的行为。一个namespace可以多次使用增加更多的成员。比如下面两段代码是等价的: 
    getGlobalNamespace (L).beginNamespace ("test").addFunction ("foo", foo).endNamespace ();getGlobalNamespace (L).beginNamespace ("test").addFunction ("bar", bar).endNamespace ();
和 
    getGlobalNamespace (L).beginNamespace ("test").addFunction ("foo", foo).addFunction ("bar", bar).endNamespace ();
Data, Properties, Functions, and CFunctions 
     Data, Properties, Functions, and CFunctions可以依次使用addVariable,, addProperty, addFunction, and addCFunction来注册。在Lua脚本中调用注册的函数时, LuaBridge会自动地传入相应的参数,并对参数类型转和检查。同样,函数的返回值也会自动处理。当前LuaBridge最多可处理8个参数。Pointers, references, and objects of class type as parameters are treated specially。如果我们在C++中有以下定义: 
	int globalVar;static float staticVar;std::string stringProperty;std::string getString () { return stringProperty; }void setString (std::string s) { stringProperty = s; }int foo () { return 42; }void bar (char const*) { }int cFunc (lua_State* L) { return 0; }
为了在Lua使用这些变量和函数,我们可以按以下方式注册它们: 
    getGlobalNamespace (L).beginNamespace ("test").addVariable ("var1", &globalVar).addVariable ("var2", &staticVar, false)     // read-only.addProperty ("prop1", getString, setString).addProperty ("prop2", getString)		// read only.addFunction ("foo", foo).addFunction ("bar", bar).addCFunction ("cfunc", cFunc).endNamespace ();
Variables在注册时,可以通过传递第二个参数为false,确保Variables不会在Lua被修改,默认第二个参数是true。Properties在注册时,若不传递set函数,则在脚本中是read-only。 
通过上面注册后,则下面表达式在Lua是有效的: 
	test		-- a namespace,实质就是一个table,下面都是table中的成员test.var1   -- a lua_Number variabletest.var2   -- a read-only lua_Number variabletest.prop1  -- a lua_String propertytest.prop2  -- a read-only lua_String propertytest.foo	-- a function returning a lua_Numbertest.bar	-- a function taking a lua_String as a parametertest.cfunc  -- a function with a variable argument list and multi-return
注意test.prop1和test.prop2引用的C++中同一个变量,然后test.prop2是read-only,因此在脚本中对test.prop2赋值,会导致运行时错误(run-time error)。在Lua按以下方式使用: 
	test.var1 = 5		 -- okaytest.var2 = 6		 -- error: var2 is not writabletest.prop1 = "Hello"  -- okaytest.prop1 = 68	   -- okay, Lua converts the number to a string.test.prop2 = "bar"	-- error: prop2 is not writabletest.foo ()		   -- calls foo and discards the return valuetest.var1 = foo ()	-- calls foo and stores the result in var1test.bar ("Employee") -- calls bar with a stringtest.bar (test)	   -- error: bar expects a string not a table

Class Objects 
类的注册是以beginClass或deriveClass开始,以endClass结束。一个类注册完后,还可以使用beginClass重新注册更多的信息,但是deriveClass只能被使用一次。为了给已经用deriveClass注册的类,注册更多的信息,可以使用beginClass。 
class A { public:A() { printf("A constructor\n");}static int staticData;static int getStaticData() {return staticData;}static float staticProperty;static float getStaticProperty () { return staticProperty; }static void setStaticProperty (float f) { staticProperty = f; }static int staticCFunc (lua_State *L) { return 0; }std::string dataMember;char dataProperty;char getProperty () const { return dataProperty; }void setProperty (char v) { dataProperty = v; }void func1 () {printf("func1 In Class A\n"); }virtual void virtualFunc () {printf("virtualFunc In Class A\n");  }int cfunc (lua_State* L) { printf("cfunc In Class A\n");  return 0; }};class B : public A {public:B() { printf("B constructor\n");}double dataMember2;void func1 () {printf("func1 In Class B\n"); }void func2 () { printf("func2 In Class B\n"); }void virtualFunc () {printf("virtualFunc In Class B\n");  }};int A::staticData = 3;float A::staticProperty = 0.5;
按下面方式注册: 
getGlobalNamespace (L).beginNamespace ("test").beginClass<A>("A").addConstructor <void (*) (void)> ().addStaticData ("staticData", &A::staticData).addStaticProperty ("staticProperty", &A::getStaticData).addStaticFunction ("getStaticProperty", &A::getStaticProperty) //read-only.addStaticCFunction ("staticCFunc", &A::staticCFunc).addData ("data", &A::dataMember).addProperty ("prop", &A::getProperty, &A::setProperty).addFunction ("func1", &A::func1).addFunction ("virtualFunc", &A::virtualFunc).addCFunction ("cfunc", &A::cfunc).endClass ().deriveClass<B, A>("B").addConstructor <void (*) (void)> ().addData ("data", &B::dataMember2).addFunction ("func1", &B::func1).addFunction ("func2", &B::func2).endClass ().endNamespace ();
注册后,可以再Lua脚本中按一下方式使用: 
local AClassObj = test.A ()  --create class A instanceprint("before:",test.A.staticData) -- access class A static member                                     test.A.staticData = 8  -- modify class A static member                                                 print("after:",test.A.staticData)                                                                      print("before:", test.A.getStaticProperty())                                                           --test.A.staticProperty = 1.2 --error:can not modify                                                   print("staticCFunc")test.A.staticCFunc()AClassObj.data = "sting"print("dataMember:",AClassObj.data)AClassObj.prop = 'a'print("property:",AClassObj.prop)AClassObj:func1()AClassObj:virtualFunc()AClassObj:cfunc()BClassObj = test.B()BClassObj:func1()BClassObj:func2()                                                                                      BClassObj:virtualFunc()
       
其输出结果为: 
A constructorbefore: 3after:  8before: 0.5staticCFuncdataMember:     stingproperty:       afunc1 In Class AvirtualFunc In Class Acfunc In Class AA constructorB constructorfunc1 In Class Bfunc2 In Class BvirtualFunc In Class B
类的方法注册类似于通常的函数注册,虚函数也是类似的,没有特殊的语法。在LuaBridge中,能识别const方法并且在调用时有检测的,因此如果一个函数返回一个const object或包含指向const object的数据给Lua脚本,则在Lua中这个被引用的对象则被认为是const的,它只能调用const的方法。对于每个类,析构函数自动注册的。无须在继承类中重新注册已在基类中注册过的方法。If a class has a base class that is **not** registeredwith Lua, there is no need to declare it as a subclass. 
Constructors 
为了在Lua中,创建类的对象,必须用addConstructor为改类注册构造函数。并且LuaBridge不能自动检测构造函数的参数个数和类型(这与注册函数或方法能自动检测是不同的),因此在用注册addConstructor时必须告诉LuaBridge在Lua脚本将用到的构造函数签名,例如: 
struct A {A ();};struct B {explicit B (char const* s, int nChars);};getGlobalNamespace (L).beginNamespace ("test").beginClass <A> ("A").addConstructor <void (*) (void)> ().endClass ().beginClass <B> ("B").addConstructor <void (*) (char const*, int)> ().endClass ();.endNamespace ()
在Lua中,就可以一些方式,创建A和B的实例: 
a = test.A ()           -- Create a new A.b = test.B ("hello", 5) -- Create a new B.b = test.B ()           -- Error: expected string in argument 1
lua_State* 
有时候绑定的函数或成员函数,需要lua_State*作为参数来访问栈。使用LuaBridge,只需要在将要绑定的函数最后添加lua_State*类型的参数即可。比如: 
void useStateAndArgs (int i, std::string s, lua_State* L);                                       getGlobalNamespace (L).addFunction ("useStateAndArgs", &useStateAndArgs);
在Lua中,就可按以下方式使用: 
useStateAndArgs(42,"hello")
在脚本中,只需传递前面两个参数即可。注意 lua_State*类型的参数就放在定义的函数最后,否则结果是未定义的。 
Class Object Types 
一个注册的类型T,可能以下方式传递给Lua脚本: 
`T*` or `T&`: 		Passed by reference, with _C++ lifetime_.`T const*` or `T const&`: 	Passed by const reference, with _C++ lifetime_.`T` or `T const`: 		Passed by value (a copy), with _Lua lifetime_.
C++ Lifetime 
对于C++ lifetime的对象,其创建和删除都由C++代码控制,Lua GC不能回收这些对象。当Lua通过lua_State*来引用对象时,必须确保该对象还没删除,否则将导致未定义的行为。例如,可按以下方法给Lua传递 
C++ lifetime的对象: 
A a;push (L, &a);		 // pointer to 'a', C++ lifetimelua_setglobal (L, "a");push (L, (A const*)&a);   // pointer to 'a const', C++ lifetimelua_setglobal (L, "ac");push <A const*> (L, &a);  // equivalent to push (L, (A const*)&a)lua_setglobal (L, "ac2");push (L, new A);	    // compiles, but will leak memorylua_setglobal (L, "ap");
Lua Lifetime
当C++通过值传递给Lua一个对象时,则该对象是Lua lifetime。在值传递时,该对象将在Lua中以userdata形式保存,并且当Lua不再引用该对象时,该对象可以被GC回收。当userdata被回收时,其相应对象的 
析构函数也会被调用。在C++中应用lua lifetime的对象时,必须确保该对象还没被GC回收,否则其行为是未定义的。例如,可按以下方法给Lua传递的是Lua lifetime的催下: 
B b;push (L, b);                    // Copy of b passed, Lua lifetime.lua_setglobal (L, "b");
当在Lua中调用注册的构造函数创建一个对象时,该对象同样是Lua lifetime的,当该对象不在被引用时,GC会自动回收该对象。当然你可以把这个对象引用作为参数传递给C++,但需要保证C++在通过引用使用该对象时, 
改对还没有被GC回收。 
 Pointers, References, and Pass by Value 
当C++对象作为参数从Lua中传回到C++代码中时,LuaBridge会尽可能做自动转换。比如,向Lua中注册了以下C++函数: 
void func0 (A a);                                                                                void func1 (A* a);                                                                               void func2 (A const* a);                                                                         void func3 (A& a);                                                                               void func4 (A const& a);
则在Lua中,就可以按以下方式调用上面的函数: 
func0 (a)   -- Passes a copy of a, using A's copy constructor.                                   func1 (a)   -- Passes a pointer to a.                                                            func2 (a)   -- Passes a pointer to a const a.                                                    func3 (a)   -- Passes a reference to a.                                                          func4 (a)   -- Passes a reference to a const a.
上面所有函数,都可以通过a访问对象的成员以及方法。并且通常的C++的继承和指针传递规则也使用。比如: 
void func5 (B b);                                                                                void func6 (B* b);
在lua中调用: 
func5 (b)   - Passes a copy of b, using B's copy constructor.                                    func6 (b)   - Passes a pointer to b.                                                             func6 (a)   - Error: Pointer to B expected.                                                      func1 (b)   - Okay, b is a subclass of a.
当C++给Lua传递的指针是NULL时,LuaBridge会自动转换为nil代替。反之,当Lua给C++传递的nil,相当于给C++传递了一个NULL指针。 

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mzph.cn/news/509677.shtml

如若内容造成侵权/违法违规/事实不符,请联系多彩编程网进行投诉反馈email:809451989@qq.com,一经查实,立即删除!

相关文章

H5动画制作流程没写完。。。

通过 keyframes 规则&#xff0c;您能够创建动画。创建动画的原理是&#xff0c;将一套 CSS 样式逐渐变化为另一套样式。在动画过程中&#xff0c;您能够多次改变这套 CSS 样式。以百分比来规定改变发生的时间&#xff0c;或者通过关键词 "from" 和 "to"&a…

串的基本操作

#include <iostream> #include <algorithm> #include <cstring> #define sqtrmax 1000 using namespace std; typedef struct { char *data; int length; } sqtr; void StrAssign (sqtr &S, char *chars); //赋值 void Destroy(sq…

Result window is too large, from + size must be less than or equal to: [10000] but was [12390]. See

ES 查询报错 Caused by: java.lang.IllegalArgumentException: Result window is too large, from size must be less than or equal to: [10000] but was [12390]. See the scroll api for a more efficient way to request large data sets. This limit can be set by chan…

Delta3d插件机制

Delta3d插件机制主要通过以下两个类实现&#xff1a; class MainWindow;/**Abstract interface class for STAGE plugins*/class Plugin{public:virtual ~Plugin() {} /** Is called after instantiation */virtual void Create() {}/** Is called before destruction */virt…

java中泛型学习总结

为什么需要使用泛型: 1):存储任意类型的数据在集合中 ,但是取出来都是Object类型的,此时就得强转.List list new ArrayList();list.add(1); //Interger类型Object ele list.get(0); //现在需要调用Interger类中的方法I nterger num (Interger) ele;System.out.println(num);…

数据结构实验之栈:行编辑器

题目描述 一个简单的行编辑程序的功能是&#xff1a;接受用户从终端输入的程序或数据&#xff0c;并存入用户的数据区。 由于用户在终端上进行输入时&#xff0c;不能保证不出差错&#xff0c;因此&#xff0c;若在编辑程序中&#xff0c;“每接受一个字符即存入用户数据区”的…

CDH、CM下载403,Cloudera收费无法下载解决,CDH安装包下载

CDH下载 6.3.2 链接: https://pan.baidu.com/s/1e1LmRY7aHQSCMJq3Lz6DtA 密码: 1vje --来自百度网盘超级会员V6的分享6.3.1 链接: https://pan.baidu.com/s/1Xsj_zDvuJ12q3pGTY77BRg 密码: f9h3 --来自百度网盘超级会员V6的分享6.2.1 链接: https://pan.baidu.com/s/10s7…

别说“我已经很努力了”

转自&#xff1a;http://blog.csdn.net/foruok/article/details/40247543 我们程序员的努力与挣扎有时非常尴尬&#xff0c;如果没有结果&#xff0c;都是徒然&#xff0c;都是说不得说不得…… 我自己做项目经理时&#xff0c;干的项目也经常延期……非常惭愧。而延期其实对研…

Java集合框架-概述

Java集合框架的由来: 其实在Java2(jdk1.2)之前&#xff0c;Java是没有完整的集合框架的。它只有一些简单的可以自扩展的容器类&#xff0c;比如Vector&#xff0c;Stack&#xff0c;Hashtable等。 为什么存在容器类: 容器类(集合类)可以存储多个数据,既然数组可以存储多个数据…

MySQL Binlog增量同步工具go-mysql-transfer实现详解

go-mysql-transfer产品手册:https://www.kancloud.cn/wj596/go-mysql-transfer/2111996 一、 概述 工作需要研究了下阿里开源的MySQL Binlog增量订阅消费组件canal&#xff0c;其功能强大、运行稳定&#xff0c;但是有些方面不是太符合需求&#xff0c;主要有如下三点&#x…

数据结构实验之栈五:下一较大值(一)

题目描述 对于包含n&#xff08;1<n<1000&#xff09;个整数的序列&#xff0c;对于序列中的每一元素&#xff0c;在序列中查找其位置之后第一个大于它的值&#xff0c;如果找到&#xff0c;输出所找到的值&#xff0c;否则&#xff0c;输出-1。 输入 输入有多组&#xf…

与 C++11 多线程相关的头文件

C11 新标准中引入了四个头文件来支持多线程编程&#xff0c;他们分别是<atomic> ,<thread>,<mutex>,<condition_variable>和<future>。 <atomic>&#xff1a;该头文主要声明了两个类, std::atomic 和 std::atomic_flag&#xff0c;另外还…

数据结构实验之栈二:一般算术表达式转换成后缀式

题目描述 对于一个基于二元运算符的算术表达式&#xff0c;转换为对应的后缀式&#xff0c;并输出之。输入 输入一个算术表达式&#xff0c;以‘#’字符作为结束标志。输出 输出该表达式转换所得到的后缀式。示例输入 a*b(c-d/e)*f# 示例输出 ab*cde/-f* #include<stdio.h…

INFINI GATEWAY 极限网关初体验 ElasticSearch 两个集群数据同步

文章目录极限网关-配置说明配置文件日志、数据目录定义入口定义路由定义流程定义资源使用Demo写入两个ES集群极限网关-常见问题shutdown: ORM handler is not registered极限网关地址极限网关-配置说明 极限网关的大部分配置都可以通过 gateway.yml 来进行配置&#xff0c;配置…

std::thread详解

转自&#xff1a;http://www.cnblogs.com/haippy/p/3236136.html 上一篇博客《C11 并发指南一(C11 多线程初探)》中只是提到了 std::thread 的基本用法&#xff0c;并给出了一个最简单的例子&#xff0c;本文将稍微详细地介绍 std::thread 的用法。 std::thread 在 <thread&…

Kafka 详细配置参数说明

参数值参数文件描述auto.create.topics.enableserver.properties【说明】是否允许自动创建Topic&#xff0c;若是false&#xff0c;就需要通过命令创建Topic。【默认值】true【取值范围】true或falselog.cleaner.backoff.msserver.properties【说明】检查是否有日志需要清理的时…

数据结构实验之栈三:后缀式求值

题目描述 对于一个基于二元运算符的后缀表示式&#xff08;基本操作数都是一位正整数&#xff09;&#xff0c;求其代表的算术表达式的值。输入 输入一个算术表达式的后缀式字符串&#xff0c;以‘#’作为结束标志。输出 求该后缀式所对应的算术表达式的值&#xff0c;并输出之…

std::mutex详解

Mutex 又称互斥量&#xff0c;C 11中与 Mutex 相关的类&#xff08;包括锁类型&#xff09;和函数都声明在 <mutex> 头文件中&#xff0c;所以如果你需要使用 std::mutex&#xff0c;就必须包含 <mutex> 头文件。 <mutex> 头文件介绍 Mutex 系列类(四种) st…

java中stack集合框架

栈(Stack):数据结构的一种,存储特点:Last In First Out. Stack 类表示后进先出&#xff08;LIFO&#xff09;的对象栈. 栈结构在生活中的体现: 1):QQ消息. A,B,C三个人先后发送消息,我们查看的时候发现最顶上的是最新的消息. 2):手枪弹夹的装和发射: 要来实现栈的存储,底层…

ElasticSearch Pipeline 为新增数据设置更新时间

文章目录模拟测试测试返回结果实际应用创建Pipeline查看创建Pipeline新增数据测试查看新增数据创建索引时直接设置Pipeline模拟测试 测试 POST _ingest/pipeline/_simulate {"pipeline": {"processors": [{"set": {"field": "t…