【问题标题】:C++ Preprocessor Conditional Expansion Based on ParameterC++预处理器基于参数的条件扩展
【发布时间】:2016-03-30 23:46:07
【问题描述】:

我有一个宏正在为我构建一个类。如果类本身没有将 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”宏来尝试进一步扩展。但是我不认为这是这里的问题。)想法?

【问题讨论】:

  • 您的确切问题是否只是模棱两可的构造函数调用?为什么不使用模板?
  • 除非您至少有一组有限的类型,否则我认为您不会在预处理器中比较类型名称。不过,您可以将其交给编译步骤而不是预处理步骤。
  • 是的。我不能在这里使用模板,因为其他模板化函数和类需要使用这些类。然后,这将对#include 施加顺序依赖。一团糟。那是我的第一次尝试,实际上。 MSVC 很高兴,但 GCC 和 Clang 的抱怨是正确的。
  • @chris 我也尝试使用 std::enable_if<:is_same>

标签: c++ macros metaprogramming c-preprocessor boost-preprocessor


【解决方案1】:

我能够通过使来自int 的显式声明的构造函数不那么受欢迎来解决这个问题。为此,我声明了一个简单的类,它可以从int 构造并转换回int,然后在构造函数中使用这个类而不是普通的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

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

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

【讨论】:

  • 谢谢。这完美地工作。欣赏解决问题的不同角度!
  • 一些测试用例表明我太着急了,无法解决问题。我会继续寻找解决方案。
  • @DiB,你也可以评论我答案的最后一段吗?因为使用你的 static_cast,我想不出需要构造函数接受 int 的情况
  • 我清理了我的构造函数和赋值运算符,删除了一些不必要的重载和静态强制转换。查看这个简单的示例有助于简化实际代码。解决方案似乎是不需要花哨的宏魔法,实际上减少了重载。
【解决方案2】:

如果其他人有任何他们想要这种专业化的问题,我想发布一个答案。如果您有一组有限的类型/字符串要比较并在编译时了解它们,则此解决方案将起作用。这是支持 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; 
};

【讨论】:

    猜你喜欢
    • 2014-04-02
    • 2020-09-18
    • 1970-01-01
    • 2018-01-04
    • 2012-07-22
    • 1970-01-01
    • 1970-01-01
    • 2020-10-18
    相关资源
    最近更新 更多