MFC: CObject class (Part – 2)

CObject supports object serialization. One of the MFC’s great feature, which allows to store and retrieve the object’s current state. To enable this feature in our code:

  • We must add DECLARE_SERIAL macro into our class declaration.
  • IMPLEMENT_SERIAL macro must be added into class implementation file.
  • And override the Serialize member function.

Lets extend our class to support serialization.

Add DECLARE_SERIAL macro into “Shape.h” file. We have created this file in our previous articles.

DECLARE_SERIAL(Shape, CObject);

Add IMPLEMENT_SERIAL macro into “Shape.cpp” file.

IMPLEMENT_SERIAL(Shape, CObject, 1);

The last parameter in IMPLEMENT_SERIAL macro is an unsigned integer value used for versionable schema to handle the data created by previous versions of the application.

Remember that we need to overwrite previous macros for dynamic object creation with our new serialization support macros. These macros also supports dynamic object creation along with object serialization support.

Just add a member m_area to our Shape class to store and retrieve area information to and from the Archive. This data member holds the area value of Shape‘s object.

Now we need to override CObject‘s Serialize member.

Lets add its declaration into “Shape.h” file.

virtual void Serialize(CArchive& ar);

Now add its implementation in “Shape.cpp” file.

void Shape::Serialize(CArchive& ar)
{
	CObject::Serialize(ar);

	if ( ar.IsLoading() )
	{
		ar >> m_area;
	}
	else
	{
		ar << m_area;
	}
}

We need to pass CArchive object through Serialize function. CArchive class provides the methods to read or write serializable objects to or from disk file or memory file. CArchive object can either read or write the data; it can’t read and write the data at a time. CArchive object can be created using MFC’s CFile object. CFile class is a base class for MFC’s file classes which deals with files.

So, before calling Serialize function to serialize our class’s data; we need to create a CArchive object using CFile object and call Serialize function by passing CArchive object as a parameter. The code will looks like below:

CFile file("Shape-Archive-Test.tmp", CFile::modeCreate | CFile::modeWrite);
CArchive ar(&file, CArchive::store);
shape.Serialize(ar);
ar.Close();
file.Close();

Above code creates a file object using CFile and which creates “Shape-Archive-Test.tmp” file for writing. The code creates CArchive object using CFile object. As we discussed earlier, at a time CArchive object can do either store the data or load the data; it will not do both operations at a time. Hence we need to create separate CArchive objects for storing and loading the data. And we must set the flag to tell whether the CArchive object is for storing the data or loading the data through its constructor. From above code, CArchive::store represents, the CArchive object is meant for storing the data.

After adding these, the code looks like below:

The “Shape.h” header file:

// Shape.h
//
#include <afx.h>
#include <iostream>

class Shape : public CObject
{
public:
	Shape();

	float m_area;

	virtual void Serialize(CArchive& ar);

	DECLARE_SERIAL(Shape);
};

The Shape class implementation file “Shape.cpp”:

// Shape.cpp
//
#include "Shape.h"

using namespace std;

IMPLEMENT_SERIAL(Shape, CObject, 1);

Shape::Shape()
{
	m_area = 0.0f;
}

void Shape::Serialize(CArchive& ar)
{
	CObject::Serialize(ar);

	if ( ar.IsLoading() )
	{
		ar >> m_area;
	}
	else
	{
		ar << m_area;
	}
}

// -- main()
//
void main()
{
	int input = 0; // 0 - invalid option, 1 - Store data to Archive, 2 - Load data from Archive

	cout << "Enter option: \n1 - Store data to Archive\n2 - Load data from Archive\"" << endl; 	cin >> input;

	switch (input)
	{
	case 1: // Store data to Archive
		{
			Shape shape;
			shape.m_area = 245.67f;

			cout << "The area value " << shape.m_area << " is storing into the Archive.";

			CFile file("Shape-Archive-Test.tmp", CFile::modeCreate | CFile::modeWrite);
			if ( file.m_hFile != NULL )
			{
				CArchive ar(&file, CArchive::store);
				shape.Serialize(ar);
				ar.Close();
				file.Close();
			}
			else
				cout << "Error: Unable to create the file." << endl;
		}
		break;

	case 2: // Load data from Archive
		{
			Shape shape;

			cout << "Beforing loading the value from Archive, the area value is " << shape.m_area << "." << endl;

			try
			{
				CFile file("Shape-Archive-Test.tmp", CFile::modeRead);

				CArchive ar(&file, CArchive::load);
				shape.Serialize(ar);
				ar.Close();
				file.Close();

				cout << "After loading the value from Archive, the area value is " << shape.m_area << "." << endl;
			}
			catch(...)
			{
				cout << "Error: Unable retrieve information from Archive." << endl;
			}
		}
		break;

	default:
		cout << "Invalud option." << endl;
	}
}

Now compile the file “Shape.cpp”.

cl /EHsc Shape.cpp

Run the program and type the option “1” to test how the data is archiving and with option “2” you can test how the data is loading from Archive.

Observe that the Shape object’s member m_area value is assigning it from Archive in case of option “2”. It means, the application loads the Shape object’s state from Archive.

Lets look at other features of CObject in our next article.

**

2 comments for “MFC: CObject class (Part – 2)

  1. Pingback: budowa

Leave a Reply