c++ - Boost::Serialisierung und MFC Doc / View Architektur




boost-serialization (3)

Ich portiere eine vorhandene MFC C ++ - Anwendung, um Boost :: Serialization für XML-Dateien zu verwenden. Mein CDocument-Objekt enthält alle Daten für die App. Ich habe die Serialisierungsfunktion implementiert als:

template<class Archive>
void CMyDoc::serialize(Archive& ar, const unsigned int version)
{
ar  & BOOST_SERIALIZATION_NVP(m_Param1)
    & BOOST_SERIALIZATION_NVP(m_Param2);
}

Um die Ereignisse speichern und laden zu können, habe ich in der CDoc * .cpp-Datei die Basisklassenfunktionen OnOpenDocument () und OnSaveDocument () überladen, um Boost :: Serialization zu implementieren:

BOOL CMyDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
clear();    // clear current params

//if (!CDocument::OnOpenDocument(lpszPathName)) // Old MFC serialize code
//  return FALSE;

CEvolveTrafficDoc* pDoc = this; // pointers the same here
std::ifstream ifs(lpszPathName);
boost::archive::xml_iarchive ia(ifs);
ia >> boost::serialization::make_nvp("MyDoc",pDoc); // pointer changes here
// *this = *pDoc; // POSSIBLE solution with CMyDoc copy constructor implemented

return TRUE;
}

BOOL CMyDoc::OnSaveDocument(LPCTSTR lpszPathName)
{
//if (!CDocument::OnSaveDocument(lpszPathName)) // Old MFC serialize code
//  return FALSE;

std::ofstream ofs(lpszPathName);
boost::archive::xml_oarchive oa(ofs);
oa << boost::serialization::make_nvp("MyDoc",this);

return TRUE;
}

Das Speichern eines Dokuments funktioniert einwandfrei. Das Problem ist, dass das Laden eines Dokuments nicht funktioniert. Die Boost-Bibliothek scheint das CMyDoc-Objekt zu kopieren, da der Zeiger auf eine andere Adresse zurückkommt. Dies bedeutet, dass die geladene Datei nicht in das aktuelle Dokument geladen wird. Kann ein CDoc mit Boost überschreiben? Es kann mit MFC CArchive.

Ich dachte darüber nach, die Zeile als "MÖGLICHE Lösung" anzuzeigen, aber das würde bedeuten, den Kopierkonstruktor für die CMyDoc-Klasse zu implementieren. Dies beseitigt einen der Vorteile von Boost, indem ich zwei Codezeilen für jede Variable habe: 1. ar & BOOST_SERIALIZATION_NVP (m_Param1) // zum Speichern und Laden nach pDoc 2. this-> m_Param1 = pDoc.m_Param1 // in CMyDoc Kopiere Konstruktor

Wenn ich das CMyView überlasten soll, um die Datei zu öffnen und Ereignisse zu speichern, wird die MRU-Listenverwaltung, die von der Doc / View-Architektur angeboten wird, nicht stattfinden.

Ich bin mir sicher, dass dies eine Million Mal gemacht wurde, aber ich kann keine Informationen online finden. Seltsam! Jede Hilfe sehr geschätzt: D

Wenn ich die Dokumentation näher lese, sehe ich, dass Boost bestätigt, dass jeder serialisierte Zeiger mit einem neuen Schlüsselwort deserialisiert wird: "Die Serialisierung von Zeigern ist in der Bibliothek mit einem Code ähnlich dem Folgenden implementiert:"

// load data required for construction and invoke constructor in place
template<class Archive, class T>
inline void load_construct_data(
Archive & ar, T * t, const unsigned int file_version
){
// default just uses the default constructor to initialize
// previously allocated memory. 
::new(t)T();
}

Die Dokumentation empfiehlt, diese Funktion bei Bedarf zu überladen:

template<class Archive>
inline void load_construct_data(
Archive & ar, my_class * t, const unsigned int file_version
){
// retrieve data from archive required to construct new instance
int attribute;
ar >> attribute;
// invoke inplace constructor to initialize instance of my_class
::new(t)my_class(attribute);
}

Dies würde wiederum dazu führen, dass ein CMyDoc-Kopierkonstruktor implementiert werden muss. Aaarrghhhh !!

Falls es jemandem hilft, habe ich eine Antwort von Robert Ramey bekommen. Im Grunde habe ich nichts Offensichtliches verpasst: Die CMyDoc-Funktion serialize (Archiv & ar, const unsigned int version) war kein Runner, also habe ich separate boost_save- und boost_load-Funktionen implementiert. Ich musste OnOpenDocument und OnSaveDocument überladen, zum Beispiel:

BOOL CMyDoc :: OnOpenDocument (LPCTSTR lpszPfadName) {clear ();

// Call base class function with empty local Serialize function
// to check file exists etc
if (!CDocument::OnOpenDocument(lpszPathName))
    return FALSE;

std::string file( lpszPathName );
boost_load(file);
return TRUE;

}

