Save and Restore

.

A general purpose manager for saving and restoring values and objects

Quite often when writing code, one needs to save a value or object, perform some operation on it, and then restore the original value. This mean creating a variable to hold the saved state, and initialising it, at the beginning of a block, and then writing code to restore it at the end. Unfortunately, the code needed to save and restore an object is often dependant on the object itself. For example, a device context (CDC) uses SaveDC and RestoreDC; the working directory uses ::GetCurrentDirectory and ::SetCurrentDirectory; a document might serialise to/from an in-memory file.

The following two template classes encapsulate the save and restore procedure. They are general enough for most situations.

The TSaveAndRestoreGeneral template class is, as the name implies, the more general of the two. It requires that you write a global Save and Restore function for the object you are managing. This template class has two paramters. The first is the type of object to be managed, and the second the type of value used to hold the state information. The state information may often not be the same type as the object being managed. For example, a CDC uses an int as its state value (using SaveDC and RestoreDC).

The TSaveAndRestoreSimple class is for the simpler cases where the object being managed and the state information are the same type. In other words, one simply copies the original value to save, and copies it back to restore. The class includes the simple Save and Restore functions so you don't need to write them yourself.

Here is the source code for these two template classes .

// SaveAndRestore.h
// (c) 1997 Roger Onslow
#ifndef _TSaveAndRestore_
#define _TSaveAndRestore_

template <class T, class TSAVE> class TSaveAndRestoreGeneral {

     // Write specific versions of these for the object type you want to manage
public:
     friend void Save(const T& t, TSAVE& savevalue);
     friend void Restore(T& t, const TSAVE& savevalue);

     // Management of save/restore - not usually directly called
protected:
     T& m_variable; // reference to variable we are managing here
     TSAVE m_savevalue;  // the saved value
public:
     void SaveInto(TSAVE& savevalue) { ::Save(m_variable,savevalue); }
     void SetFrom(const TSAVE& setvalue) { ::Restore(m_variable,setvalue); }
     void RestoreNow() { SetFrom(m_savevalue); }

     // Control auto-restore
protected:
     bool m_bKeep;  // is set, do NOT restore in constructor
public:
     void Keep() { m_bKeep = true; }
     void NoKeep() { m_bKeep = false; }

     // Constructor and destructor do the save and restore
public:
     TSaveAndRestoreGeneral(T& t) : m_variable(t), m_bKeep(false) { SaveInto(m_savevalue); }
     ~TSaveAndRestoreGeneral() { if (! m_bKeep) RestoreNow(); }
};

template <class T> class AFX_EXT_CLASS TSaveAndRestoreSimple : public TSaveAndRestoreGeneral {
     // Simple save and restore functions just take current value
     friend void Save(const T& t, T& savevalue) { savevalue = t; }
     friend void Restore(T& t, const T& savevalue) { t = savevalue; }

     // Constructor just calls base class
public:
     TSaveAndRestoreSimple(T& t) : TSaveAndRestoreGeneral(t) {}
};

#endif

The TSaveAndRestoreGeneral class includes some additional member functions to change the way the save and restore works.

Keep() turns off the automatic restore. In other words, if you make a change to the managed object and want to keep it, rather that restore the previous value, then call the Keep() function. This can be used for an undo/redo situation, where you may make a change and then optionally want to undo it. NoKeep() is the opposite function, and turns the auto-restore back on.

RestoreNow() lets you restore the original value at any time, not just when the save/restore manager goes out of scope.

SetFrom() and SaveInto() let you save and restore the state to an external state variable. These functions would not usually be called outside their use internally in the TSaveAndRestoreGeneral class, but have been made public "just in case".

Below is an example of how to define a save and restore manager for a simple class (like CString) using TSaveAndRestoreSimple.

typedef TSaveAndRestoreSimple CSaveAndRestoreString;

And here is an example of how it would be used.

class CMyClass {
     CString m_mystring;
     void MyFunc () {
          CSaveAndRestoreString saveit(m_mystring);
          // ...
          // do things to m_mystring
          // ...
          // m_mystring is restored at the end
     }
};

When member function ?MyFunc? starts, the current value of ?m_mystring? is saved. At the end of the function, when ?saveit? goes out of scope, the value of ?m_mystring? is restored.

A more complex example would is to save and restore a device context. In this case, a simple copy of the object is not appropriate, so we use TSaveAndRestoreGeneral. A CDC uses an int to save and restore its current state, so we use as our template parameters.

// SaveAndRestoreDC.h
// (c) 1997 Roger Onslow
#ifndef _CSaveAndRestoreDC_
#define _CSaveAndRestoreDC_

#include "SaveAndRestore.h"

