C++ 技术中心

   :: 首页 :: 联系 ::  :: 管理
  160 Posts :: 0 Stories :: 87 Comments :: 0 Trackbacks

公告

郑重声明:本BLOG所发表的原创文章,作者保留一切权利。必须经过作者本人同意后方可转载,并注名作者(天空)和出处(CppBlog.com)。作者Email:coder@luckcoder.com

留言簿(27)

搜索

  •  

最新随笔

最新评论

评论排行榜


1.近来想研究tolua++,正好忙里抽闲,看些文章,并做了些总结。

一.环境设置
2.下载lua5.1  并安装
3.下载tolua++/Files/API/tolua-1.0.93.tar.bz2.zip  (下载完成后,把.zip去掉)
4
.新建工程lua++(生成静态库),将tolua++解压后的tolua++-1.0.93\src\lib目录下的6个.c和.h文件。加
   到该工程中。并设置附加包含目录:
C:\Program Files (x86)\Lua\5.1\include
    并编译成功后,生成lua++.lib
5.打开tolua-1.0.93\tolua++-1.0.93\win32\vc7目录下的toluapp.sln,
  设置附加包含目录:
C:\Program Files (x86)\Lua\5.1\include 
  附加库目录:
C:\Program Files (x86)\Lua\5.1\lib
  加入附加依赖项:lua5.1.lib
  最终生成到
tolua-1.0.93\tolua++-1.0.93\bin\tolua++.exe

二.测试一(变量访问)
1.新建工程NewLua
2.Tarray.h如下:
#ifndef _TARRAY_H__
#define _TARRAY_H__

extern int g_Arr[10];


#endif/*_TARRAY_H__*/

3.编写pkg文件,内容如下:
$#include "tarray.h"
extern int g_Arr[10]@Arr;

4.输入命令:
>tolua++.exe -n tarray -o tarray.cpp tarray.pkg
 -n tarray选项指定包的名字为tarray。如果不用-n显式指定,tolua++.exe会生成一个和pkg文件名一样的包名,同时生成tolua_**_open(lua_State*)入口函数。

5.将生成的tarray.cpp加入到该工程中。并且加入lua5.1.lib,
lua++.lib
  并附加包含lua的头文件目录

6.在main文件如下:
#include "stdafx.h"
#include "lua.hpp"  
#include "tarray.h"  

int tolua_tarray_open (lua_State* tolua_S);  
int g_Arr[10]={0};  

int _tmain(int argc, _TCHAR* argv[])
{
    lua_State * L = lua_open();  
    int i=0;  
    for(i=0; i<10; i++) g_Arr[i] = i;  

    luaopen_base(L);  
    tolua_tarray_open(L);  
    luaL_dofile(L, "C:/Users/Administrator/Desktop/test/NewLua/NewLua/array.lua");  

    printf("now in c++, re-show Arr:");  
    for(i=0; i<10; i++) printf("%d ", g_Arr[i]);  
    printf(" ");  

    lua_close(L);  
    return 0;
}

7.新建array.lua文件如下:
print("now in lua script!   try to print 'Arr' by name:")
print(Arr)
print("now print 'Arr':")
--print contents of Arr
for i=0,9 do print(Arr[i]) end


--change contents of Arr
print("now change the Arr.")
for i=0,9 do Arr[i] = i*2 end

8.打印结果如下:
now in lua script!   try to print 'Arr' by name:
table: 008168E0
now print 'Arr':
0
1
2
3
4
5
6
7
8
9
now change the Arr.
now in c++, re-show Arr:0 2 4 6 8 10 12 14 16 18

二. 导出类
1.同样,新建
tclass.h文件,内容如下:
#ifndef _TESTCLASS_H  
#define _TESTCLASS_H  
#include "stdafx.h"
#include <string.h>  

class CNumber {   
    //tolua_export 
public:   
    //tolua_begin      
    CNumber():m_nNum(0){ }      
    CNumber(int num):m_nNum(num){ }    
    ~CNumber() { }      
    void SetNumber(int num) { m_nNum = num; }     
    int GetNumber()     { return m_nNum; }     
    int Add(int num)  
    {             
        m_nNum += num;          
        return m_nNum;      
    }   
    
    //tolua_end  
protected:  

    int m_nNum;  
};   

class CMessage
{
public:
    CMessage()
    {
        strcpy(m_buff,"init message!");
    }
    ~CMessage()
    {

    }
    void SetMessage(char *msg)
    {
        strcpy(m_buff,msg);
    }

    char *GetMessage()
    {
        return m_buff;
    }

    void ShowMessage()
    {
        printf("%s\n",m_buff);
    }
protected:
    char m_buff[256];
};

//tolua_export  //tolua_begin  class

#endif

2.pkg文件内容如下:

$#include "tclass.h"
$#include "tarray.h"
extern int g_Arr[10]@Arr;


class CNumber {
public:
    CNumber(); 
    CNumber(int num);
    ~CNumber();   
    void SetNumber(int num);    
    int GetNumber();    
    int Add(int num);
};

class CMessage
{
public:
    CMessage();
    ~CMessage();
    void SetMessage(char *msg);
    char *GetMessage();
    void ShowMessage();

};

3. 在导出类的时候,构造函数被映射到lua中的new,析构函数被映射为delete。接下来,用tolua++.exe生成用于完成“导出类到lua"功能的cpp文件。如下:
tolua++.exe -n tarray -o tarray.cpp tarray.pkg

4.main.cpp代码如下:
#include "stdafx.h"
#include "lua.hpp"  
#include "tarray.h"  

int tolua_tarray_open (lua_State* tolua_S);  
int g_Arr[10]={0};  

int _tmain(int argc, _TCHAR* argv[])
{
    lua_State * L = lua_open();  
    int i=0;  
    for(i=0; i<10; i++) g_Arr[i] = i;  

    luaopen_base(L);  
    tolua_tarray_open(L);  
    luaL_dofile(L, "C:/Users/Administrator/Desktop/test/NewLua/NewLua/array.lua");  
    
    
    printf("now in c++, re-show Arr:");  
    for(i=0; i<10; i++) printf("%d ", g_Arr[i]);  
    printf(" \n\n"); 

    
    luaL_dofile(L, "C:/Users/Administrator/Desktop/test/NewLua/NewLua/classtest.lua");  
    
    lua_close(L);  
    return 0;
}

4.新建
classtest.lua文件内容如下:
print("now in classtest.lua")


--new a CNumber Object
print("now allocate a CNumer object:")
num = CNumber:new()
num2=CNumber:new(222)
print("init,num:"..num:GetNumber())
print("init,num2:"..num2:GetNumber())



-- set number
print("now call SetNumber()")
num:SetNumber(6)
print("set number:"..num:GetNumber())

-- delete object
num:delete()
print("num is deleted,access GetNumber is invalid!")
print("invalidcall,result:"..num:GetNumber())

-- new a CMessage Object
msg=CMessage:new()
print("init msg:"..msg:GetMessage())

-- set message
print("msg:SetMessage('changed message')")
msg:SetMessage("changed message")
print("now msg:"..msg:GetMessage())
msg:ShowMessage()
msg:delete()

5.执行结果如下:
now in lua script!   try to print 'Arr' by name:
table: 00157D48
now print 'Arr':
0
1
2
3
4
5
6
7
8
9
now change the Arr.
now in classtest.lua
now in c++, re-show Arr:0 2 4 6 8 10 12 14 16 18

now in classtest.lua
now allocate a CNumer object:
init,num:0
init,num2:222
now call SetNumber()
set number:6
num is deleted,access GetNumber is invalid!
invalidcall,result:-17891602
init msg:init message!
msg:SetMessage('changed message')
now msg:changed message
changed message

三. 导出聚合类
1. 
聚合是最常见的构造新类的方式了,另一个是继承。tolua++支持单继承,后面会提到继承的例子。这里先看看怎么将利用了聚合的类导出到lua中。
   我的目的是想在Lua中使用C++类的实例,而不是在lua中生成C++类实例,所以我在利用tolua++向lua导出类时一般不导出构造函数,这样就无法
  在lua中生成类实例。     但是为了演示的方便,这个例子中用到的两个简单类CNumber和CMessage仍然导出了构造函数。    

   另外一个单件(singleton)CTestSystem的构造函数、拷贝构造函数、=操作符都被声明为protected,在向lua导出时只导出了 几个方法。你无法
在lua中生成它的实例,即便在C++中也不行,只能通过其静态成员函数GetSingleton()获取。

2. 新建classg.h如下:
#ifndef _CLASS_G_H__
#define _CLASS_G_H__

#include <string.h>

class CNumberEx
{
public:
    CNumberEx():m_nNum(0){}
    CNumberEx(int num):m_nNum(num){}
    ~CNumberEx(){}

    void SetNumber(int num)    {m_nNum = num;}
    int GetNumber(){return m_nNum;}
    int Add(int num){m_nNum += num;return m_nNum;}
protected:
    int m_nNum;
};

class CMessageEx{
public:
    CMessageEx(){strcpy(m_szMessage,"init message");}
    CMessageEx(char *initMsg){if(initMsg) strcpy(m_szMessage,initMsg);}
    ~CMessageEx(){}
    void SetMessage(char *initMsg){if(initMsg) strcpy(m_szMessage,initMsg);}
    char *GetMessage(){return m_szMessage;}
    void ShowMessage(){printf("msg:%s\n",m_szMessage);}

protected:
    char m_szMessage[256];
};

class CTestSystem
{
public:
    static CTestSystem &GetSingleton(){
        static CTestSystem sys;
        return sys;
    }

    
    CNumberEx& GetNumberObj(){return m_number;}
    CMessageEx & GetMessageObj(){return m_message;}
protected:
    CTestSystem(){}
    CTestSystem(const CTestSystem &){}
    ~CTestSystem(){}
    CTestSystem& operator=(const CTestSystem &rhs);

private:
    CNumberEx m_number;
    CMessageEx m_message;
};

#endif/*_CLASS_G_H__*/

3.在tarray.pkg如下

$#include "tclass.h"
$#include "tarray.h"
$#include "classg.h"

extern int g_Arr[10]@Arr;


class CNumber {
public:
    CNumber(); 
    CNumber(int num);
    ~CNumber();   
    void SetNumber(int num);    
    int GetNumber();    
    int Add(int num);
};

class CMessage
{
public:
    CMessage();
    ~CMessage();
    void SetMessage(char *msg);
    char *GetMessage();
    void ShowMessage();

};



class CNumberEx
{
public:
    CNumberEx();
    CNumberEx(int num);
    ~CNumberEx();

    void SetNumber(int num);
    int GetNumber();
    int Add(int num);
};

class CMessageEx
{
public:
    CMessageEx();
    CMessageEx(char *initMsg);
    ~CMessageEx();
    void SetMessage(char *initMsg);
    char *GetMessage();
    void ShowMessage();
};


class CTestSystem
{
public:
    static CTestSystem &GetSingleton();
    CNumberEx& GetNumberObj();
    CMessageEx & GetMessageObj();
};


4.main如下:
#include "stdafx.h"
#include "lua.hpp"  
#include "classg.h"
#include "tarray.h"  



int tolua_tarray_open (lua_State* tolua_S);  
int g_Arr[10]={0};  

int _tmain(int argc, _TCHAR* argv[])
{
    
    
    /*
    在lua5.1中,用来生成lua状态对象的lua_open函数不再直接可用,替换为lua_newstate,
    不过 lua_newstate要提供内存分配函数,lua扩展库提供了无参数的luaL_newstate,
    用起来方便。同时为了向前兼容,还做了宏定 义#define lua_open luaL_newstate()。
    所以你仍然可以用lua_open来或者lua_State,但是要注意这里只是个宏。
    
    luaopen_base()打开基本的库。     
    tolua_classgroup_open是tolua++生成的函数,用来向lua导出你定义的类和其它变量及函数。     
    luaL_dofile也是宏定义,用来加载并执行一个脚本文件,在lauxlib.h中定义。     
    lua_close关闭之前打开的状态块。
    
*/
    lua_State *L = luaL_newstate();
    luaopen_base(L);
    tolua_tarray_open(L);

    luaL_dofile(L, "C:/Users/Administrator/Desktop/test/NewLua/NewLua/classgroup.lua");  
    
    lua_close(L);  
    return 0;
}

5.同样执行命令(注意生成的tarray.cpp加入到工程编译)
>tolua++.exe -n tarray -o tarray.cpp tarray.pkg

6.lua脚本如下:
print("now in classgroup.lua!")
singleton = CTestSystem:GetSingleton()
print(singleton)
numobj = singleton:GetNumberObj()
print(numobj)
msgobj=singleton:GetMessageObj()
print(msgobj)

--access CNumber and CMessage
print("init obj's number:"..numobj:GetNumber())
numobj:SetNumber(100)
print("after call numobj:SetNumber(100),number:"..numobj:GetNumber())
print("init msgobj's message:"..msgobj:GetMessage())
msgobj:SetMessage("This is message is set in lua script")
print("new msg:"..msgobj:GetMessage())
msgobj:ShowMessage()

7.结果如下:
now in classgroup.lua!
userdata: 001C54D8
userdata: 001CD388
userdata: 001CD470
init obj's number:0
after call numobj:SetNumber(100),number:100
init msgobj's message:init message
new msg:This is message is set in lua script
msg:This is message is set in lua script


三. 导出单继承类
1. 新建inheritance.h文件如下:
#ifndef _INHERITANCE_H__
#define _INHERITANCE_H__

#include <Windows.h>
#include <string>

typedef enum{
    AUICSNormal   = 0,
    AUICSHover    = 1,
    AUICSPushed   = 2,
    AUICSDisabled = 3,
    AUICSHide     = 4,
    AUICSFORCEDOWRD = 0xFFFFFFFF
}AUIControlState;


class CAUIControl
{
public:
    CAUIControl():m_nID(-1),m_state(AUICSNormal),m_bEnable(true),m_strText(""){}
    virtual ~CAUIControl(){}

public:
    void SetID(int nID){m_nID = nID;}
    int GetID(){return m_nID;}
    
    void SetText(char *szText){m_strText = szText;}
    const char* GetText(){return m_strText.c_str();}

    void SetSize(SIZE sz){m_size = sz;}
    SIZE GetSize(){return m_size;}

    void SetEnabled(bool bEnable){m_bEnable = bEnable;}
    bool IsEnabled(){return m_bEnable;}

    void SetPosition(POINT pt){m_position = pt;}
    POINT GetPosition(){return m_position;}

public:
    virtual void Render() = 0;

    virtual bool MsgProc(HWND hwnd,UINT uMsg,WPARAM wPram,LPARAM lParam){return false;}

protected:
    int m_nID;
    POINT m_position;
    AUIControlState m_state;
    std::string m_strText;
    SIZE m_size;
    bool m_bEnable;
};

class CAUIButton : public CAUIControl
{
public:
    CAUIButton():m_pTexture(NULL){}
    virtual ~CAUIButton(){}

public:
    void SetTexture(char *szFile){}
    void SetTextureRects(const RECT &rcNormal,const RECT &rcHover,const RECT &rcPushed,const RECT &rcDisabled){}

public:
    void Render(){printf("CAUIButton::Render");}
     bool MsgProc(HWND hwnd,UINT uMsg,WPARAM wPram,LPARAM lParam)
     {
        
         printf("CAUIButton::MsgProc ");
         return false;
     }
protected:
    void *LoadTexture(char *szTextureFile){return NULL;}
    void *m_pTexture;
    RECT m_rects[4];
};

extern CAUIButton g_button;


#endif/*_INHERITANCE_H__*/

2.pkg文件如下:
$#include "inheritance.h"

class CAUIControl
{
public:
    void SetID(int nID);
    int GetID();
    
    void SetText(char *szText);
    const char* GetText();

    void SetSize(SIZE sz);
    SIZE GetSize();

    void SetEnabled(bool bEnable);
    bool IsEnabled();

    void SetPosition(POINT pt);
    POINT GetPosition();
};

class CAUIButton : public CAUIControl
{
public:
    CAUIButton();
    virtual ~CAUIButton();

public:
    void SetTexture(char *szFile);
    void SetTextureRects(const RECT &rcNormal,const RECT &rcHover,const RECT &rcPushed,const RECT &rcDisabled);
};

extern CAUIButton g_button@button;

3.main文件如下:

CAUIButton g_button;
int _tmain(int argc, _TCHAR* argv[])
{
    lua_State *L = luaL_newstate();
    luaopen_base(L);
    tolua_tarray_open(L);
    
    //luaL_dofile(L, "C:/Users/Administrator/Desktop/test/NewLua/NewLua/classgroup.lua");  
    luaL_dofile(L, "C:/Users/Administrator/Desktop/test/NewLua/NewLua/inheritance.lua");  
    
    lua_close(L);  
    return 0;
}


4. lua脚本如下:
print("now in inheritance.lua!")
-- access global
button:SetID(100)
button:SetText("global button")
print(button:GetText())

newbutton = CAUIButton:new()

newbutton:SetID(100)
newbutton:SetText("new button")
print(newbutton:GetText())


5.输出结果:
now in inheritance.lua!
global button
new button


四.互调综合实例

1. 新建toluaclass.h如下:
#ifndef _TOLUACLASS_H_
#define _TOLUACLASS_H_

class CBase
{
public:
    CBase(){}
    virtual ~CBase(){}

    virtual void ShowMessage(){printf("BaseClass\n");}
    static char *ClassName(){return "CBase\n";}
};

class CDerived1 : public CBase{
public:
    CDerived1(){}
    ~CDerived1(){}

    void ShowMessage(){printf("CDerived1Class\n");}
    void ShowDerived1(){printf("show derived1\n");}
};


class CDerived2:public CBase{
public:
    CDerived2(){m_nNumber = 0;}
    ~CDerived2(){}
    
    void ShowMessage(){printf("Derived2Class\n");}
    void ShowDerived2(){printf("show derived2\n");}
    void SetNumber(int num){m_nNumber = num;}
    int GetNumber(){return m_nNumber;}

protected:
    int m_nNumber;
};

extern CDerived1 * toDerived1(void *p);
extern CDerived2 * toDerived2(void *p);

#endif/*_TOLUACLASS_H_*/

2.pkg文件如下:
$#include "toluaclass.h"


class CBase
{
public:
    virtual void ShowMessage();
    static char *ClassName();
};

class CDerived1 : public CBase{
public:

    void ShowMessage();
    void ShowDerived1();
};


class CDerived2:public CBase{
public:    
    void ShowMessage();
    void ShowDerived2();
    void SetNumber(int num);
    int GetNumber();
};

extern CDerived1 * toDerived1(void *p);
extern CDerived2 * toDerived2(void *p);


$[
testHelper={}

function testHelper.toDerived3(e)

  return tolua.cast(e,"CDerived2")
end

$]
tolua++提供了转换机制,tolua++生成了一些工具函数,在tolua为名的module中。其中tolua.cast就是用来做类型转换的。只需要改动tlclass.pkg文件
3. 驱动代码如下:
// NewLua.cpp : 定义控制台应用程序的入口点。
//

#include "stdafx.h"
#include "lua.hpp"  
#include "classg.h"
#include "tarray.h"  
#include "inheritance.h"
#include "toluaclass.h"


int tolua_tarray_open (lua_State* tolua_S);  



CDerived1 *toDerived1(void *p)
{
    return dynamic_cast<CDerived1*>((CBase*)p);
}

CDerived2 *toDerived2(void *p)
{
    return dynamic_cast<CDerived2*>((CBase*)p);
}

int _tmain(int argc, _TCHAR* argv[])
{
    lua_State *L = luaL_newstate();
    luaopen_base(L);
    tolua_tarray_open(L);
    
    //luaL_dofile(L, "C:/Users/Administrator/Desktop/test/NewLua/NewLua/classgroup.lua");  
    luaL_dofile(L, "C:/Users/Administrator/Desktop/test/NewLua/NewLua/CallLuaFunc.lua");  
    
    CBase *p1 = new CDerived1();
    CBase *p2 = new CDerived2();

    //call Derived1Test
    lua_getglobal(L,"Derived1Test");
    lua_pushlightuserdata(L,p1);
    
    if(lua_pcall(L,1,0,0)!=0)
    {
        fprintf(stderr,"Call Devied1Test failed:%s",lua_tostring(L,-1));
    }

    //call Derived2Test
    lua_getglobal(L,"Derived2Test");
    lua_pushlightuserdata(L,p2);
    if(lua_pcall(L,1,0,0)!=0)
    {
        fprintf(stderr,"Derived2Test failed:%s",lua_tostring(L,-1));
    }

    //lua中转换测试
    lua_getglobal(L,"Derived3Test");
    lua_pushlightuserdata(L,p2);
    if(lua_pcall(L,1,0,0)!=0)
    {
        fprintf(stderr,"Derived3Test failed:%s",lua_tostring(L,-1));
    }


    delete p1;
    delete p2;

    lua_close(L);  
    return 0;
}

在C++中调用lua脚本的函数大概分为三步:    
a..找到函数并入栈;(这里是lua_getglobal(L,  "Derived1Test");)    
b..参数入栈;(这里是lua_pushlightuserdata(L, p1);)    
c..调用lua_pcall进行实际调用    
第一步不必说了;
第二步可以传递任意个任意类型的参数,lua_pushnumber,lua_pushstring, lua_pushboolean等 等可以调用;
第三步是调用lua_pcall,lua_pcall第一个参数是lua_State*,
这是我们的工作环境了。
第二参数是要传递的参数个 数,我们这里是1;
第三个参数是lua函数返回的结果个数,我们的lua函数不返回结果,设为0。
第四个参数是比较复杂,为0时指lua_pcall会在 调用失败时把原始错误信息放到栈上;
其它值代表栈的索引,该索引处放了一个错误处理函数,lua_pcall失败时会根据这个索引去调用该函数。     调用失败的时候我只是简单地打印一条出错信息,这个错误码放在栈顶,我们用lua_tostring(L,-1)访问并转换为字符串。可以修改下驱动代 码,比如把第二次调用传入p1,这样就可以看见错误信息。     最后我还是在C++代码中打印了下CDerived2对象的值,以验证lua和C++中访问的是同一个对象。    Lua和C++的交互都是通过栈,所以要写交互部分的代码就要不停的出栈入栈,烦死个人。不过这也是Lua灵活的地方。
4.lua脚本如下:
print ("now in CallLuaFunc.lua!")

-- lua function to test CDerived1, CDerived2, they'll be called from C++

function Derived1Test(e)

d1 = toDerived1(e)
if d1 then
    d1:ShowMessage()
    d1:ShowDerived1()

else
    print("invalid d1(nil)")

end

end


function Derived2Test(e)

d2 = toDerived2(e)

if d2 then
    d2:ShowMessage();
    d2:ShowDerived2();
    d2:SetNumber(180);

    print(d2:GetNumber())


else
    print("invalid d2(nil)")

end

end



-- 利用pkg中的tolua.cast转换
function Derived3Test(e)
d3 = testHelper.toDerived3(e)

if d3 then
    d3:ShowMessage();
    d3:ShowDerived2();
    d3:SetNumber(380);

    print(d3:GetNumber())


else
    print("invalid d3(nil)")

end

end


posted on 2014-05-07 14:06 C++技术中心 阅读(5541) 评论(0)  编辑 收藏 引用 所属分类: 游戏开发

只有注册用户登录后才能发表评论。
网站导航: 博客园   IT新闻   BlogJava   博问   Chat2DB   管理