【问题标题】:Do C++ Templates play nicely with VCL classes?C++ 模板能很好地与 VCL 类配合使用吗?
【发布时间】:2011-05-28 12:15:30
【问题描述】:

我正在尝试使用 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->WriteComponent 将组件保存到流中,其中继承的属性(例如 TLabel.Width/Height/etc.)不写不出来。即使是上面显示的“空”mixin 也是如此。

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

【问题讨论】:

    标签: delphi templates c++builder vcl


    【解决方案1】:

    快速/简单的答案是:不;在处理模板时,编译器不会生成正确的描述符来使流式传输工作。但是,由于以前出现过这种情况,因此我在封面下偷看以找出缺少的内容。我发现它几乎就在那里。所以这里有更多信息。

    预先编译器永远不会将基于模板的类型视为 Delphi。例如,执行以下操作:

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

    ...你会看到错误

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

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

    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) {};
    };
    

    既然编译器将类型视为没有 hack 的 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 的逻辑,看看是否有某种方法可以让它计算出正确的数字。如果时间允许,请为此打开一个 QC:现在我已经窥视了下面的内容,我相信它不需要太多努力就可以按预期工作。

    干杯,

    布鲁诺

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

    【讨论】:

    猜你喜欢
    • 2010-12-13
    • 1970-01-01
    • 2013-04-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多