Quick Introduction to LuaBind

zz:http://www.nuclex.org/articles/cxx/1-quick-introduction-to-luabind

 

Written by Markus Ewald   

Thursday, August 24 2006 18:51

The Lua LogoLua is a great scripting language for games and regular applications alike because it is fast, simple and well suited for embedding (embedding means putting the scripting language into your application instead of making your application an add-on module to the scripting language). Lua can easily be compiled and setting up a lua environment in your code is no big task either, because all you have to do is call lua_open() and later lua_close().

The Lua LogoHowever, when the time comes to create bindings for your functions and classes so scripts can call into your code, things quickly become harder. You're forced to learn the semantics of the lua stack, what effects various functions of the lua API have on it, how lua treats user data and finally, you'll have to repeat the tedious work of writing lua wrappers for your functions and classes again and again. LuaBind solves this problem nicely by automatically generating the wrapper code for any function or class you hand to LuaBind. Contrary to other lua wrapper generators, LuaBind works entirely in C++ and does not require any kind of special preprocessor.

Once LuaBind is set up and working, using Lua becomes as fun and easy as you always wanted it to be. This article will help you to reach that point and then shows you how to use it to call C/C++ functions from Lua, to call Lua functions from C++ and even to export entire C++ classes.

Getting your Hands on LuaBind Binaries

To use LuaBind, you have two options: Add all LuaBind sources to your project, or compile LuaBind into a static library which you can then link to your project. This article will follow the latter approach, because it is cleaner and you can more easily upgrade your projects to new versions of LuaBind.

To compile LuaBind, you would normally download the sources of Lua, Boost and LuaBind, set up and compile each library as well as possibly modify LuaBind due to changes in the Boost library which have not been reflected in LuaBind yet. Because this whole process can be somewhat troublesome, you can also download a single package containing precompiled binaries here:

Lua 5.1.4LuaBind 0.8.1 and a minimal subset of Boost 1.39.0 in a preconfigured example project for Microsoft Visual Studio 2008 SP1 or Microsoft Visual C++ 2008 Express SP1 that compiles out-of-the-box.
LuaBind 0.8.1 Binaries and Demo (use 7-Zip or WinRAR to extract)

Calling Lua Functions from C++

It doesn't get any easier than this. To call a function in a lua script from C++, you can use LuaBind's call_function()template function like this:

int main() {
// Create a new lua state
lua_State *myLuaState = lua_open();
 
// Connect LuaBind to this lua state
luabind::open(myLuaState);
 
// Define a lua function that we can call
luaL_dostring(
myLuaState,
"function add(first, second)/n"
"  return first + second/n"
"end/n"
);
 
cout << "Result: "
<< luabind::call_function<int>(myLuaState, "add", 2, 3)
<< endl;
 
lua_close(myLuaState);
}

Let's see, first we use luabind::open() to connect the lua state to LuaBind. This has to be done for all lua states where we want to use LuaBind. Next, we execute some lua code so the lua state contains a global function namedadd(), which is then called in the cout line using luabind::call_function(). This template function requires the type of the return value to be passed in as a template parameter. Its first argument is the lua state that contains the lua function we'd like to call. The next argument denotes the name of that function and after that we can list any arguments that we want to pass to the lua function. The types of these additional arguments can be identified without our help because of the way templates work in C++.

Making C++ functions callable from Lua

The next example is a bit more complicated. We want to call a C++ function from a lua script. Instead of wasting our time with lua stack manipulation and data type conversion, we can simply do this:

void print_hello(int number) {
cout << "hello world " << number << endl;
}
 
int main() {
// Create a new lua state
lua_State *myLuaState = lua_open();
 
// Connect LuaBind to this lua state
luabind::open(myLuaState);
 
// Add our function to the state's global scope
luabind::module(myLuaState) [
luabind::def("print_hello", print_hello)
];
 
// Now call our function in a lua script
luaL_dostring(
myLuaState,
"print_hello(123)/n"
);
 
lua_close(myLuaState);
}

As before, we need to connect LuaBind to the lua state by using luabind::open(). The luabind::module() part is required to tell LuaBind what scope we want to add our function to. Finally, using luabind::def() we can export a C/C++ function to the lua state. The first argument is the name as it will be seen in lua scripts, the second is the C/C++ function we want to export.

If you need to export multiple functions, you should seperate them from each other using commas (,). You're not allowed to just end each definition with a semicolon because they're still enclosed by the luabind::module statement.

Exporting Classes to Lua

Now onto the fun part. LuaBind allows you to export entire C++ classes to lua, including constructors with arguments, overloaded functions and even operators. It is just as well possible to use a lua class from within C++ through LuaBind. But let's start with something simple:

class NumberPrinter {
public:
NumberPrinter(int number) :
m_number(number) {}
 
void print() {
cout < m_number < endl;
}
 
private:
int m_number;
};
 
int main() {
// Create a new lua state
lua_State *myLuaState = lua_open();
 
// Connect LuaBind to this lua state
luabind::open(myLuaState);
 
// Export our class with LuaBind
luabind::module(myLuaState) [
luabind::class_<NumberPrinter>("NumberPrinter")
.def(luabind::constructor<int>())
.def("print", &NumberPrinter::print)
];
 
// Now use this class in a lua script
luaL_dostring(
myLuaState,
"Print2000 = NumberPrinter(2000)/n"
"Print2000:print()/n"
);
 
lua_close(myLuaState);
}

This might look difficult, but once you've got the idea, exporting other classes will be a no-brainer. The first thing to notice is luabind::class_, which is a template struct used to export classes to lua. The template argument we're specifying is, of course, the class that we wish to export. Its normal constructor argument defines the name under which the class will be known in lua scripts. You're actually creating a temporary, unnamed instance of aluabind::class_<NumberPrinter> on which methods can be called.

The methods we're calling can be seen in the next two lines where the class constructor is exported (using a special auxiliary structure luabind::constructor) as well as the print method used to display the number. Notice that unlike exported functions, class methods are not seperated by commas, but as before mustn't be terminated by a semicolon.

Exporting class attributes and properties

LuaBind can also export a class' attributes (variables defined in a class) or even simulate a variable to lua by using the getter and setter methods in a class. To make it a bit more interesting, we're going to export two C++ template structures now. The concept of templates does not exist in lua, so we can only register an actual class or in other words a template instanced to a specific type.

template<typename T>
struct Point {
Point(T X, T Y) :
X(X), Y(Y) {}
 
T X, Y;
};
 
template<typename T>
struct Box {
Box(Point<T> UpperLeft, Point<T> LowerRight) :
UpperLeft(UpperLeft), LowerRight(LowerRight) {}
 
Point<T> UpperLeft, LowerRight;
};
 
int main() {
// Create a new lua state
lua_State *myLuaState = lua_open();
 
// Connect LuaBind to this lua state
luabind::open(myLuaState);
 
// Export our classes with LuaBind
luabind::module(myLuaState) [
luabind::class_<Point<float> >("Point")
.def(luabind::constructor<floatfloat>())
.def_readwrite("X", &Point<float>::X)
.def_readwrite("Y", &Point<float>::Y),
 
luabind::class_<Box<float> >("Box")
.def(luabind::constructor<Point<float>, Point<float> >())
.def_readwrite("UpperLeft", &Box<float>::UpperLeft)
.def_readwrite("LowerRight", &Box<float>::LowerRight)
];
 
// Now use this class in a lua script
luaL_dostring(
myLuaState,
"MyBox = Box(Point(10, 20), Point(30, 40))/n"
"MyBox.UpperLeft.X = MyBox.LowerRight.Y/n"
);
 
lua_close(myLuaState);
}

As you can see, the Lua script works perfectly with both classes, despite the fact that one is used within the other. The def_readwrite() method of luabind::class_ directly exports the point and box structure's attributes and makes them available to the script. If all those template brackets are irritating you, there's of course always the possibility to use a typedef instead!

You could now directly pass a Box or Point to a lua function you call through luabind::call_function() and it would work just the way you expect it to work.

More!

If you ever made a typo while working your way through this tutorial, you might have found yourself in front of an ugly run-time error message. This is due to the fact that luabind transforms lua errors into various exceptions, all derived from the common std::exception. If you wish to see a human-readable error message, a try..catch block has to be set up to catch std::exceptions!

We will now demonstrate this as well as explore some other useful parts of LuaBind:

struct ResourceManager {
ResourceManager() :
m_ResourceCount(0) {}
 
void loadResource(const string &sFilename) {
++m_ResourceCount;
}
size_t getResourceCount() const {
return m_ResourceCount;
}
 
size_t m_ResourceCount;
};
 
int main() {
// Create a new lua state
lua_State *myLuaState = lua_open();
 
// Connect LuaBind to this lua state
luabind::open(myLuaState);
 
// Export our class with LuaBind
luabind::module(myLuaState) [
luabind::class_<ResourceManager>("ResourceManager")
.def("loadResource", &ResourceManager::loadResource)
.property("ResourceCount", &ResourceManager::getResourceCount)
];
 
try {
ResourceManager MyResourceManager;
 
// Assign MyResourceManager to a global in lua
luabind::globals(myLuaState)["MyResourceManager"] = &MyResourceManager;
 
// Execute a script to load some resources
luaL_dostring(
myLuaState,
"MyResourceManager:loadResource(/"abc.res/")/n"
"MyResourceManager:loadResource(/"xyz.res/")/n"
"/n"
"ResourceCount = MyResourceManager.ResourceCount/n"
);
 
// Read a global from the lua script
size_t ResourceCount = luabind::object_cast<size_t>(
luabind::globals(myLuaState)["ResourceCount"]
);
cout << ResourceCount << endl;
}
catch(const std::exception &TheError) {
cerr << TheError.what() << endl;
}
 
lua_close(myLuaState);
}

The .property() method of luabind::class_ emulates an attribute in the lua class through getter and (optionally) setter methods in C++. It is not only shorter, but also far more natural to write Items = X.ItemCount than to writeItems = X:getItemCount(). The other feature this example introduces is luabind::globals() which returns the lua globals table (where all of a script's functions and global variables are stories in) wrapped up in a luabind::object. In this example, we're accessing a global named ResourceCount, which we need to cast to an integer usingluabind::object_cast<>().

This should be enough to get you up to speed with LuaBind. Experiment with the examples and then go read the LuaBind docs, they're not even harder to read than this article ;)

There's a lot more to LuaBind you can discover, like operator overloading, deriving classes from each other, read only properties, and more.

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
Luabind是一个开源的C++库,用于绑定Lua脚本和C++代码。它使得在C++代码中调用Lua脚本变得更加容易。在Linux平台上安装Luabind库需要进行以下步骤: 1. 下载Luabind库 可以从Luabind的官方网站(http://luabind.sourceforge.net/)下载最新版本的Luabind库。下载后将文件解压缩到一个目录中。 2. 安装Lua库 在Linux系统上安装Luabind库之前,需要确保已经安装了Lua库。如果还没有安装Lua库,可以通过以下命令来安装: sudo apt-get install lua5.1 3. 安装boost库 Luabind库依赖于boost库,因此需要先安装boost库。可以通过以下命令来安装: sudo apt-get install libboost-all-dev 4. 编译和安装Luabind库 进入Luabind库的解压缩目录,然后执行以下命令: ./configure make sudo make install 执行完成后,Luabind库就已经安装完成了。 5. 测试Luabind库 可以通过以下步骤来测试Luabind库是否安装成功: 创建一个名为test.cpp的文件,包含以下代码: #include <iostream> #include <luabind/luabind.hpp> void hello() { std::cout << "Hello, world!" << std::endl; } int main() { lua_State* L = luaL_newstate(); luaL_openlibs(L); luabind::open(L); luabind::module(L) [ luabind::def("hello", &hello) ]; luaL_dostring(L, "hello()"); lua_close(L); return 0; } 编译test.cpp文件: g++ -o test test.cpp -lluabind -llua -lboost_system 运行test文件,如果输出“Hello, world!”则表示Luabind库已经安装成功。 注:以上命令可能需要root权限,如果遇到权限问题,可以在命令前加上sudo。

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

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

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值