封装的原因

Creo的接口有:Creo Toolkit (C语言),Creo Object Toolkit (C++),Creo Object Toolkit (Java),vbapi,weblink。其中Creo Toolkit (C语言)提供的接口最全,但是使用 C 语言开发又会有开发效率的问题。基于以上原因,将C接口进行进一步封装,兼顾功能与效率的问题。

基于Creo内部数据结构的封装方法

分析整理 Creo 提供的C接口中的数据结构,将他们分为以下几个类型:

  • Pod Object 可以直接进行内存拷贝的对象或数据:
    • ProModelitem(这种数据非常常见,用处非常多)
    • ProAsmcompath
    • ProMatrix
    • 等等;
  • Handle Object:指针(句柄),但是由Creo内部管理内存的分配与释放。如:
    • ProMdl
  • Not Copyable Object:指针,不可以拷贝,需要用户自行管理内存的 Alloc 与 Free 。如:
    • ProSelection
    • 各种 Data等。
  • 其他。

封装类图(见附件):
PtkPkgLib-类图.pdf
(注:类图中的类不全,目前未包含大量的ProMech相关类)

字符串的处理:

程序在处理过程中会大量的使用字符串,在Creo内部支持utf8编码的char及Unicode编码的wchar_t。不支持Multi byte编码,需要自己转换。建议默认使用Unicode编码。
字符串的使用。默认使用 stl 里面的 std::wstring 处理。需要特别注意,std::wstring在跨dll使用时,会出现内存异常 crash 。在必须跨dll使用时,转换成 char 或者 wchar_t

解耦

字符串的使用会造成Protoolkit与stl代码的耦合。封装过程中主要使用到的stl中类有std::string, std::wstring, std::vector

string解耦

  1. 新定义字符串类型 ptkstring, ptkwstring(便于因需要重定义为其他类型,如QString等)

    1. //utf8
    2. typedef std::string ptkstring;
    3. //unicode
    4. typedef std::wstring ptkwstring;
  2. string转char,wstring 转 wchar_t 都使用宏 _PtkString2Ptr 处理。

_PtkString2Ptr的实现:

  1. template <typename T> T* ptkConstCastPtr(const T* _item) {
  2. return const_cast<T*>(_item); }
  3. #define _PtkString2Ptr(str) _PtkStringIsEmpty(str) ? nullptr: ptkConstCastPtr(str.c_str())

vector解耦

类似于String,耦合处尽可能使用宏定义来实现

  1. //vector
  2. #define ptkvector std::vector
  3. //set
  4. #define ptkset std::set

内存的管理

Creo Toolkit C 使用过程中会有很多数据对象需要用户自己 Alloc & Free,这无疑很大程度上增加了编程人员的负担。为此,在封装的过程中,增加了以下几个宏:_ScopeClassCrt,_DefineObjectHasScope,_DefineObjectNoScope,_UserControlObjectStructor

宏 _UserControlObjectStructor:

用来快速创建派生自 _UserControlObject类 类的构造函数、析构函数。降低代码量。

宏 _ScopeClassCrt:

_ScopeClassCrt用来快速创建数据对象的“生命域”类,实现代码如下:

  1. #define _ScopeClassCrt(_class) \
  2. class Scope { \
  3. public: \
  4. Scope(_class& _hdl):_ObjectPtr(&_hdl) {}; \
  5. ~Scope() {_ObjectPtr->Free();}; \
  6. private: \
  7. _class* _ObjectPtr; \
  8. };

ProSelection 的封装 ptkSelection为例,如下:

  1. class ptkSelection :
  2. public _UserControlObject<ProSelection>
  3. {
  4. public:
  5. _UserControlObjectStructor(Selection);
  6. _ScopeClassCrt(ptkSelection);
  7. public:
  8. void Free();
  9. };

在类 ptkSelection 中,使用宏 _ScopeClassCrt(ptkSelection) ,它将会在类 ptkSelection中定义一个 ptkSelection::``Scope 类,用 Scope 类定义的对象在析构时,将会调用 ptkSelectionFree函数,来释放内存。

Scope使用方法:

1.标准使用方法

  1. ptkSelection selection;
  2. ptkSelection::Scope selection_scope(selection);

这样就可以在 data_scope 析构时,将 selection 中的数据 Free 掉。

2.结合 _DefineObjectHasScope 来使用:

  1. _DefineObjectHasScope(ptkSelection,selection);

_DefineObjectHasScope 宏将会与上面的标准使用方法达到相同的效果。

3.如果代码需要调整,不需“生命域”要释放内存,可以直接将上诉定义方法改为:

  1. _DefineObjectNoScope(ptkSelection,selection);

降低代码变更带来的代码变更量。

_DefineObjectHasScope_DefineObjectNoScope

请参照 宏 _ScopeClassCrt 的使用

_DefineObjectHasScope

  1. #define _DefineObjectHasScope(class_name, obj_inst) \
  2. class_name obj_inst; \
  3. class_name::Scope scope_##obj_inst(obj_inst);

_DefineObjectNoScope

  1. #define _DefineObjectNoScope(class_name, obj_inst) class_name obj_inst;