Search code examples
c++macrosmetaprogrammingc-preprocessorboost-preprocessor

C++ Preprocessor Conditional Expansion Based on Parameter


I have a macro that is building a class for me. I want to provide a constructor which takes an int if the class itself does not have an int specified as its type. The macro looks something like:

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

I can come close using the boost preprocessor to manually turn this constructor on and off...

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; };

However, I cannot replace the 0 in the macro with a conditional. I want something like:

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; };

However, that expands to something less than helpful:

    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)){};

Looking around, it doesn't seem as though BOOST_PP_NOT_EQUAL is intended for this type of comparison. (I am aware of macro expansion issues and have build some "IMPL" macros to try to get things expanded out further. I don't think that is the problem here, however.) Thoughts?


Solution

  • In case anyone else has an issue where they would like this type of specialization, I wanted to post an answer. This solution will work if you have a finite set of types/strings you want to compare and know them at compile time. Here is the example that supports int and uint8_t. The special constructor will only be written for the non-int type.

    #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);
    

    The macro expands to:

    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; 
    };