C++宏在子类未使用另一个宏时失败

C++ macro to fail when a subclass hasn't used another macro

本文关键字:失败 另一个 子类 C++ 未使用      更新时间:2023-10-16

我有一个在x.h中声明并在x.cpp中定义的类X,它必须在静态初始化阶段使用宏INIT(X)运行一些代码(在集中位置注册一些类元数据)。X的任何子类Yy.h中的声明,包括x.hy.cpp中的定义)也是如此——它必须在全局范围内运行INIT(Y)。现在,如果每个子类都已初始化,我想创建一个静态检查。此外,我不知道我将链接多少X子类。

我想在x.h中定义一个宏,如果存在未调用INIT(SubClass)XSubClass(或X的任何其他后代),该宏将生成编译器错误。怎么做?

要求:

  • C++11
  • 如果需要,可以要求在subclass.cpp文件中调用它
  • 可以要求CCD_ 18在CCD_
  • 我想要支持的编译器至少是gccmsvc
  • 它不应该在导入x.hsomeotherclass.cpp中生成错误,除非它定义了X的子类
  • 错误可以是任何类型的编译器错误,不一定必须是#error,例如未定义的变量也可以
  • 此宏的代码可能需要对X类进行额外的更改,但不需要对其任何子类进行更改
  • INIT放在subclass.cpp的任何位置都必须工作

如果我不需要修改X的任何子类来将其声明和定义放在INIT中,那么在X的新基类中定义一个伪抽象方法就可以了。

下面是该设计的模板代码,其中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("%dn", someGlobalInt()); // should print "2"
    return 0;
}

正如注释中提到的n.m.,您可以使用Curioly Recurring Template Pattern。你可以这样做,我认为这比摆弄这些lambdas更清楚:

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>是不同的类。实际工作是在我们不断生成的新基类中完成的。现在,基类将始终尝试调用其派生类的static reserved_init成员。因此,如果未定义此成员,则会出现错误。因此,您需要将宏放在类定义中。

和往常一样,如果用户真的想,他们可以绕过这一点。例如,它们可以定义另一个具有相同名称的函数。最终,我不认为你可以创建一个故意防止滥用的系统,但它应该针对诚实的错误。让我知道你对这个解决方案的看法,也许我可以引入修改来修复它。