James Durand
James Durand

Reputation: 156

Partial C++ Template Specialization with STL containers

So my goal is to create a function or functor that can return different values. The use case is to convert Json objects into the correct C++ type. For the most part I have this working but I'm running into issues with containers such as std::vector and std::array. I know you can't overload a function to just return different value types so I've been using a struct with template specialization. I figured I would need to use partial template specialization to achieve this but I'm running into compile errors. Here's what my code looks like:

template <typename T>
struct from_json
{
    T operator()(const Json::Value &json)
    {
        return T::fromJson(json);
    }
};

template <>
struct from_json<std::string>
{
    std::string operator()(const Json::Value &json)
    {
        return std::string(json.asCString());
    }
};

template <typename T>
struct from_json<std::vector<T> >
{
    std::vector<T> operator()(const Json::Value &json)
    {
        std::vector<T> vector;

        for (auto obj : json)
            vector.push_back(from_json(obj));

        return vector;
    }
};

Some notes on the code snippet: I have an abstract class that requires fromJson to be implemented by my own serializable classes which is what the general case is about. The std::string generalization seems to work fine. The std::vector is where things break. The compiler recognizes that that is the specialization that should be used but gives an error when trying to actually use it.

std::vector<std::string> my_vector = from_json<std::vector<std::string> >(json["my_vector"]);

The compiler error is the following:

error: no matching function for call to 'from_json<std::vector<std::__cxx11::basic_string<char> > >::from_json(const Json::Value&)'
std::vector<std::string> my_vector = from_json<std::vector<std::string> >(json["my_vector"]);
note: candidate: constexpr from_json<std::vector<std::__cxx11::basic_string<char> > >::from_json()

It is also worth noting that I am using C++ 11.

I'm definitely open to suggestions on this could be done better if there is a better way.

Thanks!

Upvotes: 1

Views: 584

Answers (1)

Barry
Barry

Reputation: 302757

So in this line:

vector.push_back(from_json(obj));

from_json in your code is a class template with a defined operator(). It's not a function or a function object (in that sense, it's kind of like std::hash). So you can't call it - you need call an instantiation of it.

I'm guessing you mean something like:

vector.push_back(from_json<T>{}(obj));

Or if you're into range-v3:

std::vector<T> operator()(const Json::Value &json)
{
    return json | view::transform(from_json<T>{});
}

Upvotes: 2

Related Questions