我正在尝试使用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等派生类时,我的代码工作正常 - 并且该类已在流系统中正确注册。
答案 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发布了一个属性,编译器为该属性生成了正确的描述符;但是,总数还是零。