Search code examples
c++templatestype-traitsspecializationfunction-templates

Template specialization using a variable argument


I am trying to wrap a c-style variant in a library. It uses a type enum, which I mean to fill using templates.

A simplified version looks roughly like this:

enum Type
{ 
    Type_Int,
    Type_Double,
    Type_String
};

template<typename T>
Type typeId();

template<> Type typeId<int>()   { return Type_Int;  }
template<> Type typeId<double>(){ return Type_Double;  }
template<> Type typeId<const char*>()   { return Type_String;  }

template<> Type typeId<char[10]>()   { return Type_String;  }

// not allowed
// template<int N> Type typeId<char[N]>()   { return Type_String;  }

https://godbolt.org/z/3GsKv6PEn

I can recognize char[10] just fine, but I want to make a general case of char[N], which is not allowed. So how would I go about recognizing all char[N] types?

ps. It is embedded, so I prefer not to use std::string where possible.


Solution

  • You can't partially specialize a functions. But you can partially specialize class or struct.

    So to resolve this do template logic in a class and then use it in a function:

    enum Type {
        Type_Int,
        Type_Double,
        Type_String,
    };
    
    namespace detail {
    template <typename T>
    class TypeId;
    
    template <>
    class TypeId<int> {
    public:
        static constexpr Type value = Type_Int;
    };
    
    template <>
    class TypeId<double> {
    public:
        static constexpr Type value = Type_Double;
    };
    
    template <>
    class TypeId<const char*> {
    public:
        static constexpr Type value = Type_String;
    };
    
    template <int N>
    class TypeId<const char[N]> {
    public:
        static constexpr Type value = Type_String;
    };
    }
    
    template <typename T>
    constexpr Type typeId()
    {
        return detail::TypeId<T>::value;
    }
    
    static_assert(typeId<int>() == Type_Int);
    static_assert(typeId<double>() == Type_Double);
    static_assert(typeId<const char*>() == Type_String);
    static_assert(typeId<const char[2]>() == Type_String);
    static_assert(typeId<const char[10]>() == Type_String);
    

    https://godbolt.org/z/4zfb47Mvx

    To make this more resistant to cv-qualifiers type variations you can use std::remove_cvref_t : https://godbolt.org/z/esGf4Wc8h