在我的C++项目(我使用自动工具)中,我有一个带有begin()
和end()
成员函数的类,并且我希望可选地包括cbegin()
和cend()
(仅当支持C++ 11时)。
我使用Autoconf存档中的M4文件测试了C++ 11支持。如果支持,它是一个定义HAVE_CXX11的宏,否则不定义它。
我想要的C方法是:
#ifdef HAVE_CXX11
const_iterator cbegin () const;
const_iterator cend () const;
#endif
但是我想用C++的方式做一些事情。在这种情况下,我可以使用
std::enable_if
选择性地允许cbegin
和cend
。像这样:#ifdef HAVE_CXX11
#define MY_HAVE_CXX11 true
#else
#define MY_HAVE_CXX11 false
constexpr bool have_cxx11 ()
{
return MY_HAVE_CXX11;
}
/* Now use have_cxx11() with std::enable_if */
这对于单个特定的宏可以正常工作,但是如果我想为任何给定的宏自动执行该怎么办?换句话说,我想要的是获得一个 bool(boolean) 值,该 bool(boolean) 值指示是否定义了给定的宏。
我看到两个选择:
示例1:
当autoconf定义其变量HAVE_CXX11时,还将定义have_cxx11()返回true或false,具体取决于HAVE_CXX11 autoconf变量(不是宏常量)是否定义为0或1。
示例2:
可以是函数
macro_is_defined()
,该函数返回 bool(boolean) 值,例如当我构建C++ 11项目时,macro_is_defined(HAVE_CXX11)
将返回true
。我试图找到一种在纯C++中实现这些想法的方法,但没有找到。我不得不将一行代码扩展到一块预处理器指令中,而IIRC是不可能的。我该怎么办?尝试像我尝试的那样以C++的方式做事是个好主意吗?还是太多了?
编辑:
autoheader为所有宏创建
#undef
,即使是最终被注释掉的宏也是如此。因此,我可以编写一个脚本来扫描config.h并为每个宏生成一个constexpr函数。问题是应该在构建过程中将其插入何处以及如何调用它。 最佳答案
如果您满意(我认为应该这样),则无法完成
纯粹以C++为目标,并且需要根据报价提供一些自定义构建支持,然后
也许您看到的是一种不存在的并发症。
我看不到您需要一个函数,每个HAVE_XXXX
-macro一个
或所有HAVE_XXXX
-macros之一,以告诉您给定的宏是否为
定义。我的意思是,我认为没有理由您应该首先考虑以下内容:
#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
-macros自动执行。您可以将其替换为带有以下内容的
.h
文件或.cpp
:#ifdef HAVE_XXXX
bool const have_XXXX = true;
#else
bool const have_XXXX = false;
#endif
在C++中,默认情况下
const
对象具有内部链接,因此即使在 header 中文件,这些定义在链接时不会有多个定义错误的风险。
鉴于此,定制的构建解决方案将执行脚本来解析
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
参见this answer
另一方面,我认为您在自己的做法上走错了路
实际上会使用
have_xxxx
做你想要的事情。在您已链接到this answer的评论中,
表示您将
enable-if
-SFINAE技术视为模型通过使其成为静态来启用或禁用成员函数
模板成员函数具有两种SFINAE替代方案:可以选择的一种
当
have_xxxx
为true时通过模板解析,然后选择当
have_xxxx
为false时。这实际上不是您需要的模型。它显示了如何
SFINAE是执行业务的一个实现之间的成员函数
适用时,每个模板参数和替代实现
那是无人值守。
但是你不希望你的程序什么都不做,如果
调用了静态禁用的成员函数。您想要这样的电话
导致编译错误。例如。你不想打电话给
T::cbegin()
当HAVE_CXX11
为false时禁止操作:您希望通话为编译时错误。
因此,您不需要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
并重新生成它即可。构建失败:
即使该程序仍然不调用
X::b
。你不能建立除非定义了
HAVE_XXXX
,否则该程序完全没有。这里的问题是编译器总是可以
无需模板解析即可评估
have_xxxx
。确实如此;因此它总是会发现该错误。
为了防止这种情况,您需要
enable_if
的条件取决于该函数的模板参数,因此只能在
模板解析度,但仍然必须为true [false](如果已评估),
只要
have_cxxx
为true [false]。达到此目的的任何事情都会条件如下:
!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
火灾。这是您想要的结果。关于c++ - C++ constexpr函数来测试预处理器宏,我们在Stack Overflow上找到一个类似的问题:https://stackoverflow.com/questions/18048039/