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);
}
但请注意,如果您尝试传递自己的 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;
};
我有一个正在为我构建 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);
}
但请注意,如果您尝试传递自己的 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;
};