2010-11-28 5 views
19

Estoy tratando de compilar la siguiente pieza de código, me sale un error en la línea que se especializa en std :: vector, parece que se está asumiendo de alguna manera el parámetro que se pasa ser dos parámetros ¿Es acaso algo relacionado con los corchetes angulares?Problema con los parámetros de la plantilla utilizados en las macros

¿Hay alguna manera/mecanismo especial en el que dichos parámetros se puedan pasar correctamente a la macro?

#include <vector> 

template<typename A> 
struct AClass {}; 

#define specialize_AClass(X)\ 
template<> struct AClass<X> { X a; }; 


specialize_AClass(int) //ok 

specialize_AClass(std::vector<int,std::allocator<int> >) //error 

int main() 
{ 
    return 0; 
} 

El error que consigo es el siguiente:

1 Line 55: error: macro "specialize_AClass" passed 2 arguments, but takes just 1 
2 Line 15: error: expected constructor, destructor, or type conversion before 'int' 
3 compilation terminated due to -Wfatal-errors. 

Enlace: http://codepad.org/qIiKsw4l

Respuesta

2

Desde el preprocesador se ejecuta antes del análisis semántico, la coma en el parámetro de plantilla se interpreta como argumento separador para la macro. En su lugar, usted debe ser capaz de usar macros variadic a hacer algo como esto:

#define specialize_AClass(...)\ 
template<> struct AClass<__VA_ARGS__> { X a; }; 
+0

Tenga en cuenta que las macros variadic no son en realidad parte de el lenguaje C++ anterior a C++ 0x. –

+0

Es cierto, pero son parte del estándar C99, y la mayoría de los compiladores los admiten cuando compilan fuera de cualquier modo pedante. –

-1

Hay un montón de otros problemas con su código, pero para hacer frente a la cuestión específica, el preprocesador simplemente trata a < y > como menos- que y más que los operadores.

Ese es el alcance de su conocimiento sobre C++.

Hay algunos trucos que se pueden utilizar para permitir expresiones de plantilla que se pasan como argumentos de la macro, pero la simple y por un margen muy grande mejor respuesta para un principiante es:

No hagas eso .

Saludos & HTH.,

+1

@downvoter: indique siempre el motivo de su voto a la baja para que otros puedan saber qué sucede con las respuestas que votó negativamente, o por qué sus votos negativos son tontos. –

+2

Abajo porque (a) "NO HACER ESO" no es útil cuando hay razones legítimas para hacerlo y cuando no se brinda ninguna alternativa (b) Hay buenos trucos para hacer eso, especificado por Johannes arriba. – Jon

+0

@Jon: gracias por la explicación negativa: creo que eres el primero en explicar. Johannes muestra un truco impresionante para C++ 98, pero no especifica otros trucos, solo ese único. DeadMG comenta sobre el truco que he preferido usar, la macro COMMA. Y para C99 y C++ 11, y compiladores que admiten características más nuevas (por ejemplo, g ++ actual y C++ visual), puede definir simplemente una macro variable 'REMOVE_PARENS (...) __VA_ARGS__'. Dicho esto, no creo que sea una queja digna de downvote que la respuesta no ayuda con una generalización. Escribí mi respuesta para el OP, (cito) "un principiante". –

10

tiene dos opciones. Uno de los cuales ya se mencionó: usando __VA_ARGS__. Sin embargo, esto tiene la desventaja de que no funciona en el estricto C++ 03, sino que requiere un preprocesador suficientemente compatible con C99/C++ 0x.

La otra opción es poner entre paréntesis el nombre de tipo. Pero a diferencia de otras afirmaciones de respuesta, no es tan fácil como simplemente poner entre paréntesis el nombre del tipo. Escribir una especialización como se indica a mal formada

// error, NOT valid! 
template<> struct AClass<(int)> { X a; }; 

He trabajado alrededor de este (y aumentar utiliza probablemente la misma bajo el capó) pasando el nombre de tipo entre paréntesis, y luego la construcción de un tipo de función de ella

template<typename T> struct get_first_param; 
template<typename R, typename P1> struct get_first_param<R(P1)> { 
    typedef P1 type; 
}; 

Con eso, get_first_param<void(X)>::type denota el tipo X. Ahora puede reescribir su macro a

#define specialize_AClass(X) \ 
template<> struct AClass<get_first_param<void X>::type> { 
    get_first_param<void X>::type a; 
}; 

Y solo tiene que pasar el tipo entre paréntesis.

+2

Alternativamente, podría simplemente usar BOOST_PP_COMMA() o una función similar para pasar sus propias comas. – Puppy

+0

¡Gran truco! ¿Sabes si hay alguna manera de hacer que esto funcione con tipos de vehículos certificados? – qbt937

5

Hay un par de problemas aquí.

Antes que nada, las macros son extremadamente tontas, son complicadas, pero esencialmente equivalen a un proceso de reemplazo de texto puro.

Por consiguiente, existen 2 números (técnicas) con el código que expuesto:

  1. No puede utilizar una coma en el medio de una invocación de la macro, sólo falla, BOOST_FOREACH es una biblioteca muy conocido y sin embargo lo único que podían hacer era decirle al usuario que sus argumentos no deberían contener comas, a menos que pudieran estar entre paréntesis, que no siempre es el caso
  2. Incluso si se produce el reemplazo, su código fallaría en C++ 03, porque crearía un símbolo >> al final de la especialización de plantilla, que no se analizaría correctamente.

Hay preprocesamiento/trucos plantilla metaprogramación, sin embargo, la solución más simple es utilizar un tipo sin comas:

typedef std::vector<int, std::allocator<int> > FooVector; 
specialize_AClass(FooVector) 

Por último, hay un problema estético, debido a su omnipresencia, las macros se administra mejor nombres que no pueden chocar con nombres "regulares" (tipos, funciones, variables). El consenso general es utilizar todos los identificadores en mayúsculas, como en:

SPECIALIZE_ACLASS 

Tenga en cuenta que esto no puede empezar por un guión bajo, porque la norma restringe el uso de identificadores que emparejan _[A-Z].* o [^_]*__.* a los autores de compiladores para la biblioteca estándar o lo que quieran (esos no son emoticonos: p)

9
template<typename TypeX, typename TypeY> 
class Test 
{ 
public: 
    void fun(TypeX x, TypeY y) 
    { 
     std::wcout << _T("Hello") << std::endl; 
     std::wcout << x << std::endl; 
     std::wcout << y << std::endl; 
    } 
}; 

#define COMMOA , 

#define KK(x) x val; 

void main() 
{ 
    KK(Test<int COMMOA int>); 
    val.fun(12, 13); 
} 

Tengo una nueva forma de resolver este problema. espero que le puede ayudar :)

+0

ofende mi sentido de la belleza, pero se ajusta a la ley. ¡Gracias! – Brad

1

Si usted está dispuesto a añadir un poco más de código antes de llamar a la macro, siempre se puede hacer esto como una solución:

typedef std::vector<int,std::allocator<int> > myTypeDef; 
specialize_AClass(myTypeDef) //works 
+0

No siempre se puede usar typedef. Por ejemplo, imagine que desea crear una instancia explícita de la clase de plantilla que se pasa como macro argumento. En tal caso, necesita el nombre de clase exacto, el pseudónimo typedef-ed no funcionará. – stgatilov

Cuestiones relacionadas