2009-09-24 8 views
3

Voici mon problème: dans un en-tête je définir un modèle de structure type_to_string, qui vise à définir une chaîne correspondant à un argument de type donné:Définir un symbole dans un autre espace de noms

namespace foo { 

    template <typename T> 
    struct type_to_string 
    { 
     static const char * value; 
    }; 
} 

template <typename T> 
const char * foo::type_to_string<T>::value = "???"; 

Je définissent également une valeur par défaut pour la chaîne.

Maintenant, je veux utiliser une macro pour définir de nouveaux types:

#define CREATE_ID(name)        \ 
struct name;           \ 
                 \ 
template<>           \ 
const char * foo::type_to_string<name>::value = #name; 

Le problème est que je voudrais la macro pour être utilisable dans les espaces de noms, comme dans:

namespace bar 
{ 
    CREATE_ID(baz) 
} 

ce qui n'est pas possible car type_to_string<T>::value doit être défini dans un espace de noms englobant foo.

Voici les erreurs de compilation je reçois:

[COMEAU 4.3.10.1] error: member "foo::type_to_string<T>::value [with T=bar::baz]" 
cannot be specialized in the current scope 

[VISUAL C++ 2008] error C2888: 'const char *foo::type_to_string<T>::value' : 
symbol cannot be defined within namespace 'bar' 
    with 
    [ 
     T=bar::baz 
    ] 

Étrangement, GCC 4.3.5 (version MinGW) ne produit pas d'erreurs. Est-ce que quelqu'un sait une solution de contournement pour cela, peut-être en utilisant des règles de recherche que je ne connais pas (ie déclarer type_to_string dans la macro pour que chaque espace de noms ait sa propre version, ou quelque chose comme ça)?

Répondre

9

Selon C Standard 14.7.3/2:

Une spécialisation explicite doit être déclarée dans l'espace de noms dont le modèle est membre, ou, pour modèles membres, dans l'espace dont la La classe englobante ou le modèle de la classe englobante est un membre. Une spécialisation explicite d'une fonction membre, d'une classe membre ou d'un membre de données statiques d'un modèle de classe doit être déclarée dans l'espace de noms dont le modèle de classe est membre. Une telle déclaration peut également être une définition. Si la déclaration n'est pas une définition, la spécialisation peut être définie plus tard dans le nom- espace dans lequel la spécialisation explicite a été déclarée, ou dans un espace de noms qui contient celui dans lequel la spécialisation explicite a été déclarée.

Vous pouvez écrire quelque chose comme ce qui suit:

#define DECL_ID(name) \ 
struct name;           

#define CREATE_ID(name) \ 
template<>    \ 
const char * foo::type_to_string<name>::value = #name; 

namespace bar { namespace bar2 { 
    DECL_ID(baz) 
} } 
CREATE_ID(bar::bar2::baz) 

Ou

#define CREATE_ID(ns, name)  \ 
namespace ns { struct name; } \ 
           \ 
template<>      \ 
const char * foo::type_to_string<ns::name>::value = #name; 

CREATE_ID(bar, baz) 

La troisième option est la superposition des deux premiers. Il permet d'avoir le nom non qualifié dans value (si elle est nécessaire):

#define DECL_ID(name) \ 
struct name;           

#define CREATE_ID(ns, name) \ 
template<>    \ 
const char * foo::type_to_string<ns::name>::value = #name; 

namespace bar { namespace bar2 { 
    DECL_ID(baz) 
} } 
CREATE_ID(bar::bar2, baz) 
+0

Merci pour le paragraphe de la norme, il est toujours bon d'avoir les exigences exactes. En ce qui concerne votre deuxième solution, c'est en fait quelque chose que je fais déjà: j'ai deux paramètres, un pour définir l'espace de noms, l'autre pour le type. Cependant, je souhaite que la macro puisse être utilisable dans un autre espace de noms, afin de ne pas restreindre le nombre d'espaces de noms utilisés. Je pourrais définir la macro CREATE_ID avec les paramètres 1, 2, 3, ... pour accepter plusieurs noms d'espace de noms, mais cela serait plutôt fastidieux. Si c'est la seule solution, je n'aurai pas beaucoup de choix ... –

+0

Pour l'anecdote, j'ai fini par utiliser Boost.Preprocessor pour passer une liste d'espaces de noms à la macro. De cette façon, le nom peut être inclus dans plusieurs espaces de noms imbriqués. –

1

Voici la solution que j'employé, en utilisant Boost.Préprocesseur:

#include <boost/preprocessor/seq/for_each.hpp> 
#include <boost/preprocessor/seq/size.hpp> 
#include <boost/preprocessor/repetition/repeat.hpp> 

#define BEGIN_NS(r, _, elem) namespace elem { 
#define CLOSE_NS(z, n, _)  } 
#define APPEND_NS(r, _, elem) elem:: 

#define CREATE_ID(ns_list, name)       \ 
                 \ 
BOOST_PP_SEQ_FOR_EACH(BEGIN_NS, ~, ns_list)    \ 
    struct name;           \ 
BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(ns_list), CLOSE_NS, ~) \ 
                 \ 
template<>            \ 
const char * devs::type_to_string<      \ 
    BOOST_PP_SEQ_FOR_EACH(APPEND_NS, ~, ns_list)name  \ 
>::value = #name; 

qui doit être utilisé en dehors de tout espace de noms comme celui-ci:

CREATE_ID((bar) (bar2), baz) 

Il semble étrange que je devais définir une macro que répéter n fois le caractère « } », si quelqu'un a un moyen plus élégant de le faire, n'hésitez pas à poster un commentaire!

Questions connexes