Dies ist notwendig, da das MFC-CArchive die Datei besitzt, bis die MFC-Serialize-Funktion beendet wird und boost :: serialization den Zugriff auf die Datei verweigert. Selbst das Aufrufen von ar.Abort () in den Serialize-Funktionen funktioniert nicht, da die CDocument-Basisklasse davon ausgeht, dass die AR beim Zurückkehren zur Funktion Serialize der Basisklasse existiert.


Answers

Wenn ich das obige nochmal durchlese, sehe ich, dass es keine vollständige Antwort ist. Hoffentlich ist das hilfreicher.

Zuerst (und wichtig, wie ich es leider herausgefunden habe!), Machen Sie Ihren ersten Parameter zu einer CMyDoc Versionsnummer. Ich habe ein geschütztes Mitglied: unsigned int m_Version; und dann ist der Klassenkonstruktor:

CMyDoc::CMyDoc(): m_Version(1)  // The current file structure version
{
    // construction code
} 

Dies ermöglicht es Ihnen, frühere Dateiversionen einfach zu lesen. Hier sind die vier Funktionen, jeweils zwei zum Laden und Speichern.

Zum Laden:

BOOL CMyDoc::OnOpenDocument(LPCTSTR lpszPathName)
{
    clear();    // avoid memory leaks if necessary

    // Call base class function with empty local Serialize function
    // to check file etc
    if (!CDocument::OnOpenDocument(lpszPathName))
        return FALSE;

    std::string file( lpszPathName );

    try {
        boost_load(file);
    } 
    catch (const boost::archive::archive_exception& ae) 
    {
        CFrameWnd * pFrame = (CFrameWnd *)(AfxGetApp()->m_pMainWnd);
        CView * pView = pFrame->GetActiveView();
        ostringstream str; 
        str << "Problem loading file.\n" 
            << "Boost reports: " << ae.what() << '\n' 
            << "Possibly: " << extraArchiveWhat(ae.what()) 
            << std::ends;
        MessageBox(pView->GetSafeHwnd(),str.str().c_str(), "MyApp", MB_OK|MB_ICONERROR);
        return FALSE;
    }

    // If we get here we have been successful
    return TRUE;
}

int CSimbaDoc::boost_load(std::string file)
{
    std::ifstream ifs(file);
    boost::archive::xml_iarchive ia(ifs);

    int file_version; // local so as not to write over class m_Version
    ia  >> boost::serialization::make_nvp("m_Version", file_version)
        >> BOOST_SERIALIZATION_NVP(m_Param1)
        >> BOOST_SERIALIZATION_NVP(m_Param2);
    if(file_version > 0) // read a variable added to class after version 0
        ia >> BOOST_SERIALIZATION_NVP(m_Param3);

    // and anything else you need to read
}

Und zum Speichern:

BOOL CMyDoc::OnSaveDocument(LPCTSTR lpszPathName)
{
    // Call base class function with empty local Serialize function
    // to check file etc
    if (!CDocument::OnSaveDocument(lpszPathName))
        return FALSE;

    std::string file( lpszPathName );
    boost_save(file);

    return TRUE;
}

int CSimbaDoc::boost_save(std::string file)
{
    std::ofstream ofs(file);
    boost::archive::xml_oarchive oa(ofs);

    oa  << BOOST_SERIALIZATION_NVP(m_Version) // always save current class version
        << BOOST_SERIALIZATION_NVP(m_Param1)
        << BOOST_SERIALIZATION_NVP(m_Param2)
        << BOOST_SERIALIZATION_NVP(m_Param3);
    // and whatever else
}

Es gibt eine sehr saubere Lösung mit Boost.IOStreams:

// We mean to not track this class, or you'll get object-tracking warnings
BOOST_CLASS_TRACKING(MyDoc, boost::serialization::track_never)

void MyDoc::Serialize(CArchive& ar)
{
    namespace io = boost::iostreams;
    io::file_descriptor fd(ar.GetFile()->m_hFile, io::never_close_handle);
    io::stream<io::file_descriptor> file(fd);

    if (ar.IsStoring())
    {
        boost::archive::xml_oarchive oa(file);
        oa << *this;
    }
    else
    {
        boost::archive::xml_iarchive ia(file);
        ia >> *this;
        // then update the views...
    }
}

template<class Archive>
void MyDoc::serialize(Archive & ar, unsigned version)
{
    // Your Boost.Serialization code here
    ar & BOOST_SERIALIZATION_NVP(member);
}

Sie müssen sich nicht mit OnOpenDocument / OnSaveDocument befassen. Überschreiben Sie einfach das CDocument :: Serialize und leiten Sie es an Boost.Serialization weiter.


Der boost::assign überlastet den Kommaoperator stark, um diese Art von Syntax zu erreichen:

vector<int> v; 
v += 1,2,3,4,5,6,7,8,9;






c++ mfc boost-serialization