J'ai une macro qui construit une classe pour moi. Je veux fournir un constructeur qui prend un int si la classe elle-même n'a pas un int spécifié comme type. La macro ressemble à quelque chose comme:

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

Je peux me rapprocher en utilisant le préprocesseur boost pour manuellement activer et désactiver ce constructeur ...

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

Cependant, je ne peux pas remplacer le 0 dans la macro par un conditionnel. Je veux quelque chose comme:

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

Cependant, cela devient quelque chose de moins qu'utile:

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

En regardant autour de vous, il ne semble pas que BOOST_PP_NOT_EQUAL soit destiné à ce type de comparaison. (Je suis conscient des problèmes d'expansion des macros et j'ai construit des macros "IMPL" pour essayer d'élargir davantage les choses. Je ne pense pas que ce soit le problème ici, cependant.) Réflexions?

2
DiB 24 déc. 2015 à 17:35

2 réponses

Meilleure réponse

Au cas où quelqu'un d'autre aurait un problème où il aimerait ce type de spécialisation, je voulais publier une réponse. Cette solution fonctionnera si vous avez un ensemble fini de types / chaînes que vous souhaitez comparer et les connaître au moment de la compilation. Voici l'exemple qui prend en charge int et uint8_t. Le constructeur spécial ne sera écrit que pour le type 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);

La macro se développe pour:

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; 
};
1
DiB 24 déc. 2015 à 19:13

J'ai pu contourner ce problème en rendant le constructeur explicitement déclaré de int moins préféré. Pour cela, je déclare une classe simple qui peut être construite à partir de int et reconvertie en int, puis j'utilise cette classe au lieu de plain int dans le constructeur:

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

Cela permet

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

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

En direct sur Coliru

Notez cependant que cela créera un problème si vous essayez de passer votre propre cours avec operator int() déclaré. Votre approche d'origine pourrait gérer cela (convertir la classe en int et appeler le constructeur int), alors que mon approche ne le fera pas, car le compilateur n'autorisera pas deux conversions définies par l'utilisateur (en int puis à int_wrapper).

De plus, je ne peux pas du tout appeler le deuxième constructeur, car si vous avez static_cast<TYPE>(x), cela signifie que int devrait être convertible en TYPE pour chaque TYPE que vous utilisez, mais alors le premier constructeur suffit. Cependant, si votre deuxième constructeur n'est qu'un exemple simplifié et que vous ne transtypez pas directement TYPE en int, alors vous pourriez trouver ma réponse utile.

0
Petr 24 déc. 2015 à 15:10