0

Problem method:

template <typename T>
    void SerializableScalar<T>::deserialize(const Json::Value& token)
    {
        if (isDeserilizationPossible(token)){

            if (token.isInt())
            {
                myValue = token.asInt();
            }
            if (token.isDouble())
            {
                myValue = token.asDouble();
            }
            if (token.isString())
            {
                myValue = token.asString().c_str();
            }
            if (token.isBool())
            {
                myValue = token.asBool();
            }
        }
    }

Where token can hold types string, int, double, bool.

and then i create object and use deserialize method

Json::Value token(55);

When i create object:

SerializableScalar<int> object;
object.deserialize(token);

I get compile errors because when I created object of T = int; in my deserialize method i cannot convert string to int even when json doesn't hold value of string , couse compiler inspect all branches.... And now i am asking you for advice. Is there any solution for this? I tried to overload deserialize method, its working but I don't want separate 4 methods, I am looking for something cleaner .

IsDeserilizationPossible method

template <typename T>
   bool SerializableScalar<T>::isDeserilizationPossible(const            Json::Value& token)
    {
        if (!token.isArray() && !token.isObject() && !token.empty())
        {
            myIsDeserialize = true;
            return true;
        }

        throw std::exception("Some exception");
    }

Additional info:

In cLass is just empty constructor and variable myValue holding type of T. point is deserialize Json, set value of type T. Client know the type he wants get so he use getMethod to get value of Serializable object; Thats it , i tried specialization already it works fine but i was just curious if is there something more i can use , so i don't need to overload method.

Errors: :

error C2440: '=' : cannot convert from 'const char *' to 'double'   
error C2440: '=' : cannot convert from 'const char *' to 'int'
Cœur
  • 37,241
  • 25
  • 195
  • 267
Filip Jerga
  • 75
  • 1
  • 8
  • It would be helpful if you included the compiler error, and possibly further class definition for SerializableScalar. What is `myValue`? – Joel Cornett Feb 17 '16 at 06:59
  • updated, errors included – Filip Jerga Feb 17 '16 at 07:27
  • There's really no way around it. You're going to have to write a template specialization for each `T` that you care about. You can, however specialize for "groups" of types. See my answer here for reference: http://stackoverflow.com/a/34111729/1142167 – Joel Cornett Feb 17 '16 at 07:30
  • Basically, there is no way to ensure at compile-time that any of the branches of your if statement will be traversed at runtime. That's why your compiler chokes. It can't know ahead of time whether `token.isInt()` will return `true` for `int`s only, for example. – Joel Cornett Feb 17 '16 at 07:35

3 Answers3

1

Unfortunately, there is no way around creating specializations for each relevant type. Here is one way that delegates the specializations to a helper class:

template<typename T>
struct Helper
{
    static inline bool is(const Json::Value&);
    static inline T as(const Json::Value&);
};

template<> bool Helper<int>::is(const Json::Value& tok)
{ return tok.isInt(); }

template<> bool Helper<double>::is(const Json::Value& tok)
{ return tok.isDouble(); }

template<> bool Helper<string>::is(const Json::Value& tok)
{ return tok.isString(); }

template<> bool Helper<bool>::is(const Json::Value& tok)
{ return tok.isBool(); }

template<> int Helper<int>::as(const Json::Value& tok)
{ return tok.asInt(); }

template<> double Helper<double>::as(const Json::Value& tok)
{ return tok.asDouble(); }

template<> string Helper<string>::as(const Json::Value& tok)
{ return tok.asString(); }

template<> bool Helper<bool>::as(const Json::Value& tok)
{ return tok.asBool(); }

template<typename T>
void SerializableScalar<T>::deserialize(Json::Value& tok)
{
    if ( Helper<T>::is(tok) )
        myValue = Helper<T>::as(tok);
}
Joel Cornett
  • 24,192
  • 9
  • 66
  • 88
1

This should be a comment, but it got too long:

Problems with this approach

Compile- and run-time quantities don't fit together well. I guess you already found it uncomfortable to have to hard-code the type int into your serialization.

SerializableScalar<int> object;

What if it contains a string instead? Do you want to deserialize on each possible type?

SerializableScalar<int> object_int;
SerializableScalar<double> object_double;
SerializableScalar<std::string> object_string;

if(object_int)  //check whether int is filled
{
    //...
}
else if (object_double)
{
    //...
}

Next, do you want to do that recursively (Json's can contain other Json's)? This is not practical in my opinion.

Exception: The content of the Json is known at compile time

The only exception is for the case you know exactly the content of your Json. Then, one can write a general wrapper function (which is more comfortable than a class imo) such as

template<typename T>
auto get_value(Json::Value const& json) {}

template<>
auto get_value<int>(Json::Value const& json) { return json.as_int(); }

template<>
auto get_value<bool>(Json::Value const& json) { return json.as_bool(); }

and apply that according to the structure of your Json. Note that I've added no type-checking such as json.is_int() up to now. Two possibilities to incorporate it:

  • First, if it's not an int, return a default value:

    constexpr int DEFAULT = -1;
    template<>
    auto get_value<int>(Json::Value const& json)
    {
        return json.is_int() ? json.as_int() : DEFAULT;
    }
    
  • Second, and a bit more sophisticated, you can return an optional<int>, which is in its null state if the json is not filled.

General case

In the general case, however, when you don't know the content or the content is about to change, one has to accept the multi-type being of a Json and model that accordingly. The basic approach is to use a boost::variant plus an appropriate visitor.

davidhigh
  • 14,652
  • 2
  • 44
  • 75
0

One possibility would be to create a catch-all overload:

void Json::Value::operator=(...)
{
    assert( false && "this should never be called" );
}

This will have lowest priority in overload resolution, however erogenous assignments will no longer produce compile-time errors, so consider it a quick fix.

sp2danny
  • 7,488
  • 3
  • 31
  • 53