// Save and Restore a DC - encapulates SaveDC and RestoreDC
inline void Save(const CDC& dc, int& savevalue) {
     savevalue = const_cast(&dc)->SaveDC();
}
inline void Restore(CDC& dc, const int& savevalue) {
     dc.RestoreDC(savevalue);
}

typedef TSaveAndRestoreGeneralint> CSaveAndRestoreDC;

#endif

Here, we have to write our Save and Restore functions. Notice the const_cast required in the Save routine because, unfortunately, SaveDC is NOT a const function (even though it does not change the CDC object).

A more interesting example is to use these classes to save and restore the current working directory.

Firstly, we need a class that corresponds to, and encapsulates, the current directory functions.

// CurrentDirectory.h
// (c) 1997 Roger Onslow
#ifndef _CCurrentDirectory_
#define _CCurrentDirectory_

class AFX_EXT_CLASS CCurrentDirectory {
     // Construction
public:
     CCurrentDirectory() {}   // nothing to do
     CCurrentDirectory(LPCTSTR dir) { *this = dir; }    // do an assignment

     // Convert to/from string
public:
     operator CString() const;     // return current dir as a string
     CCurrentDirectory& operator= (LPCTSTR dir);   // set current dir
};

#endif

This class lets you treat the current directory as a string. You can assign a string to a variable of type CCurrentDirectory, and this will change the current directory. Similarly you can use a value of type CCurrentDirectory whereever you need a CString, and it will return the current directory name. Notice that this object does not require any storage - it has no member variables and no virtual functions.

Using this class, we can easily make a new class that saves and restores the current directory.

// SaveAndRestoreCurrentDirectory.h
// (c) 1997 Roger Onslow
#ifndef _CSaveAndRestoreCurrentDirectory_
#define _CSaveAndRestoreCurrentDirectory_

#include "SaveAndRestore.h"
#include "CurrentDirectory.h"

// Save and Restore current directory
inline void Save(const CCurrentDirectory& cd, CString& savevalue) 
{savevalue = cd; }

inline void Restore(CCurrentDirectory& cd, const CString& savevalue) 
{ cd = savevalue; }

class AFX_EXT_CLASS CSaveAndRestoreCurrentDirectory : 
	public TSaveAndRestoreGeneral 
{
     CCurrentDirectory m_cd;
public:
     CSaveAndRestoreCurrentDirectory(LPCTSTR str) : m_cd(),
		TSaveAndRestoreGeneral(m_cd) { m_cd = str; }
};

#endif

This class is slightly different to the previous classes in that the constructor takes the name of the temporary current directory required.

You can use this function in you OnFileOpen, OnFileSave and OnFileSaveAs functions to force these to look in a given directory. For example

void CMyDoc::OnFileSave( ) {
     CSaveAndRestoreCurrentDirectory saveandrestore(DATADIR);
     CDocument::OnFileSave();
}

void CMyDoc::OnFileSaveAs( ) {
     CSaveAndRestoreCurrentDirectory saveandrestore(DATADIR);
     CDocument::OnFileSaveAs();
}

void CMyApp::OnFileNew( ) {
     CSaveAndRestoreCurrentDirectory saveandrestore(DATADIR);
     CWinApp::OnFileNew();
}

Here, DATADIR would be defined as the directory for your data files. For example, you might use a variable that is initialised from a registry setting.

In summary, these classes take a lot of the burden out of saving and restoring objects and values. The specific logic to so for a given object type can be encapsulated in a class and reused whenever it is required.



Comments

  • What is wrong with this code?

    Posted by Legacy on 06/28/2001 12:00am

    Originally posted by: sally

    template <class T>
    
    class CrfTransitory
    {
    public:
    CrfTransitory( T& p_src, const T& p_useVal )
    : m_src( &p_src ),
    m_oldVal( p_src )
    {
    *m_src = p_useVal;
    }

    ~CrfTransitory()
    {
    *m_src = m_oldVal;
    }

    private:
    T* const m_src;
    const T m_oldVal;
    };

    Reply
Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • The explosion in mobile devices and applications has generated a great deal of interest in APIs. Today's businesses are under increased pressure to make it easy to build apps, supply tools to help developers work more quickly, and deploy operational analytics so they can track users, developers, application performance, and more. Apigee Edge provides comprehensive API delivery tools and both operational and business-level analytics in an integrated platform. It is available as on-premise software or through …

  • Java developers know that testing code changes can be a huge pain, and waiting for an application to redeploy after a code fix can take an eternity. Wouldn't it be great if you could see your code changes immediately, fine-tune, debug, explore and deploy code without waiting for ages? In this white paper, find out how that's possible with a Java plugin that drastically changes the way you develop, test and run Java applications. Discover the advantages of this plugin, and the changes you can expect to see …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds