2016-02-13 14 views
6

Ho quattro classi (A, B, C e D) seguendo il classico motivo a rombi e una classe Container contenente un unique_ptr<A>. Voglio serializzare queste classi usando la libreria di serializzazione cereal.Ereditarietà virtuale e polimorfismo: la libreria dei cereali è in disordine con il layout degli oggetti?

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(...) 
    } 
} 

tutte le classi hanno cereali save e load funzioni, ma io li inclusi solo per B e Container, in quanto sono gli unici utilizzati in questo esempio.

Io uso queste classi come segue:

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); 

E ottengo il seguente output:

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 

La mia domanda è: E 'probabile che questo un bug nei cereali, o c'è qualcosa che non ottengo con l'ereditarietà virtuale?

È previsto che gli indirizzi dei campi di un dato oggetto cambino mai in un programma C++?

+0

Perchè 'ar (obj)' chiama 'B :: save (ar)'? – aschepler

+0

@aschepler La libreria cereali fa questo: http://uscilab.github.io/cereal/serialization_functions.html – Xion345

risposta

1

Non riesco a riprodurre il tuo errore sull'attuale ramo di sviluppo di cereali, tuttavia posso riprodurlo sul master corrente (1.1.2). Ho modificato il codice per compilare effettivamente:

#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; 
} 

L'uscita con 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": {} 
         } 
        } 
       } 
      } 
     } 
    } 
} 

L'output utilizzando sviluppano:

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": {} 
         } 
        } 
       } 
      } 
     } 
    } 
} 

Quindi, qualunque fosse la causa di questo problema è probabile fisso nell'attuale sviluppo del ramo di cereali, che verrà rilasciato come 1.2 nel prossimo futuro.

Problemi correlati