-1

I would like my program to save and read a Config structure in a JSON file.

However, I have a problem with generating the correct JSON file. Probably the problem is inheritance.

JSON Output (Incorrect):

  {
        "config": {
            "confVector": [
                {
                    "common": "a"
                },
                {
                    "common": "b"
                }
            ]
        }
    }

Expected (correct) JSON:

  {
        "config": {
            "confVector": [
                {
                    "common": "a",
                    "a" : 1
                },
                {
                    "common": "b",
                    "b" : "b"
                }
            ]
        }
    }

Code :

Base struct with common element

struct Base
{
    std::string common;

    template <class Archive>
    void serialize(Archive &ar)
    {
        ar(CEREAL_NVP(common));
    }
};

Two specific structures

struct A : public Base
{
    int a;

    template <class Archive>
    void serialize(Archive &ar)
    {
        ar(cereal::make_nvp("Base", cereal::base_class<Base>(this)));
        ar(cereal::make_nvp("a", a));
    }
};

struct B : public Base
{
    std::string b;

    template <class Archive>
    void serialize(Archive &ar)
    {
        ar(cereal::make_nvp("Base", cereal::base_class<Base>(this)));
        ar(cereal::make_nvp("b", b));
    }
};

struct Config
{
    std::vector<Base> confVector;

    template <class Archive>
    void serialize(Archive &ar)
    {
        ar(CEREAL_NVP(confVector));
    }
};
CEREAL_REGISTER_POLYMORPHIC_RELATION(Base, A)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Base, B)

Main: test save to json file

int main()
{
    std::string workPath = MAKE_STR(PLC_PROGRAM);

    Config config;

    A a;
    a.a      = 1;
    a.common = "a";

    B b;
    b.b      = "b";
    b.common = "b";

    config.confVector.push_back(a);
    config.confVector.push_back(b);

    std::ofstream outstream;
    outstream.open(workPath + "/test.json");

    {
        cereal::JSONOutputArchive ar(outstream);
        ar(cereal::make_nvp("config", config));
    }

    outstream.close();
}
Michał Hanusek
  • 199
  • 3
  • 13
  • I can't find a good duplicate (I'm sure there's one somewhere) but take a look at [object slicing](https://stackoverflow.com/questions/274626/what-is-object-slicing) – Kevin Jul 11 '18 at 20:53

2 Answers2

0

Your objects are being sliced.

std::vector<Base> confVector;

This is a vector of objects of type Base.
If you push in objects that are derived from Base then it only copies the base part of the object.

A a;
B b;
config.confVector.push_back(a);  // a is of Type A not Base
config.confVector.push_back(b);  // b is of type B not Base

So both these objects are being sliced when put in the vector.

You could store pointers to the object:

std::vector<Base> confVector;
...
config.confVector.push_back(&a);
config.confVector.push_back(&b);
Martin York
  • 257,169
  • 86
  • 333
  • 562
0

I solved this problem.

struct Base
{
    Base()          = default;
    virtual ~Base() = default;

    std::string common;

    template <class Archive>
    void serialize(Archive &ar)
    {
        ar(CEREAL_NVP(common));
    }
};

struct A : public Base
{
    A() = default;

    A(int v)
    {
        a = v;
    }

    int a;

    template <class Archive>
    void serialize(Archive &ar)
    {
        ar(cereal::make_nvp("Base", cereal::base_class<Base>(this)));
        ar(a);
    }
};

struct B : public Base
{
    B() = default;

    B(std::string text)
    {
        b = text;
    }

    std::string b;

    template <class Archive>
    void serialize(Archive &ar)
    {
        ar(cereal::make_nvp("Base", cereal::base_class<Base>(this)));
        ar(b);
    }
};

struct Config
{
    std::vector<std::shared_ptr<Base>> vector;

    template <class Archive>
    void serialize(Archive &ar)
    {
        ar(vector);
    }
};

CEREAL_REGISTER_TYPE(A)

CEREAL_REGISTER_TYPE_WITH_NAME(B, "ClassB")

CEREAL_REGISTER_POLYMORPHIC_RELATION(Base, A)
CEREAL_REGISTER_POLYMORPHIC_RELATION(Base, B)

int main()
{

    std::string workPath = "/home/user/"

    {
        std::ofstream os(workPath + "polymorphism_test.json");
        cereal::JSONOutputArchive oarchive(os);

        std::shared_ptr<Base> ptr1 = std::make_shared<A>(123);
        std::shared_ptr<Base> ptr2 = std::make_shared<B>("foobar");

        Config op;
        op.vector.push_back(ptr1);
        op.vector.push_back(ptr2);

        oarchive(op);
    }

    {
        std::ifstream is(workPath + "polymorphism_test.json");
        cereal::JSONInputArchive iarchive(is);
        Config ip;
        iarchive(ip);
    }

    return 0;
}

Output:

{
    "value0": {
        "value0": [
            {
                "polymorphic_id": 2147483649,
                "polymorphic_name": "A",
                "ptr_wrapper": {
                    "id": 2147483649,
                    "data": {
                        "Base": {
                            "common": ""
                        },
                        "value0": 123
                    }
                }
            },
            {
                "polymorphic_id": 2147483650,
                "polymorphic_name": "ClassB",
                "ptr_wrapper": {
                    "id": 2147483650,
                    "data": {
                        "Base": {
                            "common": ""
                        },
                        "value0": "foobar"
                    }
                }
            }
        ]
    }
}
Michał Hanusek
  • 199
  • 3
  • 13