Boost::Serialization and MFC Doc/View architecture

Hi All, 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 CMyDoc.cpp file I have: 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 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. 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. Thanks, Colin

I hope you don't mind, but I've a bit more information on this:
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
Hi All,
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 CMyDoc.cpp file I have:
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
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.
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.
Thanks,
Colin

Colin Caprani wrote:
I hope you don't mind, but I've a bit more information on this:
I'm personally very familiar with MFC and it's implemenation of serialization. In fact, it was this system which inspired me to write the serialization library in the first case. So here are a couple of observations. I've often found the ms implemention often "good enough". This is especially true for those older aps which don't using STL. If they use the MS CArray, etc. It's easier to use the ms serialization since it's already done for these data structures. Many applications I do for customers are much simpler than other stuff I do and there's a strong bias to not add another (large) library so future programmers don't have to deal with a new thing to learn. On the other hand, many times the MFC serialization just can't deal with it. This often happens when I use the CDocument as a "holder" for the "real" data which is more elaborate and exploits all the modern useful machinery like that boost and STL have: variants, stl collections, ranges, signals/slots, etc. etc. This is the way I minimize my work - using MFC for the GUI, COM interfacing and all the other MS/Windows dependent stuff and a member of CDocument for all the heavy lifting. I'm very much into minimizing effort, time and tedium. Also my customers have very high expectations about what an application should do (everything) and low expectations about what it should cost (nothing since programming is "fun"). Sorry I wandered off topic. So, my ap looks like // CMy document // standard MFC implementation for open, new, etc, etc, .... // uses updateallviews and all the standard stuff CMyDocument : public CDocument { // MFC serialize void Serialize(CArchive &ar){ // now bridge to boost if(m_saving){ ostream os = ar.... ; // I forget how I do this - I might even make a new output stream boost::text_oarchive oa(os)[ oa << m_d; } else{ ... } } private: app_data m_d; }; I don't know if that helps, but there it is. It's mainly motivated by the fact that when I try to overload Open, New, I get all sorts of side-effects in MFC that I have to analize and track down. Robert Ramey

On Sun, Jul 3, 2011 at 7:19 PM, Robert Ramey
Colin Caprani wrote:
I hope you don't mind, but I've a bit more information on this:
I'm personally very familiar with MFC and it's implemenation of serialization. In fact, it was this system which inspired me to write the serialization library in the first case. So here are a couple of observations.
Robert, Firstly thank you for replying. Secondly, a bigger thanks for the library! Since I am not an expert/professional programmer, I wasn't sure that I wasn't missing something obvious. Once I read your answer and accepted that for the CMyDoc class itself the serialize(Archive& ar, const unsigned int version) function wasn't a runner I implemented separate boost_save and boost_load functions. However, contrary to what you mentioned, 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. I now have one small(ish) problem: I get a memory exception when I try to delete a pointer to an object created from a load operation in the clear() function. It fails at: _ASSERTE(pHead->nBlockUse == nBlockUse); I think it's because the serialization library allocates on a different heap to the local one. This this sound right? I know it's not great practice but I can probably live with this leak until I implement shared_ptr which should eliminate it - am I right about that?! Thanks for all the help, Colin

Colin Caprani wrote:
On Sun, Jul 3, 2011 at 7:19 PM, Robert Ramey
wrote:
I now have one small(ish) problem: I get a memory exception when I try to delete a pointer to an object created from a load operation in the clear() function. It fails at: _ASSERTE(pHead->nBlockUse == nBlockUse); I think it's because the serialization library allocates on a different heap to the local one. This this sound right? I know it's not great practice but I can probably live with this leak until I implement shared_ptr which should eliminate it - am I right about that?!
My personal experience is that ignoring this will lead to grief down the road. I don't know what your load function does so I can't comment in particular case. Robert Ramey

On Mon, Jul 4, 2011 at 4:55 PM, Robert Ramey
My personal experience is that ignoring this will lead to grief down the road. I don't know what your load function does so I can't comment in particular case.
You're right! I've replicated the problem in a skeletal MFC application. All default options used on creation in VS2010, SDI application, unicode off, shared DLL. I attach the following in case it helps: 1. MyTypes & MyType class definitions file 2. CBoostSerializationTestDoc files 3. BoostSerialize.h boost includes MyTypes is just a collector of MyType, but is stored as an owned pointer in the Doc class. There are two problems that occur: 1. The assertion failure mentioned previously: To replicate this, run the program save a file (first.bst) and save another file (second.bst). Open first.bst, then open second.bst to get failure. This problem is fixed by commenting out the CObject inheretence on the MyType and MyTypes classes (the inheretence is legacy from MFC serialization used in the original app). 2. A memory leak on exit when the Problem #1 assertion is fixed.
From inspection of debug output window. Many 8 bytes, which I suspect as m_Val's of MyType.
I suspect that I may be making a basic error, perhaps not though, and that's why I'm following up. Thanks for any help you can offer, Colin
participants (2)
-
Colin Caprani
-
Robert Ramey