上一篇在這 C++混合編程之idlcpp教程Python篇(4)
第一篇在這 C++混合編程之idlcpp教程(一)
與前面的工程相似,工程PythonTutorial3中,同樣加入了三個文件:PythonTutorial3.cpp, Tutorial3.i, tutorial3.py。其中PythonTutorial3.cpp的內容基本和PythonTutorial2.cpp雷同,不再贅述。
首先看一下Tutorial3.i的內容:
namespace tutorial { struct Point { float x; float y; meta: Point(); Point(float a, float b); Point(const Point ref 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]; meta: static Triangle new 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++頭文件對應的位置上。下兩行
meta:
static Triangle new New();
聲明了一個名為New的靜態函數,其實現代碼就在後續的類型聲明內,所以此處用meta阻止在頭文件中生成函數聲明,如前所述,idlcpp如果看見了構造函數的聲明,會生成靜態函數New,所以此時不能出現構造函數的聲明,以免名字沖突。對照一下後面實現部分的C++聲明
static Triangle* New()
這裡和C++不一致的地方是少了一個*並且多了一個new。new是idlcpp中的一個關鍵字,放在函數返回值類型與函數名之間,表示函數內部以new的形式創建了一個對象,返回其指針,外界需要用delete的形式刪除它(還有另一種關於引用計數的情況,暫不討論)。在腳本語言中一般自帶垃圾收集機制,腳本語言自動管理內存的分配釋放。程序員一般不用關心何時刪除對象這樣的問題,而在C++中在堆上分配對象的生命期一般由程序員維護。為處理其間的差異,idlcpp在函數聲明的返回值類型部分有如下幾種形式:
idlcpp聲明
C++聲明
實現
typeName
typeName
返回值
typeName ref
typeName&
返回引用
typeName ptr
typeName*
返回指針
typeName new
typeName*
返回指針,外界需要delete,或者增加了引用計數,外界需要release
typeName new []
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; public: static Point* New(); static Point* New(float a,float b); static Point* NewArray(unsigned int count); static Point* Clone(const Point& pt); 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.py的內容:
import pafpython; paf = pafpython.paf; 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()。
編譯運行結果如下圖: