2016-02-13 9 views
6

Ich habe vier Klassen (A, B, C und) nach dem klassischen Diamantmuster und eine Container Klasse mit einem unique_ptr<A>. Ich möchte diese Klassen mithilfe der Serialisierungsbibliothek cereal serialisieren.Virtuelle Vererbung und Polymorphie: Verwirrt die Cerealienbibliothek das Objektlayout?

struct A {int f1; int f2; int f3} 

struct B : public virtual A { 
    template<typename Archive> 
    inline void save(Archive& ar) const { 
     std::cerr << "Saving Obj: " << this << std::endl; 
     std::cerr << "This: " << &(this->f1) << " " 
      << &(this->f2) << " " << &(this->f3) << std::endl; 
     std::cerr << "This: " << this->f1 << " " 
      << this->f2 << " " << this->f3 << std::endl; 
    }; 
} 
}; 

struct C : public virtual A {}; 

struct D : public B, public C {}; 

#include <cereal/archives/binary.hpp> 
CEREAL_REGISTER_TYPE(B); 
CEREAL_REGISTER_TYPE(C); 
CEREAL_REGISTER_TYPE(D); 

struct Container { 
    std::unique_ptr<A> obj; 

    template<typename Archive> 
    inline void save(Archive& ar) const { 
     std::cerr << "Saving Container" << std::endl; 
     std::cerr << "Obj Addr: " << obj.get() << std::endl; 
     std::cerr << "Obj: " << &(obj->f1) << " " << &(obj->f2) 
      << " " << &(pq->f3) << std::endl; 
     std::cerr << "Obj: " << " " << pq->sq_count << " " << pq->sq_bits 
      << " " << pq->dim << std::endl; 
     ar(obj); // Call serialization for obj, ie B.save(...) 
    } 
} 

Alle Klassen haben Getreide save und load Funktionen, aber ich enthalten sie nur für B und Container, da sie die einzigen, die in diesem Beispiel verwendet werden.

Ich benutze diese Klassen wie folgt:

std::unique_ptr<A> obj(new B); 
obj->f1 = 8; 
obj->f2 = 8; 
obj->f3 = 128; 
std::unique_ptr<Container> db(new Container); 
db.obj = std::move(obj); 

std::ofstream out_file(out_filename); 
cereal::BinaryOutputArchive out_archive(out_file); 
out_archive(db); 

Und ich bekomme die folgende Ausgabe:

Saving Container 
Obj Addr: 0x23d2128 
Obj: 0x23d2130 0x23d2134 0x23d2138 // Fields adresses (f1,f2,f3) 
Obj: 8 8 128 // Fields values 
Saving Obj: 0x23d2128 // Same object 
This: 0x23d2118 0x23d211c 0x23d2120 // Different field adresses ! 
This: 4293296 0 37569440 // Garbage 

Meine Frage ist: Ist es wahrscheinlich, dass dies einen Fehler in Getreide ist, oder gibt es etwas, das ich nicht mit virtueller Vererbung bekomme?

Wird erwartet, dass sich die Adressen der Felder eines bestimmten Objekts jemals in einem C++ - Programm ändern?

+0

Warum sollte 'ar (obj)' 'B :: save (ar)' aufrufen? – aschepler

+0

@aschepler Die Getreidebibliothek tut das: http://uscilab.github.io/cereal/serialization_functions.html – Xion345

Antwort

1

Ich kann nicht reproduzieren Sie Ihren Fehler auf dem aktuellen entwickeln Zweig von Getreide, aber ich kann es auf dem aktuellen Master (1.1.2) reproduzieren. Ich änderte den Code zu kompilieren eigentlich:

#include <cereal/types/memory.hpp> 
#include <cereal/types/polymorphic.hpp> 
#include <cereal/archives/json.hpp> 
#include <fstream> 
#include <iostream> 

struct A { 
    int f1; int f2; int f3; 
    virtual ~A() {} 

    template<typename Archive> 
    void serialize(Archive & ar) 
    { 
     std::cerr << "Saving A Obj: " << this << std::endl; 
     std::cerr << "This: " << &(this->f1) << " " 
     << &(this->f2) << " " << &(this->f3) << std::endl; 
     std::cerr << "This: " << this->f1 << " " 
     << this->f2 << " " << this->f3 << std::endl; 
    }; 
}; 

struct B : public virtual A { 
    template <class Archive> 
    void serialize(Archive & ar) 
    { 
    std::cerr << "Saving B Obj: " << this << std::endl; 
    std::cerr << "This: " << &(this->f1) << " " 
     << &(this->f2) << " " << &(this->f3) << std::endl; 
    std::cerr << "This: " << this->f1 << " " 
     << this->f2 << " " << this->f3 << std::endl; 

    ar(cereal::virtual_base_class<A>(this)); 
    } 

    virtual ~B() {} 
}; 

CEREAL_REGISTER_TYPE(B); 

struct Container { 
    std::unique_ptr<A> obj; 

    template<typename Archive> 
     void serialize(Archive & ar) 
    { 
     std::cerr << "Saving Container (A)" << std::endl; 
     std::cerr << "Obj Addr: " << obj.get() << std::endl; 
     std::cerr << "Obj: " << &(obj->f1) << " " << &(obj->f2) 
      << " " << &(obj->f3) << std::endl; 

     ar(obj); // Call serialization for obj, ie B.save(...) 
    } 
}; 

int main() 
{ 
    std::unique_ptr<A> ptr(new B()); 
    ptr->f1 = 8; 
    ptr->f2 = 8; 
    ptr->f3 = 128; 
    std::unique_ptr<Container> db(new Container()); 
    db->obj = std::move(ptr); 

    std::stringstream ss; 
    { 
    cereal::JSONOutputArchive out_archive(ss); 
    out_archive(db); 
    } 

    std::cout << ss.str() << std::endl; 
} 

Die Ausgabe mit 1.1.2:

Saving Container (A) 
Obj Addr: 0x1738d78 
Obj: 0x1738d80 0x1738d84 0x1738d88 
Saving B Obj: 0x1738d78 
This: 0x1738d78 0x1738d7c 0x1738d80 
This: 4316664 0 8 
Saving A Obj: 0x1738d70 
This: 0x1738d78 0x1738d7c 0x1738d80 
This: 4316664 0 8 
{ 
    "value0": { 
     "ptr_wrapper": { 
      "valid": 1, 
      "data": { 
       "value0": { 
        "polymorphic_id": 2147483649, 
        "polymorphic_name": "B", 
        "ptr_wrapper": { 
         "valid": 1, 
         "data": { 
          "value0": {} 
         } 
        } 
       } 
      } 
     } 
    } 
} 

Die Ausgabe mit der Entwicklung:

Saving Container (A) 
Obj Addr: 0x1f74e18 
Obj: 0x1f74e20 0x1f74e24 0x1f74e28 
Saving B Obj: 0x1f74e10 
This: 0x1f74e20 0x1f74e24 0x1f74e28 
This: 8 8 128 
Saving A Obj: 0x1f74e18 
This: 0x1f74e20 0x1f74e24 0x1f74e28 
This: 8 8 128 
{ 
    "value0": { 
     "ptr_wrapper": { 
      "valid": 1, 
      "data": { 
       "value0": { 
        "polymorphic_id": 2147483649, 
        "polymorphic_name": "B", 
        "ptr_wrapper": { 
         "valid": 1, 
         "data": { 
          "value0": {} 
         } 
        } 
       } 
      } 
     } 
    } 
} 

So was dieses Problem verursacht wurde wahrscheinlich fest in der aktuellen Entwicklung Zweig der Getreide, die als 1.2 in naher Zukunft veröffentlicht werden.