0

I want to write a serial of template functions to serialize and deserialize objects. I've finished the serialization part and everything works:

#ifndef SERIALIZE_H
#define SERIALIZE_H

#include <string>
#include <vector>
#include <unordered_set>
#include <unordered_map>
#include <memory>

inline std::string to_json(int value) {
    return std::to_string(value);
}


inline std::string to_json(long value) {
    return std::to_string(value);
}


inline std::string to_json(double value) {
    return std::to_string(value);
}


inline std::string to_json(const std::string& myStr) {
    return "\"" + myStr + "\"";
}


template <typename T>
std::string to_json(const std::vector<T>& vec) {
    std::string json("[");

    for(auto &i : vec) {
        json += to_json(i);
        json += ",";
    }

    if (!vec.empty()) json.pop_back();
    json += "]";
    return json;
}


template <typename T>
std::string to_json(const std::unordered_set<T>& mySet) {
    std::string json("[");

    for(const auto& i : mySet) {
        json += to_json(i);
        json += ",";
    }

    if (!mySet.empty()) json.pop_back();
    json += "]";
    return json;
}


template <typename K, typename V>
std::string to_json(const std::unordered_map<K, V>& myMap) {
    std::string json("{");

    for(const auto& i : myMap) {
        json += to_json(i.first);
        json += ":";
        json += to_json(i.second);
        json += ",";
    }

    if (!myMap.empty()) json.pop_back();
    json += "}";
    return json;
}

#endif //SERIALIZE_H

This serialize.h can serialize all kinds of combinations, such as unordered_map<string, vector<int>>.

Now I don't know how to implement deserialization functions recursively to support arbitrary combinations.

The following is my deserialize.h which doesn't work:

#ifndef DESERIALIZE_H
#define DESERIALIZE_H

#include <string>
#include <rapidjson/document.h>


template<typename T>
T from_json(const std::string &json);

template<>
int from_json(const std::string &json) {
    return std::stoi(json);
}

template<>
long from_json(const std::string &json) {
    return std::stol(json);
}

template<>
double from_json(const std::string &json) {
    return std::stod(json);
}

template<>
std::string from_json(const std::string &json) {
    return json.substr(1, json.size()-1);
}

//
template<typename T>
std::vector<T> from_json(const std::string& json) {
    rapidjson::Value jsonValue;
    {
        const std::string &input = "{\"input\":" + json + "}";
        rapidjson::Document document;
        document.Parse(input.c_str());
        jsonValue = document["input"];
    };
    std::vector<T> vec;
    assert(jsonValue.IsArray());

    for (rapidjson::SizeType i = 0; i < jsonValue.Size(); i++) {
        int element = from_json<T>(std::string(jsonValue[i].GetString()));
        vec.push_back(element);
    }
    return vec;
}

#endif //DESERIALIZE_H

rapidjson is a C++ JSON library, https://github.com/miloyip/rapidjson

Then if I try to deserialize a JSON string:

#include "deserialize.h>

int main() {
    auto vec1 = from_json<std::vector<int>>(std::string("[1,2,3]"));
    return 0;
}

It will throw out compilation errors:

error: call of overloaded ‘from_json(std::string)’ is ambiguous

It seems there is no way to implement deserialization function as easily as serialization.

Any ideas?

dyp
  • 38,334
  • 13
  • 112
  • 177
soulmachine
  • 3,917
  • 4
  • 46
  • 56
  • Related: http://www.gotw.ca/publications/mill17.htm . Specifically, there's no *more specialized* relationship between the two (primary) function templates you're defining, since they only differ in their return types. One solution if you want to keep the interface is to dispatch to some `template T from_json(tag, const std::string&); template std::vector from_json(tag>, const std::string&);` overload set, where a *more specialized* relationship can be established based on the first (dummy/tag) parameter. – dyp Sep 20 '15 at 22:27
  • I wouldn't bother making those inline. Converting strings isn't a simple operation. – Neil Kirk Sep 20 '15 at 22:33
  • Are you expecting it to call `template T from_json(const std::string &json);`, or `template std::vector from_json(const std::string& json)`? That is, should it return a `std::vector` (from the declared but not defined first template) or a `std::vector>` (from the second template)? – user253751 Sep 20 '15 at 23:11

1 Answers1

2

You are trying to overload the same function (from_json(...)) with the same arguments and a different return type each time. This is not legal. For overloading, you need different argument types or argument number.

Instead of
template<>
double from_json(const std::string &json) {
    return std::stod(json);
}

template<>
std::string from_json(const std::string &json) {
    return json.substr(1, json.size()-1);
}

maybe try this, or at least this should be the general idea:

template<>
void from_json(const std::string &json, double *out_value) {
    *out_value = std::stod(json);
}

template<>
void from_json(const std::string &json, std::string *out_value) {
    out_value = json.substr(1, json.size()-1);
}

I am sure there are errors, but I think this way it can work (if you fix them)

Yaron
  • 1,540
  • 3
  • 19
  • 33
  • 1
    OP is mixing *function template explicit specialization* with *function overloading*. Esp. the `template<> double from_hson(const std::string &json);` is an explicit specialization of the `template T from_json(const std::string &json);` primary template. – dyp Sep 20 '15 at 22:21
  • Using different argument types will work, that's for sure, but I still want to do template specialization on the return type. – soulmachine Sep 20 '15 at 23:48