1

I have a function-template called serialize which takes a template parameter like this:

template <typename T>
std::string serialize(T const & t);

I have different use-cases for serialize and they depend on T.

  • In general, serialize shall just throw an exception.

  • If T is a std::vector, serialize each item using serialize<Item> with T=std::vector<Item>.

  • If T is a std::map, serialize each key and value separately using serialize<K> and serialize<V> with T=std::map<K,V>.

  • If T is a std::tuple, serialize each component using serialize<Ci> with i in {0, n-1} where n is the size of the tuple and Ci is the type of the component at position i.

I figured I could use std::enable_if with is_specialization to differentiate those cases, which is why I wrote them like this:

template <typename T>
std::string serialize(T const & t) { throw std::exception(); }

template <typename TVec,
          typename T = std::enable_if<is_specialization<TVec, std::vector>, TVec::value_type>::type>
std::string serialize(TVec const & tvec) { /*Something with serialize<Item>*/ }

template <typename KVMap,
          typename K = std::enable_if<is_specialization<TMap, std::map>, KVMap::key_type>::type,
          typename V = std::enable_if<is_specialization<TMap, std::map>, KVMap::mapped_type>::type>
std::string serialize(KVMap const & kvmap) { /*Something with serialize<K> and serialize<V>*/ }

And of course, here's the implementation of is_specialization : See here

You'll notice I didn't provide an implementation for the std::tuple-case, which is because I don't know how to write that one. My first impulse was to do something along the lines of:

template <typename Tuple,
          ???>
std::string serialize(Tuple const & tup) { /*Something with serialize<Ci> for different Ci*/ }

But how do I get Ci? Do you have an idea how to correctly write the std::tuple-case?

I tried to replace ??? with an std::enable_if to find out whether Tuple is a std::tuple, but unlike the cases with std::vector and std::map I cannot get a parameter-pack containing the tuples' types from the tuple-class itself... or can I? I really don't know.

Dharman
  • 30,962
  • 25
  • 85
  • 135
ChaosNe0
  • 37
  • 7

2 Answers2

3

Why not just

template <typename... Ts>
std::string serialize(const std::tuple<Ts...>&) { /* ... */ }

?

Vittorio Romeo
  • 90,666
  • 33
  • 258
  • 416
  • You were right. I'm implementing the `template T deserialize(std::string const & tstr)` in parallel and because the compiler cannot resolve `T` in that case, I started using `std::enable_if` and `is_specialization` when I didn't need it for `template std::string serialize(T const & t)`. It was my oversight. – ChaosNe0 Mar 23 '23 at 16:01
0

I figured I could use std::enable_if with is_specialization to differentiate those cases.

Simpler would be to use regular template method:

template <typename T>
std::string serialize(T const&) { throw std::exception(); }

template <typename T>
std::string serialize(const std::vector<T>& v) {
    std::string res;

    for (const auto& e : v) {
        res += serialize(e);
    }
    return res;
}

template <typename K, typename V>
std::string serialize(const std::map<K, V>& m) {
    std::string res;

    for (const auto& p : m) {
        res += serialize(p.first);
        res += serialize(p.second);
    }
    return res;
}

#if 1 // std::apply for pre-c++17

template <typename F, typename Tuple, std::size_t... Is>
auto apply_impl(F&& f, Tuple&& tuple, std::index_sequence<Is...>)
-> decltype(std::forward<F>(f)(std::get<Is>(std::forward<Tuple>(tuple))...))
{
    return std::forward<F>(f)(std::get<Is>(std::forward<Tuple>(tuple))...);
}

template <typename F, typename Tuple>
auto apply(F&& f, Tuple&& tuple)
-> decltype(apply_impl(f, tuple, std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>()))
{
    return apply_impl(f, tuple, std::make_index_sequence<std::tuple_size<std::decay_t<Tuple>>::value>());
}

#endif


template <typename... Ts>
std::string serialize(const std::tuple<Ts...>& t) {
    return apply([](const auto&... args){
        std::string res;
#if 0 // For C++17
        (res += serialize(args), ...);
#else
        const int dummy[] = {0, (res += serialize(args), void(), 0)...};
        static_cast<void>(dummy); // Avoid warning for unused variable
#endif
        return res;
    }, t);
}

Demo

Jarod42
  • 203,559
  • 14
  • 181
  • 302
  • That looks mighty impressive, but I don't know how to read half of the code. Also, when I copied this code into an online ide, it didn't work (and because I don't particularly understand it I couldn't fix it myself.) – ChaosNe0 Mar 23 '23 at 16:04
  • Typo fixed and demo added. – Jarod42 Mar 23 '23 at 18:00