在我的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选择性地允许cbegincend。像这样:
#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.hconfig.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::aX::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/

    10-11 22:55
    查看更多