1

У меня есть макрос, который создает для меня класс. Я хочу предоставить конструктор, который принимает int, если сам класс не имеет int, указанный в его типе. Макрос выглядит что-то вроде:Условное расширение препроцессора C++ на основе параметра

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

Я могу приблизиться с помощью наддува препроцессор вручную превратить этот конструктор и выключается ...

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

Однако, я не могу заменить в макросе с условным. Я хочу что-то вроде:

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», чтобы попытаться расширить возможности. Однако я не думаю, что это проблема.) Мысли?

+0

Ваша точная проблема просто неоднозначные вызовы конструктора? И почему бы не использовать шаблоны? – Petr

+1

Я не думаю, что вам удастся сравнить имена типов в препроцессоре, если у вас по крайней мере нет конечного набора типов. Однако вы можете передать это на этап компиляции, а не на этапе предварительной обработки. – chris

+0

Это так. Я не могу использовать шаблоны здесь из-за других шаблонных функций и классов, которые должны использовать эти классы. Тогда это наложило бы зависимость порядка от # include. Это беспорядок. На самом деле это была моя первая попытка. MSVC был счастлив, но GCC и Clang справедливо жаловались. – DiB

ответ

1

В случае, если у кого-то еще есть проблема, где они хотели бы эту специализацию, я хотел бы опубликовать ответ. Это решение будет работать, если у вас есть конечный набор типов/строк, которые вы хотите сравнить, и знать их во время компиляции. Вот пример, который поддерживает int и uint8_t. Специальный конструктор будет написан только для типа non-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; 
}; 
0

Мне удалось обойти это, сделав явно выраженный конструктор от 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<TYPE>(x), это означает, что int должны быть конвертированы в TYPE для каждого TYPE вы используете, но тогда первый конструктор достаточно. Однако, если ваш второй конструктор является просто упрощенным примером, и вы фактически не набрасываете TYPE на int, тогда вы можете найти мой ответ полезным.

+0

Спасибо. Это прекрасно работает. Оцените разный угол, чтобы решить проблему! – DiB

+0

@DiB, см. Мое редактирование – Petr

+0

Некоторые тестовые примеры показали, что я слишком беспокоюсь о проблеме, которую нужно решить. Я продолжу искать решение. – DiB

Смежные вопросы