galinette
galinette

Reputation: 9292

Match a template pattern only if another pattern is matched

I have a template function defined only for some types:

template<typename T, std::enable_if_t<std::is_pod_v<T>, bool> = true >
void serialize(const T & t) { /*serialize POD*/ }

template<>
void serialize(const std::string  & t) { /*serialize string*/ }

//....

And I would like to add a std::vector specialization that matches only if T can be serialized

template<typename T /*, is_serializable<T> */>
void serialize(const std::vector<T> & t) { /*do something*/ }

How can I make this one matching only if T himself matches a serialize() method?

Upvotes: 1

Views: 88

Answers (3)

Aconcagua
Aconcagua

Reputation: 25536

Pre-C++20 solution: Test if you can serialize a type:

template <typename T>
auto is_serializable(T) -> decltype(serialize(std::declval<T>()), std::true_type());
std::false_type is_serializable(...);
template <typename T>
bool constexpr is_serializable_t
    = decltype(is_serializable(std::declval<T>()))::value;

With this you can apply the same pattern as you had for the POD types already:

template<typename T, std::enable_if_t<is_serializable_t<T>, bool> = true >
void serialize(std::vector<T> const& t)
{ /*do something*/ }

Demonstration on godbolt.

Edit according to comments:

Actually it does not appear meaningful to serialise a std::vector without serialising all of its elements contained. So the most simple solution (KISS principle...) is: Just doing the serialisation witout adding any tests at all!!!

template<typename T>
void serialize(std::vector<T> const& v)
{
    // you'd most likely need:
    serialize(v.size());
    // but other variants are be possible, too, like e.g. COBS encoding the data,
    // then last element to be added, when deserializing again, would be followed
    // by the sole 0 (or whatever other sentinel) character encountered...

    for(auto& t : v)
    {
        serialize(t);
    }
}

The vector would start to serialise its elements, and these then, if yet other vectors, would just do the same – until reaching the base case where T isn't a vector any more. And now the final decision is made: Does an overload exist handling this final T or not, accordingly the code compiles or not ;)

Demonstration on godbolt.

Upvotes: 1

sklott
sklott

Reputation: 2859

SFINAE variant can be as simple as:

template<typename T, typename = decltype(serialize(std::declval<T>())) >
void serialize(std::vector<T> const& t)
{ /*do something*/ }

Upvotes: 2

Jarod42
Jarod42

Reputation: 217880

With C++20 constraints, you might do something like:

template <typename T>
void serialize(const std::vector<T>& v)
requires requires (T inner) {serialize(inner);}
{
  // serialize vector
  // ...
}

Demo

Upvotes: 4

Related Questions