Boost serialization and pointers to parent class - serialization

Assuming the following simplified code:
Parent class
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/unique_ptr.hpp>
class Parent
{
public:
Parent();
...
private:
// boost serialization
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & *m_child;
}
std::unique_ptr<Child> m_child;
}
Parent::Parent()
{
...
m_child = std::unique_ptr<Child>(new Child(this));
}
Child class
#include <boost/archive/text_oarchive.hpp>
#include <boost/archive/text_iarchive.hpp>
#include <boost/serialization/unique_ptr.hpp>
class Child
{
public:
Child(Parent * parent)
{
m_parent = parent;
};
...
private:
// boost serialization
friend class boost::serialization::access;
template<class Archive>
void serialize(Archive & ar, const unsigned int version)
{
ar & m_parent;
}
Child(){};
Parent m_parent{ nullptr };
}
My parent class creates a child class and passes a pointer to itself stored by the child.
When archiving the parent class, it archives the child which archives the pointer to the parent.
When restoring the parent class, it creates the child class and restores it as well. My question is this:
Will the pointer in the child class get restore properly so it is guaranteed to point to it's parent?
This SEEMS to work which I've verified by stepping through the debugger and comparing the values of the Parent's this pointer with the child's m_parent. However, I can't figure out how boost pulls this off and it makes me wonder if I'm just 'getting lucky'. Can anyone verify that boost serialization is smart enough to maintain the child::m_parent->parent relationship?
Or should I be taking the extra step of not archiving the pointer and setting it after loading the object?

Two things:
Object Tracking facilitates this. Object Tracking is enabled by default when serializing pointers.
No, you do not serialize the unique_ptr like that.
In fact, there's little reason to serialize the m_parent of the child, either, because the parent can set the self-reference from within the load handler.
But assuming that you want the easy way, let's stick with that idea and make it work due to Object Tracking
DEMO
I included some tricks to demonstrate that deserialization
correct de-duplicates pointers to the same object
restores the member pointers even if we forcefully "break" the invariants prior to deserialization
Live On Coliru
#include <boost/archive/text_iarchive.hpp>
#include <boost/archive/text_oarchive.hpp>
#include <boost/serialization/unique_ptr.hpp>
#include <memory>
#include <sstream>
#include <iostream>
class Parent;
class Child {
public:
Child(Parent *parent)
: m_parent(parent), m_duplicate_parent(parent) {}
Parent *m_parent { nullptr };
Parent *m_duplicate_parent{ nullptr };
private:
friend class boost::serialization::access;
template <class Archive> void serialize(Archive &ar, unsigned) {
ar & m_parent & m_duplicate_parent;
}
Child() = default;
};
class Parent {
public:
Parent() : m_child(std::make_unique<Child>(this)) {}
//private:
friend class boost::serialization::access;
template <class Archive> void serialize(Archive &ar, unsigned) {
ar & m_child;
}
std::unique_ptr<Child> m_child;
};
int main() {
std::stringstream ss;
{
boost::archive::text_oarchive oa(ss);
Parent p;
assert(&p == p.m_child->m_parent);
assert(&p == p.m_child->m_duplicate_parent);
oa << p;
}
std::cout << ss.str();
{
boost::archive::text_iarchive ia(ss);
Parent p;
// let's purposelly break the invariants so we know deserialization
// does restore them as required, and we not just "getting lucky":
p.m_child->m_parent = nullptr;
p.m_child->m_duplicate_parent = nullptr;
assert(&p != p.m_child->m_parent);
assert(&p != p.m_child->m_duplicate_parent);
// nuclear:
p.m_child.reset();
// now deserialize
ia >> p;
assert(&p == p.m_child->m_parent);
assert(&p == p.m_child->m_duplicate_parent);
}
}
Prints something similar to
22 serialization::archive 15 1 0
0 0 0 2 1 0
1 0 0 0 0

Related

How to serialize std::chrono::duration with Boost?

Is it possible to serialize std::chrono::duration or especially std::chrono::milliseconds with boost::serialization?
The following error occurred:
"struct std::chrono::duration >’ has no member named ‘serialize"
I need this for my template-class:
#include <iostream>
#include <chrono>
#include <boost/serialization/serialization.hpp>
template<typename T>
class Field {
public:
// Default Constructor
Field() {}
Field(std::string name, T value) :
mName(name), mValue(value) {}
~Field() {}
const std::string& getName() const {
return mName;
}
T getValue() const {
return mValue;
}
private:
friend class boost::serialization::access;
template<typename Archive>
void serialize(Archive& archive, const unsigned int) {
archive & boost::serialization::make_nvp("Name", mName);
archive & boost::serialization::make_nvp("Value", mValue);
}
std::string mName;
T mValue;
};
BOOST_CLASS_IMPLEMENTATION( Field<int>, boost::serialization::object_serializable )
BOOST_CLASS_IMPLEMENTATION( Field<std::chrono::milliseconds>, boost::serialization::object_serializable )
I'm creating the following object:
Field<std::chrono::milliseconds> mSimTime;
Let's say you have:
auto earlier = std::chrono::system_clock::now();
auto now = std::chrono::system_clock::now();
auto aLotOfMilliseconds= std::chrono::duration_cast<std::chrono::milliseconds>(
now - earlier);
// Serialize here int64_t.
then after deserialization, you have:
std::chrono::duration<int64_t, std::milli> duration(aLotOfMilliseconds);

Got 2 errors in my program :"use of undeclared identifier 'front'" and "call to non-static member function without an object argument"

I'm trying to call some functions from GenDList.h inside GenQueue.h after including, but I'm getting a couple errors.
This is my header, GenDList.h:
#ifndef GENDLIST_H
#define GENDLIST_H
#include <iostream>
#include "GenListNode.h"
using namespace std;
template <class T>
class GenDList
{
public:
GenListNode<T> *front;
GenListNode<T> *back;
unsigned int size;
GenDList();
~GenDList();
void insertFront(T data);
void insertBack(T data);
bool insertAfter(int key, T data);
T removeFront();
T removeBack(); //for doubly
T removeAt(int position);
int isEmpty();
unsigned int getSize();
void printList();
T find(T d);
};
#endif
This is my other header that I included the previous header file into, GenQueue.h:
#ifndef GENQUEUE_H
#define GENQUEUE_H
#include <iostream>
#include "GenDList.h"
using namespace std;
template <class T>
class GenQueue
{
public:
GenQueue();
~GenQueue();
void insert(T data);
T remove();
T peek();
int isEmpty();
int isFull();
};
#endif
//***********implementation***************
template <class T>
GenQueue<T>::GenQueue()
{
GenDList<T> list;
}
template <class T>
GenQueue<T>::~GenQueue()
{
}
template <class T>
void GenQueue<T>::insert(T data)
{
GenDList<T>::insertBack(data);
}//error: call to non-static member function without an object argument
template <class T>
T GenQueue<T>::remove()
{
GenDList<T>::removeFront();
}
template <class T>
T GenQueue<T>::peek()
{
return front; //error: use of undeclared identifier here
}

Why a serializable class must have the function that get the instance from class name?

Such as mfc, it should add
DECLARE_SERIAL(CGraph)
If I a have a class,
class A
{
int a,b;
};
I can store the value of a and b to a file ,then read it.
So I couldn't understand why DECLARE_SERIAL(CGraph) used.
The DECLARE_SERIAL and IMPLEMENT_SERIAL macros are only necessary for classes derived from CObject that you wish to serialize polymorphically using the framework provided by MFC.
If your class is not derived from CObject and/or you do not wish to use MFC's serialization polymorphically (i.e. via a pointer to CObject), then of course you can implement your own solution as you rightly say.
For example, DECLARE_SERIAL(CMyClass) expands to the following code that goes in your class declaration
protected:
static CRuntimeClass* __stdcall _GetBaseClass();
public:
static CRuntimeClass classCMyClass;
static CRuntimeClass* __stdcall GetThisClass();
virtual CRuntimeClass* GetRuntimeClass() const;
static CObject* __stdcall CreateObject();
friend CArchive& __stdcall operator>>(CArchive& ar, CMyClass* &pOb);
and IMPLEMENT_SERIAL(CMyClass, CObject, VERSIONABLE_SCHEMA | 1) expands to the following code that goes in the cpp file
CObject* __stdcall CMyClass::CreateObject()
{
return new CMyClass;
}
extern AFX_CLASSINIT _init_CMyClass;
CRuntimeClass* __stdcall CMyClass::_GetBaseClass()
{
return (CObject::GetThisClass());
}
__declspec(selectany) CRuntimeClass CMyClass::classCMyClass =
{
"CMyClass", sizeof(class CMyClass), (0x80000000) | 1,
CMyClass::CreateObject, &CMyClass::_GetBaseClass, 0, &_init_CMyClass
};
CRuntimeClass* __stdcall CMyClass::GetThisClass()
{
return ((CRuntimeClass*)(&CMyClass::classCMyClass));
}
CRuntimeClass* CMyClass::GetRuntimeClass() const
{
return ((CRuntimeClass*)(&CMyClass::classCMyClass));
}
AFX_CLASSINIT _init_CMyClass((CMyClass::GetThisClass()));
CArchive& __stdcall operator>>(CArchive& ar, CMyClass* &pOb)
{
pOb = (CMyClass*) ar.ReadObject((CMyClass::GetThisClass()));
return ar;
}
As it says in MSDN it is also possible to use serialization without using the above macros:

segfault happens when I serialize nested class in apache module

Serializing simple class "A" in Apache module done without error but when I tried to serialize my complex object like "X" which has a member, type of "A", I got segfault in Apache module. ( this doesn't happen to a executable console application )
------------------------- here is my code : ---------------------
class A {
private:
friend class boost::serialization::access; // to enable boost "access" class to call private "serialize" method of class "A"
template<class ArchT>
void serialize(ArchT &ar, unsigned int version) { // method for both serializing and deserializing
ar & memA; // (de)serialize member "memA"
}
std::string memA; // sample member
public:
A(){}
A(std::string pmemA) :
memA(pmemA) {
}
std::string GetMemA()
{
return memA;
}
};
class X {
private:
friend class boost::serialization::access;
template<class ArchT>
void serialize(ArchT &ar, unsigned int version) {
ar & memX;
ar & a;
}
std::string memX;
A a;
public:
X(std::string pmemX, A pa) :
memX(pmemX), a(pa) {
}
X(){}
};
-------------------
string st=GetRandomFileName();
ofstream out(st.c_str());
boost::archive::text_oarchive out_r(out);
A a("Amem");
X x("Xmem", a);
out_r << x; // not works
out_r << a; // works!
------------------- here is stack trace from gdb for apache ----------------
boost::serialization::typeid_system::extended_type_info_typeid_0::is_less_than(boost::serialization::extended_type_info const&) const () from /tmp/libIRSProWebApacheModule.so
2 0xb7223c61 in std::_Rb_tree

how to access variable from one class in another in QT mobility?

I have two classes: first generate position data (latitude and longitude), how I can access this data (variables latitude and longitute) in second class? becouse in second class I get crazy number(
Here are headers and classes:
first header:
#ifndef MAINWINDOW_H
#define MAINWINDOW_H
#include <QMainWindow>
#include <QGeoPositionInfoSource>
#include <QGeoPositionInfo>
#include <QtCore/QPointer>
#include <QGeoSatelliteInfo>
#include <QGeoSatelliteInfoSource>
#include "gpsform.h"
QTM_USE_NAMESPACE
namespace Ui {
class MainWindow;
}
class MainWindow : public QMainWindow
{
Q_OBJECT
public:
explicit MainWindow(QWidget *parent = 0);
~MainWindow();
double latitude;
double longitude;
double altitude;
double speed;
public slots:
void positionUpdated(QGeoPositionInfo geoPositionInfo);
private:
Ui::MainWindow *ui;
QPointer<QGeoPositionInfoSource> locationDataSource;
private slots:
void on_pushButton_2_clicked();
void on_pushButton_4_clicked();
void startGPS();
void on_pushButton_clicked();
signals:
void updated();
};
#endif // MAINWINDOW_H
first class
#include "mainwindow.h"
#include "ui_mainwindow.h"
#include "quitdiallog.h"
#include <QGeoCoordinate>
#include <QDebug>
#include <QtGui/QMessageBox>
#include <QList>
#include "gpsform.h"
MainWindow::MainWindow(QWidget *parent) :
QMainWindow(parent),
ui(new Ui::MainWindow)
{
setWindowTitle("Мой кОмпаС");
ui->setupUi(this);
startGPS();
}
MainWindow::~MainWindow()
{
delete ui;
}
void MainWindow::startGPS()
{
// Obtain the location data source if it is not obtained already
if (!locationDataSource)
{
locationDataSource =
QGeoPositionInfoSource::createDefaultSource(this);
if (locationDataSource)
{
// Whenever the location data source signals that the current
// position is updated, the positionUpdated function is called.
QObject::connect(locationDataSource,
SIGNAL(positionUpdated(QGeoPositionInfo)),
this,
SLOT(positionUpdated(QGeoPositionInfo)));
// Start listening for position updates
locationDataSource->setUpdateInterval(100);
locationDataSource->setPreferredPositioningMethods(QGeoPositionInfoSource::SatellitePositioningMethods);
locationDataSource->startUpdates();
} else {
// Not able to obtain the location data source
// TODO: Error handling
}
} else {
// Start listening for position updates
locationDataSource->setUpdateInterval(5000);
locationDataSource->startUpdates();
}
}
void MainWindow::positionUpdated(QGeoPositionInfo geoPositionInfo)
{
//gpsform *gpf=new gpsform;
if (geoPositionInfo.isValid())
{
//locationDataSource->stopUpdates();
QGeoCoordinate geoCoordinate = geoPositionInfo.coordinate();
latitude = geoCoordinate.latitude();
longitude = geoCoordinate.longitude();
altitude=geoCoordinate.altitude();
ui->label->setNum(latitude);
ui->label_2->setNum(longitude);
/*if(QGeoPositionInfo::GroundSpeed)
{
speed=QGeoPositionInfo::GroundSpeed;
ui->label_4->setNum(speed);
}*/
emit updated();
//gpf->latitude=this->latitude;
//gpsform *gpf=new gpsform;
//gpf->show();
//gpf->latitude=latitude;
}
}
void MainWindow::on_pushButton_clicked()
{
/*ui->label_3->setNum(latitude);
qDebug()<<latitude<<" "<<longitude<<" "<<altitude;*/
gpsform *gps=new gpsform;
this->hide();
gps->show();
}
void MainWindow::on_pushButton_4_clicked()
{
QuitDiallog *qi=new QuitDiallog;
this->hide();
qi->show();
}
void MainWindow::on_pushButton_2_clicked()
{
ui->label_3->setNum(latitude);
}
second header
#ifndef GPSFORM_H
#define GPSFORM_H
#include <QWidget>
#include "mainwindow.h"
QTM_USE_NAMESPACE
namespace Ui {
class gpsform;
}
class gpsform : public QWidget
{
Q_OBJECT
public:
explicit gpsform(QWidget *parent = 0);
~gpsform();
double latitude;
private:
Ui::gpsform *ui;
private slots:
void on_pushButton_clicked();
void updatedata();
};
#endif // GPSFORM_H
second class:
#include "gpsform.h"
#include "ui_gpsform.h"
#include "mainwindow.h"
#include <QTimer>
#include <QDebug>
gpsform::gpsform(QWidget *parent) :
QWidget(parent),
ui(new Ui::gpsform)
{
ui->setupUi(this);
/*ui->label->setNum(mw->latitude);*/
/* QTimer * timer = new QTimer(this);
connect(timer,SIGNAL(timeout()),this,SLOT(update()));
timer->start(50);*/
/* MainWindow *mw = new MainWindow;
QObject::connect(mw,SIGNAL(updated()),this,SLOT(updatedata()));*/
}
gpsform::~gpsform()
{
delete ui;
}
void gpsform::updatedata()
{
/* MainWindow *mw = new MainWindow;
this->latitude=mw->latitude;
ui->label->setNum(mw->latitude);*/
}
void gpsform::on_pushButton_clicked()
{
MainWindow *mw = new MainWindow;
//latitude=mw->latitude;
qDebug()<<mw->latitude;
ui->label->setNum(latitude);
}
For example I want to see latitude in second class, by pressing button. In future I'll do this by Signal/slot to generate label text every time, the position is updated. But now I'll get crazy number. Help me please
MainWindow *mw = new MainWindow;
//latitude=mw->latitude;
qDebug()<<mw->latitude;
You create a new instance of MainWindow and directly access its latitude member. But was it initialized? I see the only place writing into this member in MainWindow is positionUpdated. Are you sure this method gets invoked before you access mw->latitude? You could easily verify that with another qDebug printout from positionUpdated, or by using the debugger.
To comment on the code style in general - it's not good practice to directly access members of other classes like this. One of the problems (as you've just encountered!) with this approach is that the class has no way to actually control the validity of its member. Even the most basic solution - having an accessor method instead of raw member access could do wonders for you here, because the accessor method could potentially check if the value has been computed (or even compute it lazily).