上一篇在這 C++混合編程之idlcpp教程Lua篇(4)

第一篇在這 C++混合編程之idlcpp教程(一)

與前面的工程相似,工程LuaTutorial3中,同樣加入了三個文件:LuaTutorial3.cpp, Tutorial3.i, tutorial3.lua 。其中LuaTutorial3.cpp的內容基本和LuaTutorial2.cpp雷同,不再贅述。

首先看一下Tutorial3.i的內容:

 

namespace tutorial
{
    struct Point
    {
        float x;
        float y;
        nocode Point();
        nocode Point(float a, float b);
        nocode Point(const Point& pt);

        #{
        Point()
        {}
        Point(float a, float b)
        {
            x = a;
            y = b;
        }
        #}
    };

    struct Shape
    {
        abstract float getArea();
        ##        virtual ~Shape() {}
    };

    struct Triangle : Shape
    {
        Point m_vertices[#3];
        nocode static Triangle+ New();
        #{
            virtual float getArea()
        {
            return fabs(m_vertices[0].x * m_vertices[1].y
                + m_vertices[1].x * m_vertices[2].y
                + m_vertices[2].x * m_vertices[0].y
                - m_vertices[0].x * m_vertices[2].y
                - m_vertices[1].x * m_vertices[0].y
                - m_vertices[2].x * m_vertices[1].y) * 0.5;
        }
        static Triangle* New()
        {
            return new Triangle;
        }
        #}
    };

}


在這里仍然有struct Point。 引入了基類 struct Shape。其中這一行

abstract float getArea();

表示聲明一個純虛函數,相當于C++中的

virtual float getArea() = 0;

如果不是純虛函數,使用關鍵字virtual代替abstract即可。

新加入了類型 Triangle

struct Triangle : Shape

與C++一樣,用 : 表示繼承。因idlcpp表示的是接口信息,所以只有公有繼承。與C++不同,idlcpp并沒有public, protected, private這三個關鍵字。

然后是數據成員m_vertices;

Point m_vertices[#3];

idlcpp只關心接口的信息,在其語法分析部分,只能看見Point m_vertices[]。數字3需要用插入代碼的方式。其中#表示直接連在后面的一個標識符或整數(實際上是由字母,下劃線和數字組成的串)將插入到生成的C++頭文件對應的位置上。下一行

nocode static Triangle+ New();

聲明了一個名為New的靜態函數,其實現代碼就在后續的類型聲明內,所以此處用nocode阻止在頭文件中生成函數聲明,如前所述,idlcpp如果看見了構造函數的聲明,會生成靜態函數New,所以此時不能出現構造函數的聲明,以免名字沖突。對照一下后面實現部分的C++聲明

static Triangle* New()

這里和C++不一致的地方是用+代替了*,放在函數返回值類型與函數名之間,表示函數內部以new的形式創建了一個對象,返回其指針,外界需要用delete的形式刪除它(還有另一種關于引用計數的情況,暫不討論)。在腳本語言中一般自帶垃圾收集機制,腳本語言自動管理內存的分配釋放。程序員一般不用關心何時刪除對象這樣的問題,而在C++中在堆上分配對象的生命期一般由程序員維護。為處理其間的差異,idlcpp在函數聲明的返回值類型部分有如下幾種形式:

 

idlcpp聲明

C++聲明

實現

typeName

typeName

返回值

typeName &

typeName&

返回引用

typeName *

typeName*

返回指針

typeName +

typeName*

返回指針,外界需要delete,或者增加了引用計數,外界需要release

typeName + []

typeName*

返回指針,外界需要delete[]

例如下面的C++代碼:

int g_a;
int* getGlobal()
{
    return &g_a;
}

int* getNew()
{
    return new int;
}

int* getNewArray(int count)
{
    return new int[count];
}

三個函數的返回值類型都是int*,但對于后面兩個,分別要用delete 和delete[]釋放內存,就語法層面看,從函數的聲明不能區分這些情況。只能由程序員根據實際情況進行不同的處理。而在腳本語言中并不希望看到顯示的刪除對象的調用。所以idlcpp通過語法層面的聲明,在生成的元數據代碼中進行區分,然后由運行時庫(pafcore.dll)進行處理。

編譯后生成的Tutorial3.h的內容如下:

//DO NOT EDIT THIS FILE, it is generated by idlcpp
//http://www.idlcpp.org

#pragma once

#include "./Tutorial3.h"
namespace tutorial{ struct Triangle; }

namespace tutorial
{
    struct Point
    {
    public:

        float x;
        float y;
 
        Point()
        {}
        Point(float a, float b)
        {
            x = a;
            y = b;
        }
        
    };

    struct Shape
    {
    public:

        virtual float getArea() = 0 ;
        virtual ~Shape() {}
    };

    struct Triangle : public Shape
    {
    public:

        Point m_vertices[3];

            virtual float getArea()
        {
            return fabs(m_vertices[0].x * m_vertices[1].y
                + m_vertices[1].x * m_vertices[2].y
                + m_vertices[2].x * m_vertices[0].y
                - m_vertices[0].x * m_vertices[2].y
                - m_vertices[1].x * m_vertices[0].y
                - m_vertices[2].x * m_vertices[1].y) * 0.5;
        }
        static Triangle* New()
        {
            return new Triangle;
        }
        
    };

}

 

內容基本上都是和Tutorial3.i中一一對應的。然后看一下腳本tutorial3.lua的內容:

 

triangle = paf.tutorial.Triangle();
triangle.m_vertices[0] = paf.tutorial.Point(0,0);
triangle.m_vertices[1] = paf.tutorial.Point(0,1);
triangle.m_vertices[2] = paf.tutorial.Point(1,1);

print(triangle:getArea()._);

創建了一個tirangle對象,然后設置數據成員,此處運行時能夠檢測下標的范圍為0至2,如果超出范圍將會報錯,最后調用其基類Shape中聲明的函數getArea(),因為這是虛函數,所以最終會調用到Traingle::getArea()。

編譯運行結果如下圖: