做 C++模板与 VCL 类配合得很好吗?

发布于 2024-10-08 23:04:21 字数 630 浏览 0 评论 0原文

我正在尝试使用 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) /等)不要写。即使使用像上面所示的“空”混合也是如此。

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

I'm trying to use C++ Template 'mixins' to create some new VCL components with shared additional functionality. Example...

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

// ...additional methods

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

Used like this:

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

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

Now, everything compiles fine, and the mixin stuff seems to work - until I try and save the component to a stream using TStream->WriteComponent, where the inherited properties (eg TLabel.Width/Height/etc.) don't get written. This is even with a 'null' mixin like the one shown above.

My code works fine when just deriving classes directly from TForm, TEdit, etc - and the class is correctly registered with the streaming system.

如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。

扫码二维码加入Web技术交流群

发布评论

需要 登录 才能够评论, 你可以免费 注册 一个本站的账号。

评论(1

地狱即天堂 2024-10-15 23:04:21

快速/简单的答案是:不;在处理模板时,编译器不会生成正确的描述符来使流式处理工作。然而,由于这个问题之前已经出现过,所以我在封面下偷看以找出丢失的内容。我发现它已经快到了。这里有更多信息。

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

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

...,您将看到错误

Error E2242 test.cpp 53: __classid 需要 Delphi 样式类类型(即标记为 __declspec(delphiclass) 的类或派生自System::TObject) in function testing()"

这基本上表示编译器不认为该类型/类与 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) {};
};

所以现在编译器将类型视为 delphi 风格的类而无需 hack,我偷看了再多一点,发现您可能遇到的问题: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:现在我已经看到了下面的内容,我相信不需要太多的努力就能让其按预期工作,

干杯,

Bruneau

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

The quick/simple answer is: no; when dealing with a template, the compiler won't generate the proper descriptors to make streaming working. However, since this has come up before, I peeked under the cover to find out what's missing. And what I found is that it's almost there. So here's a little more information.

Upfront the compiler will never treat a template-based type as a Delphi. For example, do something like this:

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

... and you'll see the error

"Error E2242 test.cpp 53: __classid requires Delphi style class type (i.e. class marked __declspec(delphiclass) or derived from System::TObject) in function testing()"

This basically says the compiler does not consider this type/class as compatible with Delphi-classes [i.e. those that derive from TObject]. Internally there's just a flag on the symbol that says whether the type is delphi-compatible or not. And I noticed that I could trick the compiler into marking the type as delphi-style if I forced it to walk up the hierarchy.. which is something it has to do if I create an instance of the object. So, with this hack the error goes away:

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

But much nicer was actually to use the __declspec(delphiclass) directly on the template, as in:

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

So now that the compiler treats the type as a delphi-style class without hacks, I peeked a little more and found the issue you're probably running into: Delphi classes have the TTypeData.PropCount field - http://docwiki.embarcadero.com/VCL/en/TypInfo.TTypeData - which is a sum of the class' properties, including those of its base classes. Due to the way the various pieces of information are computed, the compiler writes out a '0' for that field when a template is involved:(

You can see this by printing out the PropCount, as in:

#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;
}

When run the above prints:

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

IOW, Mixin shows up with '0' published properties while its base type has 3:(

I suspect the streaming system relies on this count and that's why inherited properties are not being written out in your setup.

I considered tweaking the generated descriptors at runtime but since we write them to _TEXT it's bound to trigger DEP.

I'll look at the logic that computes the PropCount to see if there's some way to get it to compute the correct number. If time allows, please do open a QC for this: now that I've peek underneath, I believe it would not require much effort to get this working as expected.

Cheers,

Bruneau

PS: In my sample I even had the Mixin publish a property and the compiler generated the correct descriptor for that property; however, the total count was still zero.

~没有更多了~
我们使用 Cookies 和其他技术来定制您的体验包括您的登录状态等。通过阅读我们的 隐私政策 了解更多相关信息。 单击 接受 或继续使用网站,即表示您同意使用 Cookies 和您的相关数据。
原文