3

我正在移植现有的 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::Serialization:

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 以捕获文件打开和保存事件,则 Doc/View 架构提供的 MRU 列表管理将不会发生。

我敢肯定这已经做了一百万次,但我在网上找不到任何信息。诡异的!非常感谢任何帮助:D


仔细阅读文档,我看到 Boost 承认任何序列化的指针都使用 new 关键字进行反序列化:“指针的序列化在库中实现,代码类似于以下内容:”

// 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 复制构造函数。啊啊啊!!


如果它对任何人有帮助,我收到了 Robert Ramey 的回复。基本上,我并没有遗漏一些明显的东西: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 序列化函数退出,不允许 boost::serialization 访问该文件。即使在 Serialize 函数中调用 ar.Abort() 也不起作用,因为 CDocument 基类假定 ar 在返回基类 Serialize 函数时存在。

4

2 回答 2

2

使用 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。

于 2014-02-10T13:26:50.380 回答
1

重新阅读以上内容,我发现这不是一个完整的答案。希望这更有帮助。

首先(重要的是我很遗憾地发现!),将您的第一个参数设为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
}
于 2011-09-03T01:48:03.833 回答