2014-10-06 53 views
1

我有一个类X,在x.h中声明并在x.cpp中定义,它必须在静态初始化阶段运行一些代码(在集中位置注册一些类的元数据)宏INIT(X)。对于X的任何子类Y(在y.h中的声明,包括x.h,在y.cpp中的定义)都是相同的 - 它必须在全局范围内运行INIT(Y)。现在我想创建一个静态检查,如果每个子类已经初始化。另外我不知道我会链接多少个X子类。当子类没有使用另一个宏时,C++宏将失败

我要定义x.h宏如果存在的X一个​​(或X任何其他后裔),将产生一个编译器错误已经不叫INIT(SubClass)。怎么做?

要求:

  • C++ 11。
  • 如果需要,可能需要在subclass.cpp文件中调用它。
  • 可能需要​​将其全部定义在subclass.cpp中。
  • 我想支持的编译器至少有gccmsvc
  • 它应该不会在导入x.hsomeotherclass.cpp中生成错误,除非它定义了X的子类。
  • 该错误可能是任何种类的编译器错误,它不一定是#error,例如,未定义的变量也很好。
  • 此宏的代码可能需要对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; 
} 
+0

fyi,根据标准项目以双下划线开头保留。 '17.6.3.3.2全局名称[global.names]' ' - 包含双下划线_ _的每个名称或以下划线开头并带有大写字母(2.12)的字符保留给实现以供任何用途。' – lcs 2014-10-06 18:19:35

+0

感谢您的信息,我会改变它。但主要问题仍然存在。 – Xilexio 2014-10-06 19:43:14

+0

@Xilexio我现在没有时间,但如果今晚晚些时候这个问题仍然没有答案,我会尽力为你提供一个解决方案。 – Serge 2014-10-06 19:52:12

回答

1

由于N.M.在评论中提到,您可以使用奇怪的循环模板模式。你可以做这样的事情,我认为这是不是与这些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>属于不同类别。实际的工作是在我们继续产卵的新基类中完成的。现在,基类将总是试图调用它的派生类的静态reserved_init成员。所以如果这个成员没有定义,你会得到一个错误。因此,您需要将该宏放入类定义中。

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

+0

但是这并不强制你从'Y'中的'XInit '继承,是吗?如果用户不知道'XInit'和'INIT',只是不加它,编译器就会很好地编译它。然后它没有达到它的目的 - 提醒开发者关于'INIT'或'XInit '的子类。如果你只记得这个事实,我已经有了可行的代码(尽管我同意你有比我更清洁的解决方案)。 – Xilexio 2014-10-07 18:17:08

+0

我需要这个功能。提到:“强制ABC的每个派生类别做一次XYZ” - “力量”部分仍然缺失。 – Xilexio 2014-10-07 18:24:28

+1

我的意思是,我设置它的方式,它强制注册XInit的所有孩子。如果你想强制X的所有孩子,那么完全摆脱XInit并在X内部实现它的功能。然后X将是一个模板类,当你从X继承时,你将需要在派生类型上模板X 。 – 2014-10-07 18:26:11