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

第三篇——语言差异及解决方案(进阶篇)

By : HengStar(欣恒)

 

通过前面两篇我们对luabind做了进一步的了解,接下来让我们提升点难度吧:)下面讲的例子可能有点违背常理,但我确实在项目中遇到了,所以在此提出一下,也能更好的了解luabind的机制。

 

一.   欺骗编译器?——狗狗们的特技
好了,女士们先生们,激动人心的时刻到来了,现在是狗狗的开心演出时间,可爱的狗狗们要表演各自的绝活了~让我们拭目以待吧
先看看下面的代码:

#include <iostream>

using namespace std;

 

class Dog // 狗狗基类

{};

 

class Chihuahua : public Dog // 吉娃娃

{

public:

    bool wallow() // 打滚

    {

        cout << "I'm wallowing!" << endl;

        return true;

    }

};

 

class Gundogs: public Dog //枪猎犬

{

public:

    bool hunt() // 打猎

    {

        cout << "I'm hunting!" << endl;

        return true;

    }

};

 

class Poodle: public Dog // 贵妇

{

public:

    bool highjump() // 高跳

    {

        cout << "I'm jumping!" << endl;

        return true;

    }

};

 

typedef bool(Dog::*dogact)(); // 狗狗特技的成员函数指针

 

bool Dogshow_impl( Dog* dog, dogact act )

{

    return (dog->*act)();

}

 

int main()

{

    Dog* myDog = new Poodle;

    Dogshow_impl ( myDog, &Poodle:: highjump ); // 贵妇狗狗开始表演吧

 

    return 0;

}

 

好了,以上是全部代码,大家先猜猜输出结果吧
很不幸的,驯兽师(编译器)开始抱怨了,“我希望看到的是狗(Dog)的表演,而不是贵妇狗(Poodle)的表演”,可能这个驯兽师是个傻子,但确实设计是这样的,他只是负责执行的一方而已,Dogshow_impl函数需要接受的第二个参数是一个Dog基类的成员函数指针,而你传递一个子类Poodle的成员函数指针,这是不合法的(如果不能理解,请参阅相关C++书籍,如《C++ Primer》),因为基类根本无法了解子类里面会出现哪些类似的(函数原型相同的)成员函数,好吧,既然如此,我想个办法蒙混过关吧~我们来给表演者盖上一层面纱,让它伪装(强制转换)成所谓的狗(基类)

#define dogshow(dogpt, act) Dogshow_impl( dogpt, (dogact)act )

然后我把
Dogshow_impl ( myDog, &Poodle:: highjump ); // 贵妇狗狗开始表演吧

改成
dogshow ( myDog, &Poodle:: highjump ); // 贵妇狗狗开始表演吧

“很好”,驯兽师赞道,并让myDog执行了它的highjump方法,所以输出了:

I'm jumping!
OK,我们的目的达到了(不过我不得不承认这种设计模式是非常失败的),我现在可以任意的传递任何狗狗的指针和它的任意签名对应的成员函数作为参数了,但这始终是一种欺骗行为,驯兽师的眼睛逃过了,但它不一定逃得过评委(luabind)的审核。

 

首先,宏定义是不被lua所支持的,所以我们不能用dogshow来注册了,眼下也只能通过直接注册Dogshow_impl函数在lua中使用了,让我们注册所有类和方法试试吧。

我们怀着侥幸的心理在lua中这样调用

myDog = Poodle()

Dogshow_impl( myDog, myDog.highjump )

很遗憾,执行到上面的代码时失败了,原因如下:
首先,luabind中有严格的类型判断,这个跟最初的C++代码产生编译错误的原理一样,就不再重复了,但本质问题不在此,myDog.highjumplua中是一个function类型,并非需要的成员函数指针类型,所以在参数类型判断上就失败了,怎么办?总不能因为这个原因就不用了吧,开动脑筋想想办法吧

解决方案:

提供一种我自己的解决方案:

首先如果你非得照这种模式设计的话,我只能在基类中把所有子类需要用到lua中的成员函数声明为虚函数,如此一来,Dog类中就多了以下方法

class Dog // 狗狗基类

{

public:

   virtual bool hunt();

   virtual bool highjump ();

   …….

};

显然这样并不是很好的方法,建议在基类中把这些方法抽象出来,比如改名叫perform,如此一来基类只需要一个 virtual bool perform()的声明(当然这个基类的虚函数也得注册),子类可以自己实现它,最重要的是要能在lua中正确的调用,那么我们还需要把Dogshow_impl函数封装一层,第二个成员函数指针的参数可以用const luabind::object&取代,luabind::object可以接收一个lua中的function类型,并且可以用call_function直接调用它并用参数一(基类狗狗指针)作为call_function的参数(具体参照luabind文档),接下来我们还需要在lua中声明一个全局基类对象,专门用于“选择”调用的虚函数,看看改造后的代码吧~

首先在C++中封装一层

bool Lua_DogShow( Dog* dog, const luabind::object& func )

{

        using namespace luabind;

        call_function<void>( func, dog ); // 这个funclua函数,dog是调用者

        return true;

}

将此函数注册到Lua

然后在lua中写以下代码

DOG = Dog() // 全局的,用来“选择”调用的虚函数用的

function myFunc()

      local myDog = Poodle()

      --注意这里的DOG.highjump相当于成员函数指针,这个highjump是通过基类注册的

Lua_DogShow( myDog, DOG.highjump )

End

C++中调用Lua中的myFunc函数就会输出:
    I'm jumping!

                                                                          2009/8/14

你可能感兴趣的:(C++,function,object,Class,lua,编译器)