views:

301

answers:

2

Here is my problem: in a header I define a structure template type_to_string, which aims at defining a string corresponding to a given type argument:

namespace foo {

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

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

I also define a default value for the string.

Now, I'd want to use a macro for defining new types:

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

The problem is that I'd like the macro to be usable in namespaces, as in:

namespace bar
{
    CREATE_ID(baz)
}

which is not possible because type_to_string<T>::value must be defined in a namespace enclosing foo.

Here is the compilation errors I get:

[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
     ]

Strangely, GCC 4.3.5 (MinGW version) doesn't produce any errors.

Does anyone know a workaround for this, maybe by using some lookup rules I'm not aware of (i.e. declaring type_to_string in the macro so that each namespace has its own version, or something like that)?

+4  A: 

According to C++ Standard 14.7.3/2:

An explicit specialization shall be declared in the namespace of which the template is a member, or, for member templates, in the namespace of which the enclosing class or enclosing class template is a member. An explicit specialization of a member function, member class or static data member of a class template shall be declared in the namespace of which the class template is a member. Such a declaration may also be a definition. If the declaration is not a definition, the specialization may be defined later in the name- space in which the explicit specialization was declared, or in a namespace that encloses the one in which the explicit specialization was declared.

You could write something like the following:

#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)

Or

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

CREATE_ID(bar, baz)

The third option is superposition of first two. It allows to have unqualified name in value (if it is required):

#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)
Kirill V. Lyadvinsky
Thanks for the paragraph in the standard, it's always good to have the exact requirements. Regarding your second solution, it is in fact something I'm already doing: I have two parameters, one for defining the namespace, the other one for the type. However, I wish the macro could be usable in yet another namespace, in order not to restrict the number of namespaces used. I could define the CREATE_ID macro with 1, 2, 3, ... parameters to accept several namespace names, but this would be rather tedious. If it's the only solution, I wont have much choice...
Luc Touraille
For the record, I ended up using Boost.Preprocessor to pass a list of namespace to the macro. This way, the name can be enclosed in several nested namespaces.
Luc Touraille
A: 

Here is the solution I employed, using Boost.Preprocessor:

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

which must be used outside any namespace like this:

CREATE_ID((bar) (bar2), baz)

It seems strange that I had to define a macro only to repeat n times the character '}', if anyone has a more elegant way to do so, feel free to post a comment!

Luc Touraille