您的位置:首页 > 编程语言 > Lua

利用luabind将Lua嵌入到C++项目中(二)

2009-07-26 12:18 302 查看
第二篇——语言差异及解决方案(初级篇)
By : HengStar(欣恒)

原文链接:http://blog.csdn.net/gongxinheng/archive/2009/07/26/4381217.aspx

通过上一篇的介绍,我们已经了解到了luabind的基本架设方法了,本篇将会用我自己在实践中的经验,通过举例来说明一些实际开发中可能会遇到的语言差异上带来的一些问题及个人尝试过的解决方案。

一. 默认参数 VS 参数个数严格匹配
众所周知,C++中的默认参数能给我们带来很大程度上的便利,观察以下代码:
#define FOOD_DOGFOOD 0
#define FOOD_RICE 1
bool feedDog( const char* dogName, int foodType = FOOD_DOGFOOD );

PS:这个函数声明只是突发地想到的,没什么特别的含义(因为我自己也养了条可爱的小狗^^),假设我们养了很多狗,也可能是开了一个宠物培训基地^^我们给狗喂食的时候通过dogName来确定给哪只狗喂,喂的食物类型foodType有默认参数FOOD_DOGFOOD(因为我通常只给它吃狗粮),然后我要调用此函数的时候经常会这样写feedDog( “豆豆” );使用缺省食物类型参数,由于习惯问题,我通过luabind的def("feedDog", &feedDog)将该函数注册到Lua中使用仍然用feedDog( “豆豆” )调用,结果在执行Lua脚本时候出现错误:”No matching overload found, candidates : bool feedDog(char const*, int)” 也就是该调用没有找到匹配的重载函数,但是有候选的两个参数的feedDog函数,好了,到这里大家应该看出来是怎么回事了吧,暂且不说在C++中定义的宏FOOD_DOGFOOD不能在Lua中使用,即使你用数字0也无济于事,因为luabind中做了严格的参数个数匹配判定,注册的函数有几个参数(包括默认参数),在lua脚本中调用时就必须写几个参数。
解决方案:必须在Lua中调用时写上所有参数(包括默认参数)
feedDog( “豆豆”,0 )

当然你也可以在Lua中自己定义伪枚举(我自己命名的^^)
FOOD_DOGFOOD = 0
feedDog( “豆豆”, FOOD_DOGFOOD )


谨记:别指望C++语言带来的所有便利都能同样在Lua中使用。

二. 字符串指针 VS nil类型好了,接下来我们要开始训练狗狗们了,首先我需要叫来我要训练的狗狗,我们有以下函数,通过名字来呼唤狗狗
bool callDog( const char* dogName )
{
if( dogName == NULL ) return callAll();
else  return call(dogName);
}

也许我还希望可以一次性叫来所有的狗,所以我将该函数实现为传递NULL作为参数的时候是表示叫来所有的狗,大家知道,在Lua中是用nil指代空值,但是Lua中的nil和C++中NULL还是有一些差异的,C++中的NULL通常被定义为数字0,而在Lua中nil是一个独立的类型,nil ~= 0,但是我们还是怀着侥幸的心理期盼它能转换为C++中的NULL值,所以我们尝试这样在Lua中调用:
callDog( nil )

我们失望的从输出结果中看到了” No matching overload found, candidates : bool callDog(char const*)”
Oh!shit!(这是我当时看到这个结果的第一反应-.-#),看来结果并不是我们预期那般完美,Lua中将nil试为一种类型,在luabind中会做严格的参数类型判定,nil不是一个string,而且Lua中并不存在指针类型,所以用指针的方式去操作变量必定导致了失败的结果(但并不是所有的情况都是这样,字符指针是一个特殊的类型,稍后会提到一些允许的转换情况)。是不是有人会这样想:“好吧,既然你说在C++中NULL通常都定义为0,那我这样调用总可以了吧”。
callDog( 0 )

恩…看来我们想到一块儿了,但执行后依然从错误输出中看到了同样的文字…在此强调,luabind会更多的把字符串用string的特征看待(如std::string)。
解决方案:在使用有const char*参数的注册函数中判断该参数是否为NULL的同时还要判断是否是空串,即””,并且在Lua中调用该函数时,为NULL的参数改用空串””代替,改造后的C++代码如下所示:
bool callDog( const char* dogName )
{
// dogName[0] == ‘/0’可以用 strcmp( dogName, “” ) == 0代替
if( dogName == NULL || dogName[0] == ‘/0’ ) return callAll();
else return call(dogName);
}

在LUA中的调用:
callDog( "" )


谨记:不要在Lua中传递nil或0给字符串参数,在给Lua注册使用的函数接口中字符串参数尽可能的用std::string代替,如果非要用const char*,记得把空串和NULL判断平级

三. C++对象指针VS nil类型前面提到:字符指针是一个特殊的类型,可以从和C++对象指针的差异上证明这一点。
狗狗的主人要来接训练完成的狗狗回家咯~每个狗狗脖子上都挂着主人的标志牌,姑且把它当做是一个主人的指针吧,我们有如下代表主人的类型,已经注册到Lua中
class Person
{
public:
Person(const string& n) : name(n){}
string name;
};

在Lua脚本中有如下表示狗狗和主人关系的table
HengStar= Person("HengStar") --某主人
DaQiang= Person("大强")  --某主人
dogs={ "贝贝", "豆豆", "强强" } --狗狗序号表
dog_Master = { nil, HengStar, DaQiang } --狗狗序号对应的主人

管理员要帮狗狗的主人找到他的狗狗,所以我们又注册了一个用于判定狗狗-主人匹配的函数
bool checkMaster( Person* realMaster, Person* dogMaster )
{
assert( realMaster != NULL ); // 这是不允许的
if( dogMaster == NULL ) // 注意这里
{
cout << “某狗狗没有主人!” << endl;
return false;
}
return realMaster->name == dogMaster->name; // 假定姓名没有重复
}

好了,准备工作完毕了,下面我们开始狗狗大搜查吧,在Lua脚本中执行以下代码
for i = 1, #dog_Master do
if( checkMaster(HengStar,dog_Master[i]) ) then
print(dogs[i] .. "找到主人了!")
break
end
end
输出结果:某狗狗没有主人
豆豆找到主人了

至此,示例完毕,细心的读者一定观察到了我想说明的结论了~没错,那就是有C++注册类对象的指针作为参数的注册函数,在Lua中调用时允许从nil转换到C++中的NULL,这正是和字符指针不同的地方,也是luabind的强大之处。
以上示例代码中for i = 1, #dog_Master会遍历dog_Master中所有元素,并把对应的值传给checkManager函数的第二个参数,而dog_Master第一个元素的值就是nil,在luabind中转化为NULL并传递给checkMaster,所以输出了某狗狗没有主人。

PS:此代码示例的展示方式可能有点牵强,不过能说明我要讲述的问题也就达到目的了。

谨记:在luabind中会判定Lua中传进来的参数类型和C++注册函数的参数类型匹配,如果参数是C++注册类对象指针,则会将nil转换为NULL使用

2009/7/26
内容来自用户分享和网络整理,不保证内容的准确性,如有侵权内容,可联系管理员处理 点击这里给我发消息
标签: