/

30 tháng 6, 2013

Dẫn xuất một lớp ObjectARX mới

Chuyên mục này sẽ miêu tả cách tổ chức mã nguồn và sử dụng macro ObjectARX® để làm đơn giản công việc dẫn xuất một lớp ObjectARX mới. Các macro cho phép một lớp mới được tham gia vào cơ cấu định kiểu chạy bên trong AcRxObject (AcRxObject runtime type identification mechanism). Nếu bạn không muốn phân biệt lớp mới trong lúc chạy, bạn có thể sử dụng kiểu dẫn xuất từ C++ để tạo ra một lớp mới. 

Các đề tài trong chuyên mục:

Lớp dẫn xuất mới

Để tạo điều kiện cho phát triển, các lớp đối tượng mới nên được khai báo và thực thi trong các module ObjectDBX riêng biệt. Module này chứa phần cơ sở dữ liệu (CSDL) của ứng dụng, bao gồm các macro ObjectDBX được miêu tả trong chuyên mục này và các hàm chồng từ các lớp ObjectARX khác cũng như các hàm xác định khác trong lớp. Bởi một ứng dụng ObjectDBX, module này có thể không cần sử dụng đến trình soạn thảo của AutoCAD® editor, reactor editor, hoặc bất kể API cụ thể nào khác cho ứng dụng máy chủ AutoCAD. Bạn nên cung cấp một đối tượng enabler cho đối tượng mới của mình, xem chương 24, Object Enablers để biết thêm chi tiết.
ObjectARX cung cấp một tập hợp macro, đã được khai báo trong têm tin rxboiler, sẽ giúp bạn tạo ra những lớp dãn xuất từ AcRxObject. Nếu không sử dụng macro để định nghĩa một lớp mới, lớp đó sẽ kết thừa các identity thực thi của lớp ObjectARX cơ sở liền trên.

Ứng dụng không nên sử dụng dẫn xuất từ các lớp sau:
  • AcDbDimension
  • AcDbSymbolTable, AcDbSymbolTableRecord, and all classes derived from them
  • AcDbBlockBegin
  • AcDbBlockEnd
  • AcDbSequenceEnd
  • AcDb2dPolyline
  • AcDb2dVertex
  • AcDb3dPolyline
  • AcDb3dPolylineVertex
  • AcDbPolygonMesh
  • AcDbPolygonMeshVertex
  • AcDbPolyFaceMesh
  • AcDbPolyFaceMeshVertex
  • AcDbFaceRecord
  • AcDbViewport
  • AcDbMInsertBlock
  • AcDbVertex

Xác định lớp thời gian chạy (Runtime Class Identification RTTI)

Mọi lớp trong phân cấp ObjectARX dẫn xuất từ AcRxObject đều có một đối tượng descriptor tương ứng. Nó là một đại diện của  AcRxClass chịu trách nhiệm giữ thông tin cho RTTI. Lớp descriptor gpDesc, là một thành viên data tĩnh của lớp. Ví dụ: AcDbEllipse::gpDesc. Đối tượng descriptor lớp được tạo ra trong lúc khởi tạo, khi các lớp đã được đăng ký với ObjectARX và được thêm vào từ điển cấp hệ thống, acrxClassDictionary. Macro được miêu tả ở đây giúp cho khai báo và thực thi của các hàm nhất định có liên quan đến runtime identification và hàm khởi tạo. Chúng bao gồm routime khởi tạo như là các hàm desc(), cast(), isKindOf(), và isA() cho đối tượng mới.
Các hàm quan trọng cung cấp bởi lớp AcRxObject cho RTTI bao gồm:
  • desc(), hàm thành viên tĩnh, trả về đối tượng descriptor lớp của lớp đặc biệt.
  • cast(), hàm thành viên tĩnh,trả về một đối tượng của kiểu xác định, hoặc NULL nếu đối tượng không đúng như yêu cầu hoặc là một lớp dấn xuất.
  • isKindOf() trả về khi đối tượng thuộc về một lớp xác định (hoặc lớp dẫn xuất).
  • isA() trả về đối tượng descriptor lớp của một đối tượng.
Khi muốn biết đối tượng thuộc lớp nào, hãy sử dụng AcRxObject::isA(). Hàm này trả về đối tượng descriptor (một đại diện của AcRxClass) cho một đối tượng CSDL. Khai báo của nó là:
AcRxClass* isA() const;

Khi đã biết được lớp của đối tượng, bạn có thể sử dụng hàm desc() để lấy đối tượng descriptor.


static AcRxClass* desc();
Ví dụ sau đây tìm kiếm đại diện của AcDbEllipse hoặc bất cứ lớp dẫn xuất nào từ nó, sử dụng hàm thành viên isKindOf()AcDbEllipse::desc():
AcDbEntity* curEntity = somehowGetAndOpenAnEntity();
if (curEntity->isKindOf(AcDbEllipse::desc())) {
   // Got some kind of AcDbEllipse instance.
}
Ví dụ này trình bày một cách khác để tìm kiếm đại diện của AcDbEllipse hoặc các lớp dẫn xuất bằng cách sử dụng hàm thành viên AcDbEllipse::cast():
AcDbEllipse* ellipseEntity = AcDbEllipse::cast(curEntity);
if (ellipseEntity != NULL) {
   // Got some kind of AcDbEllipse instance.
}
Ví dụ sau lại tìm kiếm đại diện của AcDbEllisp, nhưng loại trừ các lớp dẫn xuất, sử dụng các hàm isA()AcDbEllipse::desc():
if (curEntity->isA() == AcDbEllipse::desc()) {
   // Got an AcDbEllipse, no more, no less.

Macro khai báo lớp

Tệp tin header cho đối tượng mới có thể sử dụng macro ObjectARX ACRX_DECLARE_MEMBERS(CLASS_NAME) để khai báo các hàm desc(), cast(), và isA().
Macro được dùng trong khu vực public của khai báo lớp mới:
class myClass : public AcRxObject
{
public:
ACRX_DECLARE_MEMBERS(myClass);
...
};
For AsdkPoly, the following line expands to a single long line of code.
ACRX_DECLARE_MEMBERS(AsdkPoly);
When reformatted to multiple lines for clarity, the line looks like this:
virtual AcRxClass* isA() const;
static AcRxClass* gpDesc;
static AcRxClass* desc();
static AsdkPoly* cast(const AcRxObject* inPtr)
{
    return ((inPtr == 0)
        || !inPtr->isKindOf(AsdkPoly::desc()))
        ? 0 : (AsdkPoly*)inPtr;
};
static void rxInit();
Hàm tĩnh rxInit() và con trỏ tĩnh gpDesc được khai báo bởi macro được dùng để thực thi các hàm isA(), desc(), và cast()

Macro thực thi lớp

Để thực thi một lớp mới, sử dụng một trong ba macro trong tệp tin nguồn (source):
  • ACRX_NO_CONS_DEFINE_MEMBERS(CLASS_NAME, PARENT_CLASS)
Sử dụng cho khai  lớp vắn tắt hoặc bất cứ lớp nào không cần khởi tạo.
  • ACRX_CONS_DEFINE_MEMBERS(CLASS_NAME, PARENT_CLASS, VERNO)
Sử dụng cho các lớp nhất có thể được khởi tạo nhưng không được ghi vào trong tệp tin.
  • ACRX_DXF_DEFINE_MEMBERS(CLASS_NAME, PARENT_CLASS, DWG_VERSION,\
    MAINTENANCE_VERSION, PROXY_FLAGS, DXF_NAME, APP)
Sử dụng cho các lớp có thể được ghi, đọc từ tệp tin DWG, DXF. Mỗi một macro định nghĩa như sau: 
  • Đối tượng descriptor lớp
  • Hàm khởi tạo lớp (xem Class Initialization Function)
  • Hàm desc() function cho lớp
  • Hàm ảo isA() (thừa kế từ AcRxObject) mà lớp mới này sẽ ghi đè.
Với đối tượng AsdkPoly, dưới đây là một ví dụ:
ACRX_DXF_DEFINE_MEMBERS(AsdkPoly, AcDbCurve, AcDb::kDHL_CURRENT,\
    AcDb::kMReleaseCurrent, 0, POLYGON, /*MSG0*/"AutoCAD");
Khi định dạng lại thành nhiều đoạn thẳng cho rõ ràng hơn, đoạn thẳng nhìn giống như sau:
AcRxClass* AsdkPoly::desc()
{
    if (AsdkPoly::gpDesc != 0)
        return AsdkPoly::gpDesc;
    return AsdkPoly::gpDesc =
        (AcRxClass*)((AcRxDictionary*)acrxSysRegistry()->
        at("ClassDictionary"))->at("AsdkPoly");
}
AcRxClass* AsdkPoly::isA() const
{
    return AsdkPoly::desc();
}
AcRxClass* AsdkPoly::gpDesc = 0;
static AcRxObject * makeAsdkPoly()
{
    return new AsdkPoly();
}
void AsdkPoly::rxInit()
{
    if (AsdkPoly::gpDesc != 0)
        return;
    AsdkPoly::gpDesc = newAcRxClass("AsdkPoly",
        "AsdkCurve", AcDb::kDHL_CURRENT, AcDb::kMReleaseCurrent,
         0, &makeAsdkPoly, "POLYGON", "\"AutoCAD\"");
};
Khi mở rộng ra, dấu chấm phảy (;) cuối cùng của macro gọi dòng dịch chuyển vào sau khi đóng ngoặc nhọn (}) cho một định nghĩa hàm. Vì vậy, dấu chấm phảy không được đòi hỏi cho macro này gọi dòng.
Nếu muốn viết hàm rxInit() cho riêng mình,  sử dụng macro ACRX_DEFINE_MEMBERS(), nó sẽ định nghĩa desc(), cast() và isA() cho lớp nhưng không định nghĩa hàm rxInit(). Macro này cũng không tạo ra đối tượng AcRxClass mong muốn mà là trách nhiệm của hàm rxInit().

Hàm khởi tạo lớp

Hàm khởi tạo cho mỗi lớp là rxInit(). Một ứng dụng sẽ định nghĩa một lớp riêng phải gọi ra hàm này trong quá trình khởi tạo.
Hàm này được định nghĩa tự động bởi từng hàm trong số 3 macro ACRX_xxx_DEFINE_MEMBERS() và thực hiện các nhiệm vụ sau:
  • Đăng ký lớp mới
  • Tạo ra đối tượng mô tả lớp
  • Đặt bộ mô tả vào trong lớp từ điển dictionary
Nếu muốn định nghĩa riêng hàm rxInit(), hãy sử dụng macro ACRX_DEFINE_MEMBERS().

Không có nhận xét nào:

Đăng nhận xét