Home > Blockchain >  Error with partial template specialization
Error with partial template specialization

Time:02-26

I need to write a function for parsing JSON object. For convinience I decided to use templates for doing that.

So, my function looks as follows:

template<typename ValueType>
ValueType get(const Json& json);

For each type of values i want to receive from JSON there is a specialization:

template<>
uint8_t get(const Json& json);
template<>
double get(const Json& json);
template<>
std::string get(const Json& json);
...

Now I want to extend this function and add the ability to receive containers of elements. As I understand for achieving that I need to add specialization for every container type and every value type:

template<>
std::vector<uint8_t> get(const Json& json);
template<>
std::vector<std::string> get(const Json& json);
...

It is absolutely not convinient because of N*N complexity. Instead of doing that I want have something like this:

template<typename ValueType>
std::vector<ValueType> get<std::vector<ValueType>>(const Json& json);

or even this:

template<template<typename...> class Container, typename ValueType>
Container<ValueType> get(const Json& json);

But if I try to write such templates, i receive the error:

error: function template partial specialization ‘getstd::vector<_RealType >’ is not allowed std::vector getstd::vector<ValueType>(const Json& json);

How should I refactor the function for achieving my goal?

CodePudding user response:

You might use tag dispatcher helper (so overload instead of specialization):

template <typename T>
struct tag{};

uint8_t get_helper(tag<uint8_t>, const Json& json);
double get_helper(tag<double>, const Json& json);
std::string get_helper(tag<std::string>, const Json& json);

template <typename ValueType>
std::vector<ValueType> get_helper(tag<std::vector<ValueType>>, const Json& json)
{
    // ... use get_helper(tag<ValueType>{}, sub_json);
}


template<typename ValueType>
ValueType get(const Json& json)
{
    return get_helper(tag<ValueType>{}, json);
}

CodePudding user response:

IMO use of templates can be avoided.
Change code from return value to pass by reference and it becomes easy and clean:

void get(const Json& json, uint8_t& retVal);
void get(const Json& json, double& retVal);
void get(const Json& json, std::string& retVal);

To cover std::vector flavors you can use this templete:

template<typename T>
void get(const Json& json, std::vector<T>& retVal)
{
    if (json.isArray()) {
       auto jarray = json.toArray();
       retVal.resize(jarray.size());
       for (size_t i = 0; i < retVal.size();   i) {
           get(jarray[i], retVal[i]);
       }
    }
}

If you need template anyway approach above could be used to solve your problem using only default implementation of template:

template<typename ValueType>
ValueType get(const Json& json);
{
    ValueType retVal;
    get(json, retVal); // use overloads from above
    return retVal;
}

This way default template implementation does everything what is needed. Extending is simple too.

CodePudding user response:

This answer assumes c 17 for variable templates.

Write a type template, and partially specialize that type template. Put the parse functions you need as operator() members inside the partial type template specializations. Declare get as a variable template that instantiate that type template.

template<typename Ret>
struct get_impl;

template<typename Elem>
struct get_impl<std::vector<Elem>> {
    auto operator()(const Json&) -> std::vector<Elem>; 
};

template<>
struct get_impl<std::string> {
    auto operator()(const Json&) -> std::string;
};

template<typename Ret>
inline constexpr auto get = get_impl<Ret>{};

BTW, this is similar to how one would implement a customization point object.

  • Related