C++预处理器基于参数的条件扩展

C++ Preprocessor Conditional Expansion Based on Parameter

我有一个正在为我构建 class 的宏。如果 class 本身没有将 int 指定为其类型,我想提供一个采用 int 的构造函数。宏看起来像:

CLASS_DECLARE(NAME, TYPE)\
  class NAME { \
  public: NAME(const TYPE& x) : value(x) {}\
  private: TYPE value; };

我可以使用 boost 预处理器来接近手动打开和关闭此构造函数...

CLASS_DECLARE(NAME, TYPE)\
  class NAME { \
  public: NAME(const TYPE& x) : value(x) {}\
  BOOST_PP_EXPR_IF(0, NAME(const int& x) : value(static_cast<TYPE>(x)) {})\
  private: TYPE value; };

但是,我无法用条件替换宏中的 0。我想要这样的东西:

CLASS_DECLARE(NAME, TYPE)\
  class NAME { \
  public: NAME(const TYPE& x) : value(x) {}\
  BOOST_PP_EXPR_IF(BOOST_PP_NOT_EQUAL(TYPE, int), NAME(const int& x) : value(static_cast<TYPE>(x)) {})\
  private: TYPE value; };

然而,这扩展到一些没有帮助的东西:

    BOOST_PP_EXPR_IIF_BOOST_PP_BOOL_BOOST_PP_NOT_EQUAL_CHECK_BOOST_PP_NOT_EQUAL_int(0, 
BOOST_PP_NOT_EQUAL_int)(MyType(const int& x) : value(static_cast<int>(x)){};

环顾四周,BOOST_PP_NOT_EQUAL 似乎并不是用于此类比较的。 (我知道宏扩展问题,并构建了一些 "IMPL" 宏来尝试进一步扩展。不过,我认为这不是问题所在。)想法?

我可以通过降低 int 中显式声明的构造函数的优先级来解决这个问题。为此,我声明了一个简单的 class 可以从 int 构造并转换回 int 然后在构造函数中使用这个 class 而不是普通的 int :

struct int_wrapper {
    int value;
    operator int() const { return value; }
    int_wrapper(int x): value(x) {}
};

#define CLASS_DECLARE(NAME, TYPE)\
  class NAME { \
  public: \
    NAME(const TYPE& x) : value(x) {}\
    NAME(const int_wrapper& x) : value(static_cast<TYPE>(x)) {} \
  private: \
    TYPE value; \
  };

这允许

CLASS_DECLARE(cfloat, float)
CLASS_DECLARE(cint, int)

int main()
{
    cfloat f1(1.0);
    cfloat f2(1);
    cint i(2);
}

Live on Coliru

但请注意,如果您尝试传递自己的 class 并声明 operator int(),这将产生问题。您原来的方法可以处理这个问题(将 class 转换为 int 并调用 int 构造函数),而我的方法不会,因为编译器不允许两次用户定义的转换(到 int 然后到 int_wrapper).

我现在根本无法调用第二个构造函数,因为如果你有 static_cast<TYPE>(x),这意味着 int 应该可以转换为 TYPE 每个 TYPE 你使用,但是第一个构造函数就足够了。但是,如果您的第二个构造函数只是一个简化的示例,并且您实际上并没有直接将 TYPE 转换为 int,那么您可能会发现我的回答很有用。

如果其他人有问题,他们想要这种类型的专业化,我想 post 一个答案。如果您有一组有限的 types/strings 您想在编译时比较并了解它们,则此解决方案将起作用。下面是支持 int 和 uint8_t 的例子。特殊构造函数只会为非int类型编写。

#include <boost/preprocessor.hpp>

#define TYPE_IS_int 0
#define TYPE_IS_uint8_t 1

#define CLASS_DECLARE(NAME, TYPE)\
    class NAME {\
        public: NAME(const TYPE& x) : value(x) {}\
        BOOST_PP_EXPR_IF(BOOST_PP_CAT(TYPE_IS_, BOOST_PP_EXPAND(TYPE)), NAME(const int& x) : value(static_cast<TYPE>(x)) {})\
        private: TYPE value; };

CLASS_DECLARE(MyIntType, int);
CLASS_DECLARE(MyUint8Type, uint8_t);

宏扩展为:

class MyIntType
{ 
    public: 
        MyIntType(const int& x) : value(x) {}  
    private: 
        int value; 
};

class MyUint8Type 
{ 
    public: 
        MyUint8Type(const uint8_t& x) : value(x) {} 
        MyUint8Type(const int& x) : value(static_cast<uint8_t>(x)) {} 
    private: 
        uint8_t value; 
};