luabind 0.9.1版本尝试

luabind 0.9.1版本尝试
http://www.rasterbar.com/products/luabind

1. 编译luabind 0.9.1 linux版本
编译luabind需要bjam binary。
直接copy boost/1.37.0目录中编译好的bjam binary到~/bin目录,然后在luabin根目录中运行bjam。
编译之前需要设置环境变量BOOST_ROOT=~/mylibs/boost/1.37.0/ 和LUA_PATH=~/mylibs/lua/5.1/

2. 尝试第一个example,就发现了自己版本的lua5.1 binary不支持loadlib函数

print(loadlib())

解决办法是在lua src/luaconf.h文件中,将LUA_COMPAT_LOADLIB激活,然后重新编译lua binary

3. 编写C++对LUA的扩展库:
解决2之后,编写如下的C++代码(helloworld.cc) :

#include <iostream>
#include <luabind/luabind.hpp>

void greet()
{
    std::cout << "hello world - my luabind try\n";
}

extern "C" int init(lua_State* L)
{
    using namespace luabind;

    open(L);

    module(L)
    [
        def("greet", &greet)
    ];

    return 0;
 }

然后gcc编译:

g++ -o helloworld.so helloworld.cc -fPIC -shared -I/home/zeli/mylibs/boost/1.37.0/include -I/home/zeli/mylibs/luabind-0.9.1/ -I/home/zeli/mylibs/lua/5.1/include -L/home/zeli/mylibs/luabind-0.9.1/lib -L/home/zeli/mylibs/lua/5.1/lib -lluabind -llua  

你可以将后面一坨编译选项让一个shell脚本来生成(gcccmd.sh + x):

echo "-fPIC -shared -I/home/zeli/mylibs/boost/1.37.0/include -I/home/zeli/mylibs/luabind-0.9.1/ -I/home/zeli/mylibs/lua/5.1/include -L/home/zeli/mylibs/luabind-0.9.1/lib -L/home/zeli/mylibs/lua/5.1/lib -lluabind -llua"  

如此一来,你可以少敲很多字符:

g++ -o helloworld.so helloworld.cc `./gcccmd.sh`  

在当前目录下会有一个helloworld.so文件产生。进一步你可以用ldd/nm来看下helloworld.so文件包含了些什么。

4. 在LUA中调用C++的代码:
如果你在luabind目录中直接运行lua binary,然后loadlib将会发现如下错误

[zeli@p03bc luabind]$ lua
 Lua 5.1  Copyright (C) 1994-2006 Lua.org, PUC-Rio
 > loadlib('helloworld.so', 'init')()
 stdin:1: attempt to call a nil value
 stack traceback:
   stdin:1: in main chunk
   [C]: ?
 > 

这是因为lua binary放在~/bin目录下,而helloworld.so不跟它在同一目录。loadlib函数返回nil.
直接描述so文件的全路径可以解决这个问题:

>loadlib('/home/zeli/code/luabind/helloworld.so', 'init')()
> greet()
  hello world - my luabind try
> 

init是定义在so中的函数,符合lua的C-API规范。
在loadlib之后需要直接运行这个函数,才能将里面借助luabind的函数/类注册到lua vm环境中。
完成之后,lua vm中便有了greet C函数。我们可以在lua环境中直接运行。
这是lua => C/C++的经典写法:
通过C/C++语言编写的外部库的方法来扩展lua的功能,从而让lua 代码可以调用到C/C++的函数。

5. 对overloaded functions的支持
如果你有2个overloaded自由函数,需要luabind帮忙注册到lua vm环境中,那么你需要显式地告诉luabind那个绑定函数对应的函数签名

<!-- lang: cpp -->
int f(const char*) 
void f(int)

luabind注册代码需要这样下:

<!-- lang: cpp -->  
module(L)
[
    def("f", (int(*)(const char*)) &f),
    def("f", (void(*)(int)) &f)
];

6. 对class成员函数包括构造函数的支持

<!-- lang: cpp -->
class testclass
{
public:
    testclass(const std::string& s): m_string(s) { }
    void print_string() { std::cout << m_string << "\n"; }

private:
    std::string m_string;
};

module(L) [ class_<testclass>("testclass") .def(constructor<const std::string&>()) // 这是构造函数的注册 .def("print_string", &testclass::print_string) // 这是成员函数的注册 ];

借用LUA的语言糖: a::dotask(...) <==> a.dotask(a, ...)
luabind支持将类成员函数以自由函数的方式进行注册.

如果类成员函数有重载的情况,你仍然需要向重载的自由函数一样,描述函数的签名

<!-- lang: cpp -->
struct A
{
    void f(int a) { std::cout << a << "\n"; }
    void f(int a, int b) { std::cout << a << ", " << b << "\n"; }
};
module(L)
[
   class_<A>("A")
        .def(constructor<>())
        .def("plus", &plus)
        .def("f", (void (A::*)(int))&A::f)
        .def("f", (void (A::*)(int, int))&A::f)
];

需要注意的是,注册到LUA VM中的函数,可以避开C++的访问限制,譬如说你可以注册一个non-public域的成员函数.

7. 数据成员注册
对于public数据成员,如果你想让它public给LUA vm,那么,你可以这样做:

<!-- lang: cpp -->
struct A
{
    int a;
};
module(L)
[
    class_<A>("A")
         .def_readwrite("a", &A::a) // &A::a 是a在class A中偏移量
];

如果要求数据只能读,那么得这样注册

<!-- lang: cpp -->
module(L)
[
    class_<A>("A")
          .def_readonly("a", &A::a);
];

如果数据不是C++基本数据类型,那么gett function将会返回一个引用,以方便LUA code用点操作符来操作。
如果你已经对一个数据写了get/set函数,那么可以针对这个变量操作,注册这2个函数:

<!-- lang: cpp -->
struct A
{
    int a;
    int geta() const { return a; }
    void seta(int x) { a = x; }
};
module(L)
[
    class_<A>("A")
         .property("a", &A::geta, &A::seta)
];

对于那个注册的函数或变量的名字,没必要跟C++的一样,你可以取另外一个名字,譬如.property("data", &A::geta, &A::seta)
需要说明的是geta函数最好是const函数。如果不是好像也没有问题,但是官方文档说会存在问题。

8. enum类型数据的注册
如果你在一个类中定义了一些enum类型,你也可以把它们注册到LUA中。

<!-- lang: cpp -->
struct A
{
    enum {
        enum1 = 3, enum2 = 4, enum3 = 20
    };
};
module(L)
[
    class_<A>("A")
         .enum_("constants")
        [
               value("enum1", 3)
               value("enum2"), 4)
               value("enum3"), 6)
          ]
]

这样的话,在LUA中可以这样操作那3个enum类型数据

>= A.enum1  
3  
>= A.enum2  
4  

通过实验发现,在注册的时候,可以将那3个enum对应的值改变之后注册到LUA中,譬如

<!-- lang: cpp -->
class_<A>("A")
    .enum_("constants")
    [
          value("enum1", 300)
          ...
     ]

这样A.enum1得到就是300,而不是C++定义的3。同时还发现,LUA中是可以修改A.enum1的值的。
不过我觉得LUA这样操作应该不是很好的方式。

9. 操作符的注册
在注册类的操作符函数之前,需要include头文件<luabind/operator.hpp>

<!-- lang: cpp -->
struct A
{
    A& operator +(int) { ... }
}
module(L)
[
     class_<A>("A")
          .def(self + int())
];
  • self是luabind命名空间中的全局变量,代表类本身;
  • + 是operator +,对应LUA中的+操作
  • int()是int类型变量的实例化

如果其它自定义类的参数不是很容易实例化,需要借用other<>模板类。譬如: other<const std::string&>()
如果操作符是const类型的,那么你需要用const_self变量。

如果是A& operator(int)操作符,你可以这样注册.def(self(int())) 但是如果没有任何参数的operator(),该如何进行注册呢?

借用LUA print时隐式调用的__tostring,我们可以将一个类注册luabind::tostring函数,从而直接print这个类。
要做的事情值需要提供operator <<即可。

<!-- lang: cpp -->
friend std::ostream& operator <<(std::ostream&, A&);
module(L)
[
     class_<A>("A")
          .def(tostring(self))
];

10. 静态数据的注册
静态数据的注册包括静态数据成员和静态函数。对于静态函数的注册,需要借助一个特别的语法.scope.

<!-- lang: cpp -->
class A {
public:
     static int m_sa;
     static void print() { ... }
};
module(L)
[
     class_<A>("A")
          .def(constructor<>())
          .scope
          [
               // def_readwrite("a", &A::m_sa), // ERROR: cannot compile
               def("print", &A::print)
          ]
];

上面代码中,我将静态数据的注册划掉了,是因为当前luabind不支持(编译错误)。
有2种解决方案:

  1. 针对每一个静态数据,用静态函数进行封装;似乎有点麻烦?!

  2. 借用一篇博客文章的方法(http://www.codeproject.com/Articles/22638/Using-Lua-and-luabind-with-Ogre-3d)

    // Some helpful macros for defining constants (sort of) in Lua. Similar to this code: // object g = globals(L); // object table = g["class"]; // table["constant"] = class::constant; #define LUA_CONST_START( class ) { object g = globals(L); object table = g[#class]; #define LUA_CONST( class, name ) table[#name] = class::name #define LUA_CONST_END }

借用luabind提供的global和object组件。因为类的静态变量注册到LUA是类表的一个域,直接在全局表中对于的类表中,添加一个域。
样例如下:

LUA_CONST_START( Vector3 ) 
            LUA_CONST( Vector3, ZERO);
            LUA_CONST( Vector3, UNIT_X);
            LUA_CONST( Vector3, UNIT_Y);
            LUA_CONST( Vector3, UNIT_Z);
            LUA_CONST( Vector3, NEGATIVE_UNIT_X);
            LUA_CONST( Vector3, NEGATIVE_UNIT_Y);
            LUA_CONST( Vector3, NEGATIVE_UNIT_Z);
            LUA_CONST( Vector3, UNIT_SCALE);
   LUA_CONST_END;

如果你的类静态变量可写,那么你自己可以定义一套宏,叫做LUA_STATIC_BEGIN/LUA_STATIC_ENTRY/LUA_STATIC_END
从上面的代码中看出,似乎需要先要注册类到LUA中,才能注册这样的静态变量。

在lua环境中调用用如下的方式: (用dot来操作类表,而不是colon,那时用来操作对象的)。

>= A.a
>A.a = 200
>A.print()

转载于:https://my.oschina.net/zeroli/blog/192846

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值