Boost :: Сериализация и MFC Doc / View архитектура - PullRequest
3 голосов
/ 02 июля 2011

Я портирую существующее приложение MFC C ++ для использования Boost :: Serialization для файлов XML.Мой объект CDocument содержит все данные для приложения.Я реализовал функцию сериализации как:

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

Чтобы захватить события сохранения и загрузки, в файле CDoc * .cpp я перегрузил функции базового класса OnOpenDocument () и OnSaveDocument () для реализации Boost:: Сериализация:

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

Сохранение документа работает нормально.Проблема в том, что загрузка документа не работает.Кажется, что библиотека boost копирует объект CMyDoc, потому что указатель возвращает другой адрес.Это означает, что загруженный файл не загружен в текущий документ.Может ли CDoc перезаписать себя с помощью boost?Это возможно с MFC CArchive.

Я думал о том, чтобы строка была обозначена как «ВОЗМОЖНОЕ решение», но это означало бы реализацию конструктора копирования для класса CMyDoc.Это устраняет одно из преимуществ boost в том, что у меня будет две строки кода для каждой переменной: 1. ar & BOOST_SERIALIZATION_NVP (m_Param1) // для сохранения и загрузки в pDoc 2. this-> m_Param1 = pDoc.m_Param1 // inКонструктор копирования CMyDoc

Если я перегружу CMyView для захвата событий открытия и сохранения файла, управление списком MRU, предлагаемое архитектурой Doc / View, не произойдет.

Я уверен, что этобыло сделано миллион раз, но я не могу найти какую-либо информацию в Интернете.Weird!Любая помощь приветствуется: D


Читая документацию ближе, я вижу, что Boost признает, что любой сериализованный указатель десериализуется с новым ключевым словом: «Сериализация указателей реализована в библиотеке с кодом, похожим наследующее: "

// 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();
}

В документации рекомендуется перегрузить эту функцию при необходимости:

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

Но это опять-таки приведет к необходимости реализации конструктора копирования CMyDoc.Aaarrgghhhh !!


На случай, если это кому-нибудь поможет, у меня был ответ от Роберта Рами об этом.По сути, я не упустил ничего очевидного: функция CMyDoc serialize (Archive & ar, const unsigned int version) не была раннером, поэтому я реализовал отдельные функции boost_save и boost_load.Мне пришлось перегрузить OnOpenDocument и OnSaveDocument, например:

BOOL CMyDoc :: OnOpenDocument (LPCTSTR lpszPathName) {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;

}

Этонеобходимо, поскольку MFC CArchive владеет файлом до тех пор, пока не завершится функция MFC Serialize, что не разрешает boost :: serialization для доступа к файлу.Даже вызов ar.Abort () в функциях Serialize не работает, потому что базовый класс CDocument предполагает, что ar существует после возврата в функцию Serialize базового класса.

Ответы [ 2 ]

2 голосов
/ 10 февраля 2014

Существует очень удобное решение с использованием 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);
}

Вам не нужно беспокоиться о OnOpenDocument / OnSaveDocument.Просто перезапишите CDocument :: Serialize и перешлите его в Boost.Serialization.

1 голос
/ 03 сентября 2011

Перечитывая вышесказанное, я вижу, что это не полный ответ.Надеюсь, это будет более полезным.

Во-первых (и, как я, к сожалению, выяснил!), Сделайте ваш первый параметр CMyDoc переменной номера версии.У меня есть защищенный член: unsigned int m_Version;, а затем конструктор класса:

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

Это позволяет легко читать предыдущие версии файлов.Вот четыре функции, по две для загрузки и сохранения.

Для загрузки:

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
}

И для сохранения:

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