Cómo hacer una typedef condicional en C ++

89

Estoy tratando de hacer algo como esto:

#include <iostream>
#include <random>

typedef int Integer;

#if sizeof(Integer) <= 4
    typedef std::mt19937     Engine;
#else
    typedef std::mt19937_64  Engine;
#endif

int main()
{
    std::cout << sizeof(Integer) << std::endl;
    return 0;
}

pero me sale este error:

error: missing binary operator before token "("

¿Cómo puedo hacer correctamente el typedef condicional?

Martin Drozdik
fuente
25
El preprocesador no sabe nada sobre sizeofotras construcciones de C ++. Es sin duda no sabe nada de las cosas que ha creado usted mismo con typedef, como que aun no se ha analizado todavía.
Lightness Races in Orbit
2
Puede usar enable_ifo conditionalpara definir condicionalmente typedefs, pero no puede usar preprocesador para eso.
Bartek Banachewicz
1
@LightnessRacesinOrbit: El preprocesamiento y la compilación están integrados en GCC, por lo que no solo no es seguro que el código de procesamiento del software no conozca las definiciones de tipo creadas por el usuario, sino que se sabe que es falso en el caso de GCC. La razón por la sizeofque no puede funcionar en condiciones de preprocesador es porque el lenguaje está definido de esa manera, no por cómo funciona una implementación.
Eric Postpischil
1
@LightnessRacesinOrbit: Las fases de traducción definen la sintaxis y la semántica, no el orden de procesamiento. Según C ++ 2011 (N3092) 2.2 [lex.phases] nota 11, "Las implementaciones deben comportarse como si estas fases separadas ocurrieran, aunque en la práctica diferentes fases pueden combinarse". Mi punto sobre GCC es relevante porque demuestra que su afirmación de que así es como funciona una implementación es incorrecta. En otras palabras, su comentario afirma que un método particular de implementación lo previene. Pero no es la implementación lo que evita esto ( podríamos hacerlo); es la definición del lenguaje.
Eric Postpischil
1
@Eric: No quise afirmar nada sobre las implementaciones en absoluto. Ciertamente no mencioné ninguno en particular. Mi comentario expresó un comportamiento que está sujeto a la regla como si, al igual que el tuyo. No creo que estemos realmente en desacuerdo en nada aquí; su abogacía lingüística bien podría haber venido directamente del espejo. :)
Lightness Races in Orbit

Respuestas:

139

Utilice la std::conditionalmetafunción de C ++ 11.

#include <type_traits>  //include this

typedef std::conditional<sizeof(int) <= 4,
                         std::mt19937,
                         std::mt19937_64>::type Engine;

Tenga en cuenta que si el tipo que utiliza sizeofes un parámetro de plantilla, por ejemplo T, debe utilizarlo typenamecomo:

typedef typename std::conditional<sizeof(T) <= 4, // T is template parameter
                                  std::mt19937,
                                  std::mt19937_64>::type Engine;

O hacer Enginedepender de Tcomo:

template<typename T>
using Engine = typename std::conditional<sizeof(T) <= 4, 
                                         std::mt19937,
                                         std::mt19937_64>::type;

Eso es flexible , porque ahora puedes usarlo como:

Engine<int>  engine1;
Engine<long> engine2;
Engine<T>    engine3; // where T could be template parameter!
Nawaz
fuente
4
+1 Minor nitpick: Verificar sizeof(int) <= 4tal vez no sea una forma muy portátil, ya que en una máquina Windows de 64 bits, el compilador GCC (MinGW) x64 da sizeof(int) = sizeof(long) = 4. Una mejor forma sería sizeof(void*) <= 4.
legends2k
@ legends2k: ¿Quieres decir Engine<void*> engine4;? ;-)
Nawaz
2
@Nawaz: Por supuesto que no :) Quise decir std::conditional<sizeof(void*) <= 4, std::mt19937, std::mt19937_64>en el primer fragmento de código.
legends2k
1
@ legends2k: ¿Por qué usarías eso si te lo he proporcionado Engine<void*>? : P
Nawaz
@Nawaz: Jaja ... eso es cierto. Sin embargo, pensé que el OP probablemente debería conocer el error al detectar la arquitectura en función del tamaño de un int:)
legends2k
35

Usando std::conditionalpuedes hacerlo así:

using Engine = std::conditional<sizeof(int) <= 4, 
                               std::mt19937, 
                               std::mt19937_64
                               >::type;

Si quieres hacer una typedef, también puedes hacerlo.

typedef std::conditional<sizeof(int) <= 4, 
                         std::mt19937, 
                         std::mt19937_64
                         >::type Engine
Rapptz
fuente
No hay necesidad de typenameesto
gx_
@gx_ Sí, acostumbrado a ponerlo ahí trabajando con plantillas, no con tipos concretos.
Rapptz
1
@LightnessRacesinOrbit Bueno, lo arreglé un poco.
Rapptz
5

Si no tiene C ++ 11 disponible (aunque parece que lo tiene si planea usarlo std::mt19937), entonces puede implementar lo mismo sin el soporte de C ++ 11 usando Boost Metaprogramming Library (MPL) . Aquí hay un ejemplo compilable:

#include <boost/mpl/if.hpp>
#include <iostream>
#include <typeinfo>

namespace mpl = boost::mpl;

struct foo { };
struct bar { };

int main()
{
    typedef mpl::if_c<sizeof(int) <= 4, foo, bar>::type Engine;

    Engine a;
    std::cout << typeid(a).name() << std::endl;
}

Esto imprime el nombre mutilado de fooen mi sistema, ya que intaquí hay 4 bytes.

Jason R
fuente
1
¿Por qué no usas en su if_clugar? Sería imprescindible más fácil de escribir (y entender): mpl::if_c<sizeof(int)<=4, foo, bar>::type. ¿No es así?
Nawaz
1
@Nawaz: De hecho, eso es mejor en varios sentidos. Me había olvidado mpl::if_c. Actualicé el ejemplo para usar ese enfoque en su lugar.
Jason R