The persistent API provides functionality to read/write data from/to a document (string or file). The data can be simple data types such as boolean, number, string, and string arrays, or a user defined object. Currently the implementation supports reading and writing from/to JSON document, but the framework allows application to extend the API to support other document formats.

class PersistentObject
#include <persistent.hpp>

This is the abstract base class of objects that can be serialized to/from persistent document.

Subclassed by pj::AccountCallConfig, pj::AccountConfig, pj::AccountMediaConfig, pj::AccountMwiConfig, pj::AccountNatConfig, pj::AccountPresConfig, pj::AccountRegConfig, pj::AccountSipConfig, pj::AccountVideoConfig, pj::AuthCredInfo, pj::BuddyConfig, pj::EpConfig, pj::LogConfig, pj::MediaConfig, pj::RtcpFbConfig, pj::SrtpOpt, pj::TlsConfig, pj::TransportConfig, pj::UaConfig

class PersistentDocument
#include <persistent.hpp>

This a the abstract base class for a persistent document. A document is created either by loading from a string or a file, or by constructing it manually when writing data to it. The document then can be saved to either string or to a file. A document contains one root ContainerNode where all data are stored under.

Document is read and written serially, hence the order of reading must be the same as the order of writing. The PersistentDocument class provides API to read and write to the root node, but for more flexible operations application can use the ContainerNode methods instead. Indeed the read and write API in PersistentDocument is just a shorthand which calls the relevant methods in the ContainerNode. As a tip, normally application only uses the readObject() and writeObject() methods declared here to read/write top level objects, and use the macros that are explained in ContainerNode documentation to read/write more detailed data.

Subclassed by pj::JsonDocument

struct container_node_internal_data
#include <persistent.hpp>

Internal data for ContainerNode. See ContainerNode implementation notes for more info.

class ContainerNode
#include <persistent.hpp>

A container node is a placeholder for storing other data elements, which could be boolean, number, string, array of strings, or another container. Each data in the container is basically a name/value pair, with a type internally associated with it so that written data can be read in the correct type. Data is read and written serially, hence the order of reading must be the same as the order of writing.

Application can read data from it by using the various read methods, and write data to it using the various write methods. Alternatively, it may be more convenient to use the provided macros below to read and write the data, because these macros set the name automatically:

  • NODE_READ_BOOL(node,item)

  • NODE_READ_UNSIGNED(node,item)

  • NODE_READ_INT(node,item)

  • NODE_READ_FLOAT(node,item)

  • NODE_READ_NUM_T(node,type,item)

  • NODE_READ_STRING(node,item)

  • NODE_READ_STRINGV(node,item)

  • NODE_READ_OBJ(node,item)

  • NODE_WRITE_BOOL(node,item)

  • NODE_WRITE_UNSIGNED(node,item)

  • NODE_WRITE_INT(node,item)

  • NODE_WRITE_FLOAT(node,item)

  • NODE_WRITE_NUM_T(node,type,item)

  • NODE_WRITE_STRING(node,item)

  • NODE_WRITE_STRINGV(node,item)

  • NODE_WRITE_OBJ(node,item)

Implementation notes:

The ContainerNode class is subclass-able, but not in the usual C++ way. With the usual C++ inheritance, some methods will be made pure virtual and must be implemented by the actual class. However, doing so will require dynamic instantiation of the ContainerNode class, which means we will need to pass around the class as pointer, for example as the return value of readContainer() and writeNewContainer() methods. Then we will need to establish who needs or how to delete these objects, or use shared pointer mechanism, each of which is considered too inconvenient or complicated for the purpose.

So hence we use C style “inheritance”, where the methods are declared in container_node_op and the data in container_node_internal_data structures. An implementation of ContainerNode class will need to set up these members with values that makes sense to itself. The methods in container_node_op contains the pointer to the actual implementation of the operation, which would be specific according to the format of the document. The methods in this ContainerNode class are just thin wrappers which call the implementation in the container_node_op structure.