Boost::序列化和 MFC Doc/View 架构
我正在移植现有的 MFC C++ 应用程序以对 XML 文件使用 Boost::Serialization。我的 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 // 在 CMyDoc 复制构造函数中
如果我重载 CMyView 来捕获文件打开和保存事件,则 Doc/View 架构提供的 MRU 列表管理将不会发生。
我确信这已经被执行了一百万次,但我在网上找不到任何信息。诡异的!非常感谢任何帮助: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 复制构造函数。啊啊啊啊!!
如果它对任何人有帮助,我收到了罗伯特·雷米(Robert Ramey)对此的回复。基本上,我没有遗漏一些明显的东西:CMyDoc serialize(Archive& ar, const unsigned int version) 函数不是运行程序,因此我实现了单独的 boost_save 和 boost_load 函数。我必须重载 OnOpenDocument 和 OnSaveDocument,例如:
BOOL CMyDoc::OnOpenDocument(LPCTSTR lpszPathName) { 清除();
// 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 访问该文件。即使在 Serialize 函数中调用 ar.Abort() 也不起作用,因为 CDocument 基类假定 ar 在返回到基类 Serialize 函数时存在。
I'm porting an existing MFC C++ application to use Boost::Serialization for XML files. My CDocument object contains all the data for the app. I've implemented the serialize function as:
template<class Archive>
void CMyDoc::serialize(Archive& ar, const unsigned int version)
{
ar & BOOST_SERIALIZATION_NVP(m_Param1)
& BOOST_SERIALIZATION_NVP(m_Param2);
}
To capture the save and load events, in the CDoc *.cpp file I have overloaded the base class functions OnOpenDocument() and OnSaveDocument() to implement 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;
}
Saving a document works fine. The problem is that loading a document doesn't work. The boost library seems to copy the CMyDoc object because the pointer comes back a different address. This means that the loaded file isn't loaded into the current document. Can a CDoc overwrite itself with boost? It can with MFC CArchive.
I thought about having the line indicated as "POSSIBLE solution", but this would mean implementing the copy constructor for the CMyDoc class. This removes one of the benefits of boost in that I would have two lines of code for each variable:
1. ar & BOOST_SERIALIZATION_NVP(m_Param1) // for saving and loading to pDoc
2. this->m_Param1 = pDoc.m_Param1 // in CMyDoc copy constructor
If I overload the CMyView to capture the file open and save events, the MRU list management offered by the Doc/View architecture won't happen.
I'm sure this has been done a million times, but I can't find any information online. Weird! Any help much appreciated :D
Reading the documentation closer, I see that Boost acknowledges that any serialized pointer is deserialized with a new keyword: "Serialization of pointers is implemented in the library with code similar to the following:"
// 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();
}
The documentation recommends overloading this function if necessary:
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);
}
But this would again result in needed to implement a CMyDoc copy constructor. Aaarrgghhhh!!
In case it helps anyone, I had a reply from Robert Ramey about this. Basically, I wasn't missing something obvious: the CMyDoc serialize(Archive& ar, const unsigned int version) function wasn't a runner, so I implemented separate boost_save and boost_load functions. I had to overload the OnOpenDocument and OnSaveDocument, for example:
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;
}
This is necessary since the MFC CArchive owns the file until the MFC Serialize function exits, disallowing boost::serialization to access the file. Even calling ar.Abort() in the Serialize functions doesn't work because the CDocument base class assumes the ar exists on returning to the base class Serialize function.
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(2)
使用 Boost.IOStreams 有一个非常简洁的解决方案:
您不必为 OnOpenDocument/OnSaveDocument 烦恼。
只需覆盖 CDocument::Serialize 并将其转发到 Boost.Serialization 即可。
There's a very neat solution using Boost.IOStreams:
You don't have to bother with OnOpenDocument/OnSaveDocument.
Just overwrite the CDocument::Serialize and forward it to Boost.Serialization.
重新阅读上面的内容,我发现这不是一个完整的答案。希望这更有帮助。
首先(重要的是,正如我遗憾地发现的那样!),将第一个参数设置为
CMyDoc
版本号变量。我有一个受保护的成员:unsigned int m_Version;
然后类构造函数是:这使您可以轻松读取以前的文件版本。这是四个函数,加载和保存各两个。
用于加载:
用于保存:
Re-reading the above I see it's not a complete answer. Hopefully this is more helpful.
Firstly (and importantly as I regrettably found out!), make your first parameter a
CMyDoc
version number variable. I have a protected member:unsigned int m_Version;
and then the class constructor is:This allows you to read previous file versions easily. Here are the four functions, two each for loading and saving.
For loading:
And for saving: