我有一个库,它注册了一堆元类型,如下代码所示:
abstractfoobase.h
namespace foo {
enum FooState { OK, MAYBE };
class AbstractFooBase : public QObject {...};
}
Q_DECLARE_METATYPE(foo::FooState)
还有一个对应的 abstractfoobase.cpp ,内容取决于在基类中实现的内容。
foousingclass.h:
namespace foo {
class FooUsingClass : public AbstractFooBase {...};
}
还有相应的 foousingclass.cpp ,它有方法实现等。
现在,Q_DECLARE_METATYPE
启用了Qt模板类和QVariant
的类型。要启用排队信号中的类型等,还需要进行相应的调用:
qRegisterMetaType<foo::FooState>();
qRegisterMetaType
来电的好地方是什么?我显然不希望从应用程序代码中进行任何明显的初始化调用。注册必须在执行foo::FooUsingClass *f = new foo::FooUsingClass();
之后发生。
在Java中,我将这种代码放在静态初始化块中。我也可以在C ++中看到几种方法来实现这一点,但是它们中没有一个看起来特别好看。例如,简单地将这些放到AbstractFooBase
构造函数中将导致每次创建子类实例时都会调用注册,这可能是不希望的开销。所以那些做过这个的人,你在哪里放了qRegisterMetaType
电话?
答案 0 :(得分:6)
我可以想象如下:
in abstractfoobase.cpp:
namespace foo {
/// Initializer
class FooStateInit {
public:
FooStateInit() {
qRegisterMetaType<foo::FooState>();
}
};
static FooStateInit fooStateInit;
} // namespace foo
答案 1 :(得分:5)
我最近发现了一个简单的解决方案,它也适用于静态库。
它充分利用了在Qt的元类型系统中使用qMetaTypeId<>()
的事实。因此,通过显式模板实例化,我们可以强制实现与 abstractfoobase.cpp 的链接(否则链接器可能决定是否没有引用的符号)并确保在程序启动时静态注册该类型:
abstractfoobase.h
#include <QMetaType>
namespace foo {
enum FooState { OK, MAYBE };
}
Q_DECLARE_METATYPE(foo::FooState)
extern template int qMetaTypeId<foo::FooState>();
abstractfoobase.cpp
static const int kFooStateMetaTypeId = qRegisterMetaType<foo::FooState>();
template int qMetaTypeId<foo::FooState>();
答案 2 :(得分:1)
在我创建的几个应用程序中,我曾经只是在main()
中注册它们 - 就像定义应用程序启动的函数一样。当然,这将注册与.cpp / .h文件分开,但反过来它保证我不会陷入某些名称冲突(我在一个地方看到所有名称/类型)并且还保证注册完成恰好一次:app startup;)
但是,这不是......'方便'的方式......如果有人有更好的方法,我也希望看到它。
答案 3 :(得分:1)
如果您的类型将被频繁实例化,则可以在构造函数中使用函数静态初始化:
var file = model.UserAvatarImage as IFormFile;
string fileContent = null;
using (var reader = new StreamReader (file.OpenReadStream ())) {
fileContent = reader.ReadToEnd ();
}
var result = JsonConvert.DeserializeObject<IFormFile> (fileContent);
优点:
缺点: