C ++模板可以很好地与VCL类一起使用吗?

时间:2010-12-20 21:38:17

标签: delphi templates c++builder vcl

我正在尝试使用C ++模板'mixins'来创建一些具有共享附加功能的新VCL组件。实施例...

template <class T> class Mixin : public T
{
private:
  typedef T inherited;

// ...additional methods

public:
  Mixin(TComponent *owner) : inherited(owner)
  {
  // .. do stuff here
  };
};

像这样使用:

class MyLabel : public Mixin<TLabel>
{
  ....
}

class MyEdit : public Mixin<TEdit>
{
  ....
}

现在,所有内容编译都很好,mixin的东西似乎也有效 - 直到我尝试使用TStream-&gt; WriteComponent将组件保存到流中,其中继承的属性(例如TLabel.Width / Height / etc。)写不出来。即使使用如上所示的'null'mixin也是如此。

直接从TForm,TEdit等派生类时,我的代码工作正常 - 并且该类已在流系统中正确注册。

1 个答案:

答案 0 :(得分:8)

快速/简单的答案是:不;在处理模板时,编译器不会生成适当的描述符来使流工作。然而,由于之前出现了这个问题,我偷看了封面,找出了遗漏的内容。而我发现它几乎就在那里。所以这里有更多的信息。

Upfront编译器永远不会将基于模板的类型视为Delphi。例如,做这样的事情:

void testing()
{
  __classid(Mixin<Stdctrls::TLabel>); // Error Here
}

...你会看到错误

错误 E2242 test.cpp 53:__ classid在函数testing()中需要Delphi样式类类型(即类标记为__declspec(delphiclass)或派生自System :: TObject)”

这基本上说编译器不认为这个类型/类与Delphi类兼容[即那些派生自TObject的人。在内部,符号上只有一个标志,表示该类型是否与delphi兼容。我注意到,如果我强迫它走向层次结构,我可以欺骗编译器将类型标记为delphi样式。如果我创建了一个对象的实例,它就必须这样做。所以,有了这个黑客,错误消失了:

void testing()
{
  typedef Mixin<Stdctrls::TLabel> __ttype;
  std::auto_ptr<__ttype> c2(new __ttype(0));
  __classid(Mixin<Stdctrls::TLabel>); // No more errors here
}

但更好的是直接在模板上使用__declspec(delphiclass),如:

template <class T> 
class __declspec(delphiclass) Mixin : public T {
private:
  int i;
  typedef T inherited;
public:
  __fastcall Mixin(TComponent *owner) : inherited(owner) {};
};

现在,编译器将类型视为没有黑客的delphi样式类,我稍微瞥了一眼,发现你可能遇到的问题:Delphi类有TTypeData.PropCount字段 - http://docwiki.embarcadero.com/VCL/en/TypInfo.TTypeData - 这是类的属性的总和,包括其基类的属性。由于计算各种信息的方式,编译器在涉及模板时为该字段写出“0”:(

您可以通过打印出PropCount来看到这一点,如:

#include <Stdctrls.hpp>
#include <cstdio>
#include <memory>
#include <utilcls.h>

class TCppComp : public Classes::TComponent {
  int i;
public:
  __fastcall TCppComp(TComponent* owner): Classes::TComponent(owner) {};
__published:
  __property int AAAA = {read=i, write=i};
};

template <class T> 
class __declspec(delphiclass) Mixin : public T {
private:
  int i;
  typedef T inherited;
public:
  __fastcall Mixin(TComponent *owner) : inherited(owner) {};
};

typedef Mixin<TCppComp> TMixinComp;

void showProps(TClass meta) {
  PTypeInfo pInfo = PTypeInfo(meta->ClassInfo());
  int Count = GetPropList(pInfo, tkAny, NULL);
  TAPtr<PPropInfo> List(new PPropInfo[Count]);
  std::printf("Class: %s - Total Props:%d\n", 
                   AnsiString(pInfo->Name).c_str(), Count);  
  GetPropList(pInfo, tkAny, *(reinterpret_cast<PPropList*>(&List)));
  for (int i = 0; i < Count; i++) {
    AnsiString propName(List[i]->Name);
    std::printf("\t%s\n", propName.c_str());
  }
}

void test() {
  showProps(__classid(TCppComp));
  showProps(__classid(TMixinComp));
}

int main() {
  test();
  return 0;
}

运行以上打印时:

  Class: TCppComp - Total Props:3
    AAAA
    Name
    Tag
  Class: @%Mixin$8TCppComp% - Total Props:0

IOW,Mixin显示“0”已发布属性,而其基本类型为3 :(

我怀疑流媒体系统依赖于此计数,这就是为什么未在设置中写出继承属性的原因。

我考虑过在运行时调整生成的描述符,但是因为我们将它们写入_TEXT,它必然会触发DEP。

我将查看计算PropCount的逻辑,看看是否有某种方法可以让它计算出正确的数字。如果时间允许,请为此打开一个质量控制:现在我已经在下面查看,我相信不需要太多努力就可以按预期工作。

干杯,

布诺

PS:在我的示例中,我甚至让Mixin发布了一个属性,编译器为该属性生成了正确的描述符;但是,总数还是零。