上一篇在這 C++混合編程之idlcpp教程Lua篇(5)
第一篇在這 C++混合編程之idlcpp教程(一)
工程LuaTutorial4中加入了四個文件:LuaTutorial4.cpp, Tutorial4.cpp, Tutorial4.i, tutorial4.lua。這個做法和以前不太一樣,前幾個工程中用.i文件生成的頭文件時,類型的成員函數都是用內聯的方式寫在頭文件中。實際上按C++的使用習慣來說只有簡短的函數建議以內聯方式實現,其余的函數一般寫在另一個對應的.cpp文件中。此處加入的Tutorial4.cpp就是這個用法。
首先看一下Tutorial4.i的內容:
#import "../../paf/src/pafcore/Reference.i" $$#include <vector> namespace tutorial { struct Point { float x; float y; Point(); Point(float a, float b); meta: Point(const Point ref pt); }; class Shape : Reference { abstract float getArea(); $$ virtual ~Shape() {} }; class ShapeManager(value_object) { void addShape(Shape ptr shape); float getTotalArea(); static ShapeManager ptr GetInstance(); $* ~ShapeManager(); private: std::vector<Shape*> m_shapes; *$ }; class Triangle : Shape { Point m_vertices[$3]; meta: Triangle(); $$virtual float getArea(); }; }
首先是
#import "../../paf/src/pafcore/Reference.i"
#import相當於C++中的 #include 編譯時先將其所描述的文件中的內容插入到對應的位置。
第二行
$$#include <vector>
將#include <vector> 插入到Tutorial4.h的對應位置上。
在這裡仍然有struct Point,但由於其中構造函數的實現代碼將會放到Tutorial4.cpp中。所以寫法和以前有所不同。
然後是
class Shape : Reference
對照一下上一節的寫法 struct Shape 有兩處不同,一是使用了關鍵字class 替代了struct 二是使用了基類Reference。這個基類是運行時庫pafcore提供的,具體內容請參看 Reference.i。class Reference 位於 namespace pafcore下。
許多編程語言對於內存管理都提供了內置的支持,C#,Java,Lua,Python等都有垃圾收集機制,然而在C++中沒有這種機制,一般需要程序員手工維護對象的生命期。一種常用的方法是引用計數,引用計數算是一種簡潔高效的手段了。在idlcpp中提供了引用計數的直接支持。類 ::pafcore::Reference 提供了用於引用計數的基本接口。而關鍵字class 默認其描述的類型直接或間接派生自::pafcore::Reference,使用struct 則沒有這個假設,注意此處和C++不同。另外如果在idlcpp中使用struct ,則在生成的C++類型中也使用struct 做關鍵字;如果在idlcpp中使用class ,則在生成的C++類型中也使用class 做關鍵字。如果想在C++中使用關鍵字class 且又不想讓其派生自::pafcore::Reference,idlcpp提供了相應的語法來處理這種情況,見下表:
idlcpp
C++
struct A
struct A
class A
class A : public ::pafcore::Reference
struct A(reference_object)
struct A : public ::pafcore::Reference
class A(value_object)
class A
然後是
class ShapeManager(value_object)
如前所述,希望在C++中使用class 而又不需要派生自::pafcore::Reference,在類型的名字後面加上(value_object)即可。
在class ShapeManager提供了三個接口函數。第一個函數void addShape(Shape ptr shape);在其參數中出現了關鍵字ptr。這個相當於C++中的*,表示傳指針,之所以不使用*而使用ptr代替的原因見上一節。idlcpp在函數聲明的參數傳遞類型部分有如下幾種形式:
idlcpp聲明
C++聲明
實現
typeName
typeName
傳值
typeName ptr
typeName *
傳地址
typeName ref
typeName &
傳引用
typeName ptr ptr
typeName **
傳指針的地址
typeName new ptr
typeName **
傳指針的地址,用於接收函數內部new的對象,或者增加引用計數,外界需要delete或release
typeName new [] ptr
typeName **
傳指針的地址,用於接收函數內部new []的對象數組,外界需要delete []
typeName ptr ref
typeName *&
傳指針的引用
typeName new ref
typeName *&
傳指針的引用,用於接收函數內部new的對象,或者增加引用計數,外界需要delete或release
typeName new [] ref
typeName *&
傳指針的引用,用於接收函數內部new []的對象數組,外界需要delete []
最後的
class Triangle : Shape
和上一節一樣,只不過Shape派生自::pafcore::Reference;因此class Triangle 也有引用計數的功能。
編譯後生成的Tutorial4.h的內容如下:
//DO NOT EDIT THIS FILE, it is generated by idlcpp //http://www.idlcpp.org #pragma once #include "./Tutorial4.h" #include "../../paf/src/pafcore/Reference.h" namespace tutorial{ class ShapeManager; } namespace tutorial{ class Triangle; } #include <vector> namespace tutorial { struct Point { public: float x; float y; Point(); Point(float a,float b); public: static Point* New(); static Point* New(float a,float b); static Point* NewArray(unsigned int count); static Point* Clone(const Point& pt); }; class Shape : public ::pafcore::Reference { public: virtual ::pafcore::Type* getType(); virtual float getArea() = 0 ; virtual ~Shape() {} }; class ShapeManager { public: void addShape(Shape* shape); float getTotalArea(); static ShapeManager* GetInstance(); ~ShapeManager(); private: std::vector<Shape*> m_shapes; }; class Triangle : public Shape { public: virtual ::pafcore::Type* getType(); Point m_vertices[3]; public: static Triangle* New(); static Triangle* NewARC(); static Triangle* NewArray(unsigned int count); static Triangle* NewArrayARC(unsigned int count); virtual float getArea(); }; }
在類型 Shape 和Triangle中,idlcpp為其添加了虛函數
virtual ::pafcore::Type* getType();
實現代碼見Tutorial4.ic。
此外class Triangle 中除了
static Triangle* New();
static Triangle* NewArray(unsigned int count);
這兩個由構造函數生成的靜態函數外,還多了
static Triangle* NewARC();
static Triangle* NewArrayARC(unsigned int count);
其間的區別見具體實現代碼,實現代碼在Tutorial4.ic中。
下面是Tutorial4.ic的內容
//DO NOT EDIT THIS FILE, it is generated by idlcpp //http://www.idlcpp.org #pragma once #include "Tutorial4.h" #include "Tutorial4.mh" #include "../../paf/src/pafcore/RefCount.h" namespace tutorial { inline Point* Point::New() { return new Point(); } inline Point* Point::New(float a,float b) { return new Point(a, b); } inline Point* Point::NewArray(unsigned int count) { return new_array<Point>(count); } inline Point* Point::Clone(const Point& pt) { return new Point(pt); } ::pafcore::Type* Shape::getType() { return ::RuntimeTypeOf<Shape>::RuntimeType::GetSingleton(); } ::pafcore::Type* Triangle::getType() { return ::RuntimeTypeOf<Triangle>::RuntimeType::GetSingleton(); } inline Triangle* Triangle::New() { return new ::pafcore::RefCountObject<Triangle>(); } inline Triangle* Triangle::NewARC() { return new ::pafcore::AtomicRefCountObject<Triangle>(); } inline Triangle* Triangle::NewArray(unsigned int count) { return new_array<::pafcore::RefCountObject<Triangle>>(count); } inline Triangle* Triangle::NewArrayARC(unsigned int count) { return new_array<::pafcore::AtomicRefCountObject<Triangle>>(count); } }
注意一下下面兩個函數的區別
static Triangle* New();
static Triangle* NewARC();
在::pafcore::Reference中僅僅提供了引用計數的接口,引用計數的具體實現方法是多種多樣的,pafcore中提供的一種實現方法。具體參見pafcore中的文件RefCount.h。其中提供了兩個模板類RefCountObject和AtomicRefCountObject。其中AtomicRefCountObject用原子操作處理引用計數,可用於多線程同時訪問對象引用計數的情況。在idlcpp生成的New函數和NewARC函數中分別使用了這兩個模板類,用戶可以根據具體情況調用不同的函數。
再看一下Tutorial4.cpp的內容
#include "Tutorial4.h" #include "Tutorial4.mh" #include "Tutorial4.ic" #include "Tutorial4.mc" namespace tutorial { Point::Point() {} Point::Point(float a, float b) { x = a; y = b; } ShapeManager* ShapeManager::GetInstance() { static ShapeManager s_instance; return &s_instance; } ShapeManager::~ShapeManager() { auto it = m_shapes.begin(); auto end = m_shapes.end(); for (; it != end; ++it) { Shape* shape = (*it); shape->release(); } } void ShapeManager::addShape(Shape* shape) { shape->addRef(); m_shapes.push_back(shape); } float ShapeManager::getTotalArea() { float area = 0; auto it = m_shapes.begin(); auto end = m_shapes.end(); for (; it != end; ++it) { Shape* shape = (*it); area += shape->getArea(); } return area; } float Triangle::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; } }
最上面四行將idlcpp生成的四個代碼文件包含進來,其中Tutorial4.ic和Tutorial4.mc有具體實現代碼,不可在別的地方再次包含。後面是各個類型的成員函數的實現代碼。
LuaTutorial4.cpp代碼和以前的類似,只是去除了上面四個#include語句。
最後看一下腳本tutorial4.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); shapeManager = paf.tutorial.ShapeManager.GetInstance(); shapeManager:addShape(triangle); print(shapeManager:getTotalArea()._);
編譯執行,結果如下圖: