我有一个类 X
在 x.h
中声明并在 x.cpp
中定义,它必须运行一些代码(在中注册一些类元数据一个集中的地方)在静态初始化阶段使用宏 INIT(X)
。对于任何子类 Y
(在 y.h
中声明,包括 x.h
,在 y.cpp
中定义)也是如此X
- 它必须在全局范围内运行 INIT(Y)
。现在我想创建一个静态检查是否每个子类都已初始化。此外,我不知道我将链接多少个 X
子类。
我想在 x.h
中定义一个宏,如果存在 X
(或任何其他后代)的 SubClass
,它将生成编译器错误尚未调用 INIT(SubClass)
的 X
)。怎么做?
要求:
- C++11。
- 如果需要可以要求在
subclass.cpp
文件中调用。 - 可以要求
SubClass
在subclass.cpp
中有完整的定义。 - 我想支持的编译器至少是
gcc
和msvc
。 - 它不应在导入
x.h
的someotherclass.cpp
中生成错误,除非它定义了X
的子类。 - 错误可能是任何类型的编译器错误,不一定是
#error
,例如 undefined variable 也可以。 - 此宏的代码可能需要在
X
类中进行额外更改,但不需要在其任何子类中更改。 INIT
放在subclass.cpp
中的任何地方都必须在所有包含之后工作。
在 X
的新基类中定义一个虚拟抽象方法是可行的,前提是我不需要不修改任何 X
的子类将它的声明放在那里并在 INIT
中定义。
下面是该设计的模板代码,其中 INIT
仅用于计算链接的 X
子类的数量 + 1。/*???*/
可以用任何东西代替,只要它有效。
x.h
:
#include <functional>
int &someGlobalInt();
class XInit {
public:
XInit(std::function<void ()> init) {
init();
}
};
#define INIT(cls) static XInit X_INIT_ ## cls = XInit([](){ \
++someGlobalInt(); \
/*???*/ \
})
class X {
/*???*/
};
/*???*/
x.cpp
:
#include "x.h"
int &someGlobalInt() {
static int x = 0;
return x;
}
INIT(X); // error without it
y.h
:
#include "x.h"
class Y: public X {};
y.cpp
:
#include "y.h"
INIT(Y); // error without it
main.cpp
:
#include <cstdio>
#include "x.h"
// no error, since no new X subclass is defined
int main() {
printf("%d\n", someGlobalInt()); // should print "2"
return 0;
}
最佳答案
作为 n.m.评论中提到,您可以使用 Curiously Recurring Template Pattern。你可以做这样的事情,我认为这比乱用这些 lambda 更清楚:
XInit.h
template <class T>
class XInit {
private:
static bool initialized;
static std::once_flag flag;
static void base_init<T>(){ std::call_once(flag, T::reserved_init);}
...
}
XInit.cpp
template <class T>
XInit<T>::initialized = XInit<T>::base_init();
现在您可以将宏定义为:
#define INIT(cls) static void reserved_init() {++someGlobalInt();}
现在您继承:
class X : public XInit<X> ...
请注意,如果您需要层次结构,没关系,只需执行以下操作:
class Y: public X, public XInit<Y>
注意这里没有任何多重继承,因为XInit<X>
和 XInit<Y>
是不同的类(class)。实际工作是在我们不断产生的新基类中完成的。现在,基类将始终尝试调用它的派生类的静态 reserved_init 成员。因此,如果未定义此成员,则会出现错误。因此,您需要将宏放在类定义中。
一如既往,如果用户真的想要,他们可以绕过这个问题。例如,他们可以定义另一个具有相同名称的函数。最后,我不认为你可以创建一个系统来防止故意滥用,但它应该可以防止无心之失。让我知道您对此解决方案的看法,也许我可以引入修改来修复它。
关于当子类没有使用另一个宏时 C++ 宏失败,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/26221482/