persistence/persistent_stlplus.hpp
Data Persistence for the STLplus Containers

Introduction

The template classes provided by the STLplus Containers Library have been made persistent using template dump/restore functions. The functions are actually called dump_class and stlplus::restore_class where class is the name of the template class. For example, the persistence functions for the STLplus hash are called stlplus::dump_hash and stlplus::restore_hash.

The persistence functions for templates are themselves templates, so are automatically adapted to the type that the container holds. For example, stlplus::dump_ntree which is the dump routine for the STLplus n-ary tree, will adapt to the type being held in the ntree. If the ntree contains std::string, then the stlplus::dump_ntree function will dump strings by calling the dump function defined for std::string. The template function does this by taking the name of the element dump functions as parameters.

For example:

#include <string>
#include "persistent_contexts.hpp"
#include "persistent_ntree.hpp"
#include "persistent_string.hpp"

void stlplus::dump_string_ntree(stlplus::dump_context& context, const stlplus::ntree<std::string>& data)
{
  stlplus::dump_ntree(context, data, stlplus::dump_string);
}

void stlplus::restore_string_ntree(stlplus::restore_context& context, stlplus::ntree<std::string>& data)
{
  stlplus::restore_ntree(context, data, stlplus::restore_string);
}

The last parameter passed to the stlplus::dump_ntree and stlplus::restore_ntree functions is the name of the function to use to dump/restore the element type - in this case std::string.

The same parameter profile is used for all dump/restore routines, except that for classes that contain more than one type, there will be more than one function name required. For example, the std::triple contains three different types, so requires three function names for each call:

#include <string>
#include "triple.hpp"
#include "persistent_triple.hpp"
#include "persistent_string.hpp"
#include "persistent_int.hpp"

typedef stlplus::triple<std::string,int,int> string_int_int_triple;

void stlplus::dump_string_int_int_triple(stlplus::dump_context& context, const string_int_int_triple& data)
{
  stlplus::dump_triple(context, data, stlplus::dump_string, stlplus::dump_int, stlplus::dump_int);
}

void stlplus::restore_string_int_int_triple(stlplus::restore_context& context, string_int_int_triple& data)
{
  stlplus::restore_pair(context, data, stlplus::restore_string, stlplus::restore_int);
}

In this example, stlplus::dump_string and stlplus::dump_int manage the dumping of the two types, whilst restore_string and stlplus::restore_int manage the restoration.

In both of these examples, the data structure can be dumped using only functions already provided by the persistence library. However, I prefer to wrap the function calls into a top-level function that has a simple interface and hides the implementation, as in the example above, even though the implementation is one line of code.

Note:The function passed as a parameter must have only two parameters - the context and the data. To pass a function that takes extra parameters, wrap those functions in a wrapper function, like the stlplus::dump_string_int_int_triple and stlplus::restore_string_int_int_triple functions above. These functions can be used in a higher-level data structure, whereas the stlplus::dump_pair and stlplus:restore_pair cannot because they take more than two parameters.

Header persistent_stlplus.hpp

You can include all of the functions in one go by including persistent_stlplus.hpp. If you prefer however, you can include the separate headers for each class being used. The latter approach minimises the number of headers that get included indirectly.

Persistence of stlplus::smart_ptr

This header provides persistence functions for the stlplus::smart_ptr and stlplus::smart_ptr_clone classes. This set of functions provides the following interface:

// smart_ptr

template<typename T, typename DE>
void stlplus::dump_smart_ptr(stlplus::dump_context&, const smart_ptr<T>& data, DE stlplus::dump_element);

template<typename T, typename RE>
void stlplus::restore_smart_ptr(stlplus::restore_context&, smart_ptr<T>& data, RE stlplus::restore_element);

// smart_ptr_clone using callback approach

template<typename T>
void stlplus::dump_smart_ptr_clone_callback(stlplus::dump_context&, const smart_ptr_clone<T>& data);

template<typename T>
void stlplus::restore_smart_ptr_clone_callback(stlplus::restore_context&, smart_ptr_clone<T>& data);

// smart_ptr_clone using interface approach

template<typename T>
void stlplus::dump_smart_ptr_clone_interface(stlplus::dump_context&, const smart_ptr_clone<T>& data);

template<typename T>
void stlplus::restore_smart_ptr_clone_interface(stlplus::restore_context&, smart_ptr_clone<T>& data);

There are three dump/restore pairs of functions here. The first pair act on smart_ptr, which is the variant that uses copy constructors to perform copying. This pair of functions are templatised functions for the persistence of a smart_ptr containing an element of any class and takes as its third parameter the function for managing the elements. For example, for a smart_ptr pointing to std::string, the element functions would be stlplus::dump_string and stlplus::restore_string.

The second and third pairs of functions are for smart_ptr_clone. This variant of the smart pointer (see the page on smart pointers) is for polymorphic class hierarchies. There are two approaches to making such class hierarchies persistent - known as the interface approach and the callback appraoch. These last two pairs of functions implement persistence using one or other of those two approaches. See the page on persistence of polymorphic classes for more explanation.

See also the discussion of the persistence of pointers and its impact on the persistence of smart pointers.

Persistence of stlplus::simple_ptr

This header provides persistence functions for the stlplus::simple_ptr and stlplus::simple_ptr_clone classes. This set of functions provides the following interface:

// simple_ptr

template<typename T, typename DE>
void stlplus::dump_simple_ptr(stlplus::dump_context&, const simple_ptr<T>& data, DE stlplus::dump_element);

template<typename T, typename RE>
void stlplus::restore_simple_ptr(stlplus::restore_context&, simple_ptr<T>& data, RE stlplus::restore_element);

// simple_ptr_clone using callback approach

template<typename T>
void stlplus::dump_simple_ptr_clone_callback(stlplus::dump_context&, const simple_ptr_clone<T>& data);

template<typename T>
void stlplus::restore_simple_ptr_clone_callback(stlplus::restore_context&, simple_ptr_clone<T>& data);

// simple_ptr_clone using interface approach

template<typename T>
void stlplus::dump_simple_ptr_clone_interface(stlplus::dump_context&, const simple_ptr_clone<T>& data);

template<typename T>
void stlplus::restore_simple_ptr_clone_interface(stlplus::restore_context&, simple_ptr_clone<T>& data);

There are three dump/restore pairs of functions here. The first pair act on simple_ptr, which is the variant that uses copy constructors to perform copying. This pair of functions are templatised functions for the persistence of a simple_ptr containing an element of any class and takes as its third parameter the function for managing the elements. For example, for a simple_ptr pointing to std::string, the element functions would be stlplus::dump_string and stlplus::restore_string.

The second and third pairs of functions are for simple_ptr_clone. This variant of the smart pointer (see the page on simple pointers) is for polymorphic class hierarchies. There are two approaches to making such class hierarchies persistent - known as the interface approach and the callback appraoch. These last two pairs of functions implement persistence using one or other of those two approaches. See the page on persistence of polymorphic classes for more explanation.

See also the discussion of the persistence of pointers and its impact on the persistence of simple pointers.

Persistence of stlplus::triple

This header provides persistence functions for the stlplus::triple collection.

The header provides the following functions:

template<typename T1, typename T2, typename T3, typename D1, typename D2, typename D3>
void stlplus::dump_triple(stlplus::dump_context&, const stlplus::triple<T1,T2,T3>& data,
                 D1 stlplus::dump_fn1, D2 stlplus::dump_fn2, D3 stlplus::dump_fn3);

template<typename T1, typename T2, typename T3, typename R1, typename R2, typename R3>
void stlplus::restore_triple(stlplus::restore_context&, stlplus::triple<T1,T2,T3>& data,
                    R1 stlplus::restore_fn1, R2 stlplus::restore_fn2, R3 stlplus::restore_fn3);

These functions take three parameters that are names of functions: one for each element in the triple. For example, if you were using a triple containing a string and two ints:

#include "persistent_contexts.hpp"
#include "persistent_triple.hpp"
#include "persistent_string.hpp"
#include "persistent_int.hpp"

typedef std::triple<std::string,int,int> string_int_int_triple;

void stlplus::dump_string_int_int_triple(stlplus::dump_context& context, const string_int_int_triple& data)
{
  stlplus::dump_triple(context, data, stlplus::dump_string, stlplus::dump_int, stlplus::dump_int);
}

void stlplus::restore_string_int_int_triple(stlplus::restore_context& context, string_int_int_triple& data)
{
  stlplus::restore_triple(context, data, stlplus::restore_string, stlplus::restore_int, stlplus::restore_int);
}

Persistence of stlplus::foursome

This header provides persistence functions for the stlplus::foursome collection.

The header provides the following functions:

template<typename T1, typename T2, typename T3, typename T4, typename D1, typename D2, typename D3, typename D4>
void stlplus::dump_foursome(stlplus::dump_context&, const stlplus::foursome<T1,T2,T3,T4>& data,
                   D1 stlplus::dump_fn1, D2 stlplus::dump_fn2, D3 stlplus::dump_fn3, D4 stlplus::dump_fn4);

template<typename T1, typename T2, typename T3, typename T4, typename R1, typename R2, typename R3, typename R4>
void stlplus::restore_foursome(stlplus::restore_context&, stlplus::foursome<T1,T2,T3,T4>& data,
                      R1 stlplus::restore_fn1, R2 stlplus::restore_fn2, R3 stlplus::restore_fn3, R4 stlplus::restore_fn4);

These functions take four parameters that are names of functions: one for each element in the foursome. In all other respects it is similar to stlplus::triple.

Persistence of stlplus::digraph

The stlplus::digraph contains two element types - one on the node and one on the arc. So therefore it is parametrised by two element functions.

The interface is:

template<typename NT, typename AT, typename DN, typename DA>
void stlplus::dump_digraph(stlplus::dump_context&, const digraph<NT,AT>& data, DN stlplus::dump_node, DA stlplus::dump_arc);

template<typename NT, typename AT, typename RN, typename RA>
void stlplus::restore_digraph(stlplus::restore_context&, digraph<NT,AT>& data, RN stlplus::restore_node, RA stlplus::restore_arc);

This pair of functions are templatised functions for the persistence of a digraph containing an element of any class on the node (class NT) and on the arc (class AT) so the persistence functions take as their last two parameters the functions for managing the element classes.

In addition to the graph itself, it is also possible to make node and arc iterators persistent. For example, if you have another data structure containing iterators to the graph, that other data structure can also be made persistent.

The rule is that the graph must be dumped before any of its iterators are dumped. Failure to keep to this rule will result in an stlplus::persistent_dump_failed exception being thrown.

The following functions are provided for making iterators persistent:

// node iterator

template<typename NT, typename AT, typename NRef, typename NPtr>
void stlplus::dump_digraph_iterator(stlplus::dump_context& str, const digraph_iterator<NT,AT,NRef,NPtr>& data);

template<typename NT, typename AT, typename NRef, typename NPtr>
void stlplus::restore_digraph_iterator(stlplus::restore_context& str, digraph_iterator<NT,AT,NRef,NPtr>& data);

// arc iterator

template<typename NT, typename AT, typename NRef, typename NPtr>
void stlplus::dump_digraph_arc_iterator(stlplus::dump_context& str, const digraph_arc_iterator<NT,AT,NRef,NPtr>& data);

template<typename NT, typename AT, typename NRef, typename NPtr>
void stlplus::restore_digraph_arc_iterator(stlplus::restore_context& str, digraph_arc_iterator<NT,AT,NRef,NPtr>& data);

Persistence of stlplus::hash

The stlplus::hash is a variant on the stl::map and like the map contains two element types - one for the key and one for the value. So therefore it is parametrised by two element functions.

The interface is:

template<typename K, typename T, typename H, typename E, typename DK, typename DT>
void stlplus::dump_hash(stlplus::dump_context&, const hash<K,T,H,E>& data, DK key_dump_fn, DT val_dump_fn);

template<typename K, typename T, typename H, typename E, typename RK, typename RT>
void stlplus::restore_hash(stlplus::restore_context&, hash<K,T,H,E>& data, RK key_restore_fn, RT val_restore_fn);

This pair of functions are templatised functions for the persistence of a hash and takes as its third and fourth parameters the functions for managing the key and value respectively. For example, for a hash of ints mapped onto string, the element functions would be stlplus::dump_int/stlplus::dump_string and stlplus::restore_int/stlplus::restore_string.

Persistence of stlplus::matrix

An stlplus::matrix is like a vector but with two dimensions. A matrix can contain any type (not just numeric types) and so the persistence functions look quite like the vector persistence functions.

The interface is:

template<typename T, typename DT>
void stlplus::dump_matrix(stlplus::dump_context&, const matrix<T>& data, DT stlplus::dump_fn);

template<typename T, typename RT>
void stlplus::restore_matrix(stlplus::restore_context&, matrix<T>& data, RT stlplus::restore_fn);

Persistence of stlplus::ntree

The stlplus::ntree contains one element type. So therefore it is parametrised by one element function.

The interface is:

template<typename T, typename D>
void stlplus::dump_ntree(stlplus::dump_context&, const ntree<T>& data, D stlplus::dump_fn);

template<typename T, typename R>
void stlplus::restore_ntree(stlplus::restore_context&, ntree<T>& data, R stlplus::restore_fn);

This pair of functions are templatised functions for the persistence of a ntree containing an element of any class so the persistence functions take as their last parameter the functions for managing the element classes.

In addition to the tree itself, it is also possible to make iterators persistent. For example, if you have another data structure containing iterators to the ntree, that other data structure can also be made persistent.

The rule is that the ntree must be dumped before any of its iterators are dumped. Failure to keep to this rule will result in an stlplus::persistent_dump_failed exception being thrown.

The following functions are provided for making iterators persistent:

// iterator

template<typename T, typename TRef, typename TPtr>
void stlplus::dump_ntree_iterator(stlplus::dump_context&, const ntree_iterator<T,TRef,TPtr>&);

template<typename T, typename TRef, typename TPtr>
void stlplus::restore_ntree_iterator(stlplus::restore_context&, ntree_iterator<T,TRef,TPtr>&);

// prefix iterator

template<typename T, typename TRef, typename TPtr>
void stlplus::dump_ntree_prefix_iterator(stlplus::dump_context&, const ntree_prefix_iterator<T,TRef,TPtr>&);

template<typename T, typename TRef, typename TPtr>
void stlplus::restore_ntree_prefix_iterator(stlplus::restore_context&, ntree_prefix_iterator<T,TRef,TPtr>&);

// postfix iterator

template<typename T, typename TRef, typename TPtr>
void stlplus::dump_ntree_postfix_iterator(stlplus::dump_context&, const ntree_postfix_iterator<T,TRef,TPtr>&);

template<typename T, typename TRef, typename TPtr>
void stlplus::restore_ntree_postfix_iterator(stlplus::restore_context&, ntree_postfix_iterator<T,TRef,TPtr>&);