Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

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.

like image 929
David van rijn Avatar asked Jun 19 '26 06:06

David van rijn


2 Answers

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

like image 68
Marek R Avatar answered Jun 21 '26 18:06

Marek R


How would i go about recognizing all char[N] types?

Class specialization should be the preferred way, which works all the time.

However, in your case, you could use if constexpr as follows (Requires c++17 or later) to make one typeId() function for all the Type cases:

template<typename T>
inline constexpr Type typeId() /* noexcept */
{
    if constexpr (std::is_same_v<T, int>)  return Type_Int;
    else if constexpr (std::is_same_v<T, double>) return Type_Double;
    else if constexpr (std::is_same_v<T, const char*> 
        || (std::is_array_v<T> 
               && std::is_same_v<std::remove_const_t<std::remove_extent_t<T>>, char>)
        )
        return Type_String;
}

See live demo


If there are multidimensional char arrays exist, you could replace the std::remove_extent_t with the std::remove_all_extents in the above.

like image 42
JeJo Avatar answered Jun 21 '26 19:06

JeJo



Donate For Us

If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!