Question

Now I know that partial specialization of function templates is something that doesn't exist (If I am understanding it correctly, the reasoning goes that if they did, they would never be used).

However I have a particular function template that I am using heavily now which goes like this:

template <typename>
char const* TypeName() {
    return "UNDECLARED TYPENAME";
}

#define TYPENAME(...) \
    GET_MACRO(__VA_ARGS__, TYPENAME32, TYPENAME31, TYPENAME30, TYPENAME29, \
            TYPENAME28, TYPENAME27, TYPENAME26, TYPENAME25, TYPENAME24, \
            TYPENAME23, TYPENAME22, TYPENAME21, TYPENAME20, TYPENAME19, \
            TYPENAME18, TYPENAME17, TYPENAME16, TYPENAME15, TYPENAME14, \
            TYPENAME13, TYPENAME12, TYPENAME11, TYPENAME10, TYPENAME9, \
            TYPENAME8, TYPENAME7, TYPENAME6, TYPENAME5, TYPENAME4, TYPENAME3, \
            TYPENAME2, TYPENAME1)(__VA_ARGS__)

#define TYPENAME1(x) \
template<> char const* TypeName<x>() { return #x; }

#define TYPENAME2(x, y) \
template<> char const* TypeName<x, y>() { return #x "," #y; }

#define TYPENAME3(x, y, z) \
template<> char const* TypeName<x, y, z>() { return #x "," #y "," #z; }

#define TYPENAME4(x, y, z, w) \
template<> char const* TypeName<x, y, z, w>() { return #x "," #y "," #z "," #w; }

// ... and so on and so forth

where GET_MACRO is

#define GET_MACRO(_1,_2,_3,_4,_5,_6,_7,_8,_9,_10,_11,_12,_13,_14,_15,_16,_17,_18,_19,_20,_21,_22,_23,_24,_25,_26,_27,_28,_29,_30,_31,_32,NAME,...) NAME

Okay, so this all works fine and dandy, so long as anywhere in my code I place e.g. TYPENAME(std::unordered_map<std::vector<typeA>, std::map<int, std::string>>) then I'll be able to have compile-time code evaluate TypeName<T>() where T is std::unordered_map<std::vector<typeA>, std::map<int, std::string>> to be "std::unordered_map<std::vector<typeA>, std::map<int, std::string>>". That works perfectly fine right now.

Just to clarify, the sole purpose of the TYPENAME macro is just so that I can avoid typing template <> char const* TypeName<...>() { return ... }. It's for DRYness. It is an elaborate set of macros because of the pesky commas that tend to be in types.

Now, I'd like to declare some templates that are not necessarily fully specified. For example I would like to have templates declared (through my macros) for TypeName<std::unordered_map<K,V>>() and also for TypeName<typeA>, and to have the evaluation of the former function call TypeName and TypeName to determine what to return for the string.

However it appears that I have run into a limit because I clearly am not supposed to be able to achieve this with function templates. It also appears that that entire set of macros need to just get thrown out the window if I want to do this sort of thing.

Can I do it with class templates? I do want to have this all work at compile-time, but if there is a way to do it so that a dynamic string is built and cached the first time it's accessed (at run-time), that should also be reasonably fast enough that I can deal with it.

I just want to not have to explicitly declare a template for the fully qualified type, I want TypeName to assemble the string intelligently (Like I already can do with e.g. operator <<).

I wonder if maybe I just did something really dumb and should just have made my typename T live as the function argument to TypeName rather than the template parametrization (as Herb Sutter says)? Still a little fuzzy on how that syntax would be written, though!

Was it helpful?

Solution

You should be able to do what you want with class template:

template <typename...> struct helper_typename;

#define TYPENAME1(x) \
    template<> struct helper_typename<x> \
    { static char const* TypeName() { return #x; } };

#define TYPENAME2(x, y) \
    template<> struct helper_typename<x, y> \
    { static char const* TypeName() { return #x "," #y; } };

//...

// partial specialization for std::unordered_map
template <typename K, typename V>
struct helper_typename<std::unordered_map<K, V>>
{
     static char const* TypeName()
     {
         static std::string s = std::string("std::unordered_map<")
             + helper_typename<K>::TypeName() + ", "
             + helper_typename<V>::TypeName() + ">";
          return s.c_str();
     }
};

template <typename... Ts> char const* TypeName()
{
    // forward to the correct (partial) specialized struct.
    return helper_typename<Ts...>::TypeName();
}
Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top