autoheader 为所有宏创建#undefs,即使是那些最终被注释掉的宏。所以我可以编写一个脚本来扫描 config.h 并为每个宏生成一个 constexpr 函数。问题是它应该在构建过程中插入到哪里以及如何调用它。
如果您满意(我认为您应该满意)您无法纯粹在 C++ 中实现目标,并且需要根据报价提供一些自定义构建支持,那么您可能会看到一个不存在的复杂情况。
我不认为您需要一个函数,无论是每个HAVE_XXXX
宏一个或一个所有HAVE_XXXX
宏,都可以告诉您是否定义了给定的宏。我的意思是我看不出你为什么应该从思考如何开始:
#ifdef HAVE_XXXX
#define MY_HAVE_XXXX true
#else
#define MY_HAVE_XXXX false
#endif
constexpr bool have_xxxx ()
{
return MY_HAVE_XXXX;
}
可以为所有HAVE_XXXX
宏自动化。您可以在.h
文件中或将其替换.cpp
为:
#ifdef HAVE_XXXX
bool const have_XXXX = true;
#else
bool const have_XXXX = false;
#endif
在 C++ 中,const
对象默认具有内部链接,因此即使在头文件中,这些定义也不会在链接时出现多个定义错误的风险。
鉴于此,定制的构建解决方案将执行一个脚本来解析
config.h
并写出一个头文件,比如config_aux.h
包含config.h
和包含:
#ifdef HAVE_XXXX
bool const have_xxxx = true;
#else
bool const have_xxxx = false;
#endif
对于每个HAVE_XXXX
.
您确保将config_aux.h
其构建为其他所有内容的先决条件,然后对于您的所有编译,您确保config_aux.h
编译器将其预先包含在每个翻译单元中。这样做的方法是通过 g++ 选项:
-include config_aux.h
看到这个答案
另一方面,我认为你在如何实际使用have_xxxx
来做你想做的事情方面走错了路。
在您已链接到此答案的评论中,表明您将enable-if
-SFINAE 技术视为静态启用或禁用成员函数的模型,方法是使其成为具有两个 SFINAE 替代方案的模板成员函数:一个将由模板解析选择什么时候have_xxxx
是真的,什么时候会被选择的那个have_xxxx
是假的。
这实际上不是您要求的模型。它展示了如何在
每个模板参数适用时执行业务的一个实现与无操作的替代实现之间 SFINAE 成员函数。
但是,如果调用了静态禁用的成员函数,您不希望您的程序什么都不做。您希望这样的调用
导致编译错误。例如,当为 false 时,您不希望调用T::cbegin()
成为无操作HAVE_CXX11
:您希望调用成为编译时错误。
所以你不需要SFINAE,但你确实需要成员函数成为模板成员函数,因为模板解析是在同一个类中静态启用或禁用它的唯一机制(不包括旧#ifdef
方式)。
以下程序似乎说明了明显的解决方案:
#include <type_traits>
#define HAVE_XXXX 1 // Pretend we get this from autoconf
// Pretend we get this from config_aux.h
#ifdef HAVE_XXXX
bool const have_xxxx = true;
#else
bool const have_xxxx = false;
#endif
struct X
{
void a(){}
template<typename R = typename std::enable_if<have_xxxx,void>::type>
R b() {}
};
int main()
{
X x;
// x.b();
return 0;
}
HAVE_XXXX
定义后编译,同时实现X::a
和
X::b
。它可以对X::b
. 但是如果HAVE_XXXX
没有定义,那么任何调用X::b
都需要实例化该成员函数,std::enable_if<false,void>
并且不会编译。
HAVE_XXXX
但只需编辑程序以便未定义并重建它。
构建失败:
错误:“struct std::enable_if”中的“type”未命名类型
即使程序仍然没有调用X::b
. HAVE_XXXX
除非已定义,否则您根本无法构建程序。
这里的问题是编译器总是可以have_xxxx
在不求助于模板解析的情况下进行评估。确实如此;所以它总是会发现那个错误。
为了防止这种情况,您需要 的条件enable_if
取决于函数的模板参数,因此它只会在模板解析中被评估,但仍然始终为真 [假],如果它被评估,只要have_cxxx
为真[错误的]。任何具有这种效果的东西都会起作用,并且条件如下:
!std::is_same<R,R>::value || have_xxxx
可能会想到。但:
template<
typename R =
typename std::enable_if<(!std::is_same<R,R>::value || have_xxxx),void>::type
>
R b() {}
不会以任何方式编译,因为它尝试使用模板参数R
来定义自身的默认类型。
但是,就像std::enable_if
这个无关紧要的障碍一样,为什么要求助于它呢?一个static_assert
合适的条件在体内的X::b
意志做得很好。在诊断上,它会做得更好。所以改为定义X::b
如下:
template<typename R = void>
R b() {
static_assert(!std::is_same<R,R>::value || have_xxxx,
"X::b is unimplemented without XXXX support");
}
现在,程序将编译是否HAVE_XXXX
已定义,当它被定义时,您还可以取消注释对X::b
. 但是如果HAVE_XXXX
未定义,并且您还取消了对 的调用的注释X::b
,则成员函数将被实例化;R 被定义;的条件
static_assert
被评估,发现错误,然后static_assert
火灾。这就是你想要的结果。