
定义C++类
首先,我们定义一个Student类,它拥有名字(字符串类型)和年龄(整型),并且提供一些getter和setter,最后还提供了一个print方法.这里有Student类的定义和实现:Student.h和Student.cpp
Student.h
//
// Student.h
// MyCppGame
//
// Created by guanghui on 8/11/14.
//
//
#ifndef __MyCppGame__Student__
#define __MyCppGame__Student__
#include <iostream>
#include<string>
using namespace std;
class Student
{
public:
Student();
~Student();
string getName();
void setName(string name);
int getAge();
void setAge(int age);
void print();
private:
string name;
int age;
};
#endif /* defined(__MyCppGame__Student__) */
Student.cpp
//
// Student.cpp
// MyCppGame
//
// Created by guanghui on 8/11/14.
//
//
#include "Student.h"
Student::Student()
:name("default")
{
}
Student::~Student()
{
}
string Student::getName()
{
return name;
}
void Student::setName(string name)
{
this->name = name;
}
int Student::getAge()
{
return age;
}
void Student::setAge(int age)
{
this->age = age;
}
void Student::print()
{
cout<<"My name is: "<<name<<", and my age is "<<age<<endl;
}
编写绑定代码
首先,让我们编写在Lua里面创建Student对象的方法:
int newStudent(lua_State* L)
{
lua_newuserdata(L, sizeof(Student*));
return 1;
}
接下来是getName,setName,setAge,getAge和print方法的定义:
int l_setName(lua_State* L)
{
Student * s = (Student * )lua_touserdata(L, 1);
luaL_argcheck(L, s != NULL, 1, "invalid user data");
luaL_checktype(L, -1, LUA_TSTRING);
std::string name = lua_tostring(L, -1);
s->setName(name);
return 0;
}
int l_setAge(lua_State* L)
{
Student * s = (Student * )lua_touserdata(L, 1);
luaL_argcheck(L, s != NULL, 1, "invalid user data");
luaL_checktype(L, -1, LUA_TNUMBER);
int age = lua_tonumber(L, -1);
s->setAge(age);
return 0;
}
int l_getName(lua_State* L)
{
Student * s = (Student * )lua_touserdata(L, 1);
luaL_argcheck(L, s != NULL, 1, "invalid user data");
lua_settop(L, 0);
lua_pushstring(L, s->getName().c_str());
return 1;
}
int l_getAge(lua_State* L)
{
Student * s = (Student * )lua_touserdata(L, 1);
luaL_argcheck(L, s != NULL, 1, "invalid user data");
lua_settop(L, 0);
lua_pushnumber(L, s->getAge());
return 1;
}
int l_print(lua_State* L)
{
Student * s = (Student * )lua_touserdata(L, 1);
luaL_argcheck(L, s != NULL, 1, "invalid user data");
s->print();
return 0;
}
从这里我们可以看到,userdata充当了C++类和Lua的一个桥梁,另外,我们在从Lua栈里面取出数据的时候,一定要记得检查数据类型是否合法。
注册C API到Lua里面
最后,我们需要把刚刚编写的这些函数注册到Lua虚拟机里面去。
static const struct luaL_Reg stuentlib_f [] = {
{"create", newStudent},
{"setName",l_setName},
{"setAge", l_setAge},
{"print", l_print},
{"getName",l_getName},
{"getAge", l_getAge},
{NULL, NULL}
};
int luaopen_student (lua_State *L) {
luaL_newlib(L, stuentlib_f);
return 1;
}
现在,我们把这个函数添加到之前的注册函数里面去:
static const luaL_Reg lualibs[] =
{
{"base", luaopen_base},
{"io", luaopen_io},
{"cc",luaopen_student},
{NULL, NULL}
};
const luaL_Reg *lib = lualibs;
for(; lib->func != NULL; lib++)
{
luaL_requiref(L, lib->name, lib->func, 1);
lua_settop(L, 0);
}
Lua访问C++类
现在,我们在Lua里面操作这个Student类。注意,我们绑定的每一个函数都需要一个student对象作为参数,这样使用有一点不太方便。
local s = cc.create() cc.setName(s,"zilongshanren") print(cc.getName(s)) cc.setAge(s,20) print(cc.getAge(s)) cc.print(s)
最后,输出的结果为:
zilongshanren 20 My name is: zilongshanren, and my age is 20
提供Lua面向对象操作API
现在我们已经可以在Lua里面创建C++类的对象了,但是,我们最好是希望可以用Lua里面的面向对象的方式来访问。
local s = cc.create()
s:setName("zilongshanren")
s:setAge(20)
s:print()
而我们知道s:setName(xx)就等价于s.setName(s,xx),此时我们只需要给s提供一个metatable,并且给这个metatable设置一个key为"__index",value等于它本身的metatable。最后,只需要把之前Student类的一些方法添加到这个metatable里面就可以了。
MetaTable
我们可以在Registry里面创建这个metatable,然后给它取个名字做为索引,注意,为了避免名字冲突,所以这个名字一定要是独一无二的。
//创建名字为tname的metatable并放在当前栈顶,同时把它与Registry的一个key为tname的项关联到一起 int luaL_newmetatable (lua_State *L, const char *tname); //从当前栈顶获取名字为tname的metatable void luaL_getmetatable (lua_State *L, const char *tname); //把当前栈index处的userdata取出来,同时检查此userdata是否包含名字为tname的metatable void *luaL_checkudata (lua_State *L, int index,const char *tname);
接下来,我们要利用这3个C API来为我们的student userdata关联一个metatable.
修改绑定代码
首先,我们需要创建一个新的metatable,并把setName/getName/getAge/setAge/print函数设置进去。
下面是一个新的函数列表,一会儿我们要把这些函数全部设置到metatable里面去。
static const struct luaL_Reg studentlib_m [] = {
{"setName",l_setName},
{"setAge", l_setAge},
{"print", l_print},
{"getName",l_getName},
{"getAge", l_getAge},
{NULL, NULL}
};
接下来,我们创建一个metatable,并且设置metatable.__index = matatable.注意这个cc.Student的元表会被存放到Registry里面。
int luaopen_student (lua_State *L) {
luaL_newmetatable(L, "cc.Student");
lua_pushvalue(L, -1);
lua_setfield(L, -2, "__index");
luaL_setfuncs(L, studentlib_m, 0);
luaL_newlib(L, stuentlib_f);
return 1;
}
最后,我们记得在创建Student的时候把此元表与该userdata关联起来,代码如下:
int newStudent(lua_State * L)
{
lua_newuserdata(L, sizeof(Student * ));
luaL_getmetatable(L, "cc.Student");
lua_setmetatable(L, -2);
return 1;
}
另外,我们在从Lua栈里面取出Student对象的时候,使用的是下面的函数
Student *s = (Student * )luaL_checkudata(L,1,"cc.Student");
这个luaL_checkudata除了可以把index为1的栈上的元素转换为userdata外,还可以检测它是否包含“cc.Student”元表,这样代码更加健壮。
例如,我们之前的setName函数可以实现为:
int l_setName(lua_State * L)
{
Student * s = (Student * )luaL_checkudata(L,1,"cc.Student");
luaL_argcheck(L, s != NULL, 1, "invalid user data");
luaL_checktype(L, -1, LUA_TSTRING);
std::string name = lua_tostring(L, -1);
s->setName(name);
return 0;
}
这里有Student类的完整的新的绑定代码:
//
// CppClass.h
// MyCppGame
//
// Created by guanghui on 8/11/14.
//
//
#ifndef MyCppGame_CppClass_h
#define MyCppGame_CppClass_h
#include "lua.hpp"
#include"Student.h"
int newStudent(lua_State* L)
{
lua_newuserdata(L, sizeof(Student*));
luaL_getmetatable(L, "cc.Student");
lua_setmetatable(L, -2);
return 1;
}
int l_setName(lua_State* L)
{
int n = lua_gettop(L);
cout<<"l_setName " << n <<endl;
Student *s = (Student*)luaL_checkudata(L,1,"cc.Student");
luaL_argcheck(L, s != NULL, 1, "invalid user data");
luaL_checktype(L, -1, LUA_TSTRING);
std::string name = lua_tostring(L, -1);
s->setName(name);
return 0;
}
int l_setAge(lua_State* L)
{
Student *s = (Student*)luaL_checkudata(L,1,"cc.Student");
luaL_argcheck(L, s != NULL, 1, "invalid user data");
luaL_checktype(L, -1, LUA_TNUMBER);
int age = lua_tonumber(L, -1);
s->setAge(age);
return 0;
}
int l_getName(lua_State* L)
{
Student *s = (Student*)luaL_checkudata(L,1,"cc.Student");
luaL_argcheck(L, s != NULL, 1, "invalid user data");
lua_settop(L, 0);
lua_pushstring(L, s->getName().c_str());
return 1;
}
int l_getAge(lua_State* L)
{
Student *s = (Student*)luaL_checkudata(L,1,"cc.Student");
luaL_argcheck(L, s != NULL, 1, "invalid user data");
lua_settop(L, 0);
lua_pushnumber(L, s->getAge());
return 1;
}
int l_print(lua_State* L)
{
Student *s = (Student*)luaL_checkudata(L,1,"cc.Student");
luaL_argcheck(L, s != NULL, 1, "invalid user data");
s->print();
return 0;
}
int student2string (lua_State *L) {
Student *s = (Student*)luaL_checkudata(L, 1, "cc.Student");
lua_pushfstring(L, "name = %s, age = %d",s->getName().c_str(), s->getAge());
return 1;
}
static const struct luaL_Reg stuentlib_f [] = {
{"create", newStudent},
{NULL, NULL}
};
static const struct luaL_Reg studentlib_m [] = {
{"__tostring",student2string},
{"setName",l_setName},
{"setAge", l_setAge},
{"print", l_print},
{"getName",l_getName},
{"getAge", l_getAge},
{NULL, NULL}
};
int luaopen_student (lua_State *L) {
luaL_newmetatable(L, "cc.Student");
/* metatable.__index = metatable */
lua_pushvalue(L, -1); /* duplicates the metatable */
lua_setfield(L, -2, "__index");
luaL_setfuncs(L, studentlib_m, 0);
luaL_newlib(L, stuentlib_f);
return 1;
}
#endif
Lua访问C++类
现在,我们可以用Lua里面的面向对象方法来访问C++对象啦。
local s = cc.create()
s:setName("zilongshanren")
print(s:getName())
s:setAge(20)
print(s:getAge())
s:print()
这里输出的结果为:
zilongshanren 20 My name is: zilongshanren, and my age is 20
总结
本文主要介绍如何使用UserData来绑定C/C++自定义类型到Lua,同时通过引入MetaTable,让我们可以在Lua里面采用更加简洁的面向对象写法来访问导出来的类。下一篇文章,我们将介绍Cococs2D-X里面的tolua++及其基本使用方法。


相关文章本文地址:https://blog.sxx1314.com/lua/394.html
版权声明:若无注明,本文皆为“unix 软硬件 技术宅 ”原创,转载请保留文章出处。百度已收录















