// configuration.hpp
//
// (C) 2006 - 2009 MicroNeil Research Corporation.
// See http://www.codedweller.com for details.
//
// This program is free software; you can redistribute it and/or modify it
// under the terms of the GNU General Public License as published by the
// Free Software Foundation; either version 2 of the License, or (at your
// option) any later version.
//
// This program is distributed in the hope that it will be useful, but WITHOUT
// ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
// FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License for
// more details.
//
// You should have received a copy of the GNU General Public License along with
// this program; if not, write to the Free Software Foundation, Inc., 59 Temple
// Place, Suite 330, Boston, MA 02111-1307 USA
//
// What about this =============================================================
// The configuration module provides a platform for reading configuration files
// (or string data) containing well-formed xml and mapping that data to program
// variables.
// The idea is to provide the ability for an object or application to provide
// a modular "configuration" object that models a hierarchical collection of
// "settings" that can be represented easily in code and in xml.
//
// The following is an example model of a configuration in code and that same
// configuration fully populated in xml.
//
// The code might look like this...
//
// int IntValue, DefaultInt = 3;
// double DblValue, DefaultDbl = 3.14159;
// bool BooleanValue, DefaultBool = false;
// string StringValue, DefaultString = "NoStringHere";
//
// SpecialConfigurator : public ConfigurationHandler { // Create a special handler to build a list
// ...
// public:
//
// ConfigurationHandler& Startup(ConfigurationElement& E) { // This function returns a handy handler to
// return MyStartupConfigurationHandler; // (re) initialize this handler ;-)
// }
//
// void Operator()() { // Each time the configurator is called
// ...
// }
//
// int Attribute1; // these items are interpreted and added
// double Attribute2; // to the list. A ConfigurationHandler COULD
// string Attribute3; // do something entirely different though ;-)
// string Contents;
// ...
// } Special;
//
// ConfigurationElement SampleConfig("SampleConfiguration"); // Define a sample config (doc element)
// SampleConfig // Populate the SampleConfig
// .atStartCall(Special.Startup())
// .Element("Integer", IntValue, DefaultInt).End() // Link an element to an int w/ default.
// .Element("Double", DblValue, DefaultDbl).End("Double") // Link an element to a dbl w/ default.
// .Element("String", StringValue, DefaultString).End("String") // Link an element to a string w/ default.
// .Element("ComplexElements") // Create a sub element.
// .Element("Complex1") // Sub element Complex1 has attributes.
// .Attribute("IntAtt", IntValue, DefaultInt) // Complex1 has an integer attribute.
// .Attribute("DblAtt", DblValue, DefaultDbl) // Complex1 has a dbl attribute.
// .Element("IntAtt", IntValue).End() // IntAtt can also be set by a sub element.
// .Element("DblAtt", DblValue).End() // DblAtt can also be set by a sub element.
// .End() // That's it for Complex1.
// .Element("Complex2") // Create the Complex2 sub element.
// .Attribute("C2I", IntValue, DefaultInt) // C2I attribute.
// .Attribute("C2D", DblValue) // C2D attribute - no default.
// .Attribute("C2S", StringValue, DefultString) // C2S attribute - string w/ default.
// .End("Complex2") // End of element throws if doesn't match.
// .Element("Complex3", Special.Contents) // Element 3 using a special configurator.
// .Attribute("A1", Special.Attribute1) // Set A1 and A2 and A3 and when the
// .Attribute("A2", Special.Attribute2) // element has been completed, Special()
// .Attribute("A3", Special.Attribute3) // will be called to record the entries.
// .atEndCall(Special) // Here's where we register the handler.
// .End() // Closing Complex3 to be ice.
// .End() // Closing ComplexElements to be nice.
// .End(); // Closing SampleConfiguration to be nice.
//
// The XML might look like this...
//
//
// 10
// 2.4
// This is a sample string
//
//
// 24
//
//
// stuff in here
// Another instance
// Each one gets passed to Special() on activation
// This way, Special() can build a list or some other
// interesting thing with all of these.
//
//
//
// Include This Header Once Only ===============================================
#ifndef configuration_included
#define configuration_included
#include
#include
#include
#include
#include
#include
#include
using namespace std;
class ConfigurationElement; // Elements exist
class ConfigurationAttribute; // Attributes exist
class ConfigurationData; // Data exists
class ConfigurationTranslator; // Translators exist
class ConfigurationMnemonic; // Mnemonics exist
class Configurator; // Configurators exist
//// Configuration Element /////////////////////////////////////////////////////
//
// Elements make up the core of a configuration. That is, a configuration is a
// tree of elements. Elements translate directly to well formed xml elements in
// a configuration file or string.
class ConfigurationElement {
private:
string myName; // Elements have a name.
// External important things I remember but don't touch...
ConfigurationElement* myParent; // They may have a parrent.
list myStartConfigurators; // Call these when we start Interpret()
list myEndConfigurators; // Call these when we finish Interpret()
// Internal / subordinate things I own and kill...
list myAttributes; // They may have a list of attributes.
list myElements; // They may have a list of sub-elements.
list myMnemonics; // They may have a list of mnemonics.
list myTranslators; // They may have a list of translators.
// During Interpret() operations we keep track of where we are seen...
int myLine; // Last line number I was seen on.
int myIndex; // Last char position I was seen on.
int myLength; // Last segment length.
bool myCleanFlag; // Keep track of initialization.
bool myInitOnInterpretFlag; // Initialize() at each Interpret()?
void runStartConfigurators(ConfigurationData& D); // Does what it says ;-)
void runEndConfigurators(ConfigurationData& D); // Does what it says ;-)
public:
ConfigurationElement(const char* Name); // Must be constructed with a name
ConfigurationElement(const string Name); // either c string or c++ string.
ConfigurationElement(const char* Name, ConfigurationElement& Parent); // Sub-elements are constructed with a
ConfigurationElement(const string Name, ConfigurationElement& Parent); // parrent.
// Upon desctruction an element will delete all subordinate objects:
// * All sub element objects.
// * All attribute objects.
// * All mnemonic objects.
// * All translator objects.
// It is important to use new when passing one of these objects to an
// element or attribute to prevent problems with the delete operation.
// NORMALLY these things would be created using factory methods on the
// element and attribute objects themselves - so be careful.
// It will not delete Configurators - they must
// be deleted elsewhere because they may have been
// re-used and this element wouldn't know about it ;-)
~ConfigurationElement(); // The descrutor clears and deletes all!
// Elements can be probed for some simple, useful things.
string Name(); // Get the name of this element.
ConfigurationElement& Parent(); // Get the parent of this element.
ConfigurationElement& Parent(ConfigurationElement& newParent); // Set the parent of this element.
// Note - if there is no parent (an element is the root) then it will
// return a reference to itself when Parent() is called.
int Line(); // Get the last line number.
int Index(); // Get the last data position.
int Length(); // Get the last length.
// Elements can contain either data or sub-elements.
ConfigurationElement& Element(const char* Name); // Add a new sub element by c string name.
ConfigurationElement& Element(const string Name); // Add a new sub element by c++ string name.
//// Mapping element factory methods for convenience.
//// Root-Node elements are _usually_ empty and without attributes in xml
//// so we don't make any of that type of convenience constructor here.
// char* versions
ConfigurationElement& Element( // Mapping factory for convenience,
const char* Name, // requires a name, of course,
ConfigurationTranslator& newTranslator); // Add a Translator to this element.
ConfigurationElement& Element( // Mapping factory for convenience,
const char* Name, // requires a name, of course,
string& x, string init = string("")); // Map to a string.
ConfigurationElement& Element( // Mapping factory for convenience,
const char* Name, // requires a name, of course,
int& x, int init = 0, int radix = 0); // Map to an int.
ConfigurationElement& Element( // Mapping factory for convenience,
const char* Name, // requires a name, of course,
double& x, double init = 0.0); // Map to a double.
ConfigurationElement& Element( // Mapping factory for convenience,
const char* Name, // requires a name, of course,
bool& x, bool init = false); // Map to a boolean.
// string versions
ConfigurationElement& Element( // Mapping factory for convenience,
const string Name, // requires a name, of course,
ConfigurationTranslator& newTranslator); // Add a Translator to this element.
ConfigurationElement& Element( // Mapping factory for convenience,
const string Name, // requires a name, of course,
string& x, string init = string("")); // Map to a string.
ConfigurationElement& Element( // Mapping factory for convenience,
const string Name, // requires a name, of course,
int& x, int init = 0, int radix = 0); // Map to an int.
ConfigurationElement& Element( // Mapping factory for convenience,
const string Name, // requires a name, of course,
double& x, double init = 0.0); // Map to a double.
ConfigurationElement& Element( // Mapping factory for convenience,
const string Name, // requires a name, of course,
bool& x, bool init = false); // Map to a boolean.
// End methods for heading back up the tree at the end of an element.
class EndNameDoesNotMatch {}; // Throw when End(name) doesn't match.
ConfigurationElement& End(); // Return this element's parent.
ConfigurationElement& End(const char* Name); // Check the name and return the parent
ConfigurationElement& End(const string Name); // if the name is correct - or throw!
// Elements can have attributes.
ConfigurationAttribute& Attribute(const char* Name); // Add an attribute using a cstring.
ConfigurationAttribute& Attribute(const string Name); // Add an attribute using a c++ string.
//// Mapping Attribute factory methods for convenience.
// char* versions
ConfigurationAttribute& Attribute( // Mapping factory for convenience,
const char* Name, // requires a name, of course,
ConfigurationTranslator& newTranslator); // Add a Translator to this element.
ConfigurationAttribute& Attribute( // Mapping factory for convenience,
const char* Name, // requires a name, of course,
string& x, string init = string("")); // Map to a string.
ConfigurationAttribute& Attribute( // Mapping factory for convenience,
const char* Name, // requires a name, of course,
int& x, int init = 0, int radix = 0); // Map to an int.
ConfigurationAttribute& Attribute( // Mapping factory for convenience,
const char* Name, // requires a name, of course,
double& x, double init = 0.0); // Map to a double.
ConfigurationAttribute& Attribute( // Mapping factory for convenience,
const char* Name, // requires a name, of course,
bool& x, bool init = false); // Map to a boolean.
// string versions
ConfigurationAttribute& Attribute( // Mapping factory for convenience,
const string Name, // requires a name, of course,
ConfigurationTranslator& newTranslator); // Add a Translator to this element.
ConfigurationAttribute& Attribute( // Mapping factory for convenience,
const string Name, // requires a name, of course,
string& x, string init = string("")); // Map to a string.
ConfigurationAttribute& Attribute( // Mapping factory for convenience,
const string Name, // requires a name, of course,
int& x, int init = 0, int radix = 0); // Map to an int.
ConfigurationAttribute& Attribute( // Mapping factory for convenience,
const string Name, // requires a name, of course,
double& x, double init = 0.0); // Map to a double.
ConfigurationAttribute& Attribute( // Mapping factory for convenience,
const string Name, // requires a name, of course,
bool& x, bool init = false); // Map to a boolean.
// Elements can Initialize() at each Interpret() call.
ConfigurationElement& setInitOnInterpret(); // Set the init on interpret flag.
// Elements can call external functions to aid in special operations
// such as building lists.
ConfigurationElement& atStartCall(Configurator& Functor); // Add an atStart call-back to this element.
ConfigurationElement& atEndCall(Configurator& Functor); // Add an atEnd call-back to this element.
// Extracting data from the element's contents is done with
// translators. A good set of primatives are built in, but the user
// can also make their own. If an Element is mapped to more than
// one then they are all called once the element's contents are
// collected. A translator takes the data provided by the element,
// converts it into the expected type, and sets one or more variables
// to the converted value. Usually - just one variable.
ConfigurationElement& mapTo(ConfigurationTranslator& newTranslator); // Add a Translator to this element.
ConfigurationElement& mapTo(string& x, string init = string("")); // Map to a string.
ConfigurationElement& mapTo(int& x, int init = 0, int radix = 0); // Map to an int.
ConfigurationElement& mapTo(double& x, double init = 0.0); // Map to a double.
ConfigurationElement& mapTo(bool& x, bool init = false); // Map to a boolean.
// An Element's contents may use some special mnemonics to make a
// configuration easier to understand and less error prone. When the
// contents match a mnemnoic then the translation of the mnemonic is
// passed to the Translators instead of the raw contents.
ConfigurationElement& Mnemonic(const char* name, const char* value); // Add a mnemonic using c strings.
ConfigurationElement& Mnemonic(const char* name, const string value); // Add a mnemonic using c & c++ strings.
ConfigurationElement& Mnemonic(const string name, const char* value); // Add a mnemonic using c++ & c strings.
ConfigurationElement& Mnemonic(const string name, const string value); // Add a mnemonic using c++ strings.
// The way data gets into an element tree is that it is Interpret()ed
// recursively. The data is loaded into a ConfigurationData object which
// is passed to the top Element. That element interpretes the data, moves
// the interpretation pointers, and passes the data on to it's subordinate
// elements in turn. They do the same recursively. When the last sub -
// element has had it's way with the data, the interpretation process is
// complete. The ConfigurationData object will contain the original data
// and a log of anything that happened during the interpretation process.
//
// Each time an element is asked to Interpret() data, it calls any atStart
// configurators, translates any attributes, then either translates it's
// contents or passes the data to it's children, then calls any atEnd
// configurators.
//
// To ensure that the correct default values are used the Initialize() is
// always called on all internal attributes and elements before any data is
// interpreted. To prevent this from being inefficient, a boolean flag is
// kept in each element to keep track of whether it is clean and if it is
// then the call to Initialize will simply return (skipping subordinate
// elements along the way).
//
// Interpret returns true if this object found itself at the current
// Data.Index and false if not. This helps keep the recursive parsing
// code simpler ;-)
void initialize(); // Reset all translators to defaults.
void notifyDirty(); // Set dirty (if translators change).
bool interpret(ConfigurationData& Data); // (re) Interpret this data.
};
//// Configuration Attribute ///////////////////////////////////////////////////
//
// Attributes translate directly to well formed xml attributes (within the
// start tag of an element).
class ConfigurationAttribute {
private:
string myName; // Elements have a name.
ConfigurationElement& myParent; // They may have a parrent.
list myMnemonics; // They may have a list of mnemonics.
list myTranslators; // They may have a list of translators.
int myLine; // Last line number I was seen on.
int myIndex; // Last char position I was seen on.
int myLength; // Last segment length.
public:
ConfigurationAttribute(const char* Name, ConfigurationElement& Parent); // Sub-elements are constructed with a
ConfigurationAttribute(const string Name, ConfigurationElement& Parent); // parrent.
// Attributes delete their Mnemonics and Translators when they go.
// See Elements for similar warnings about objects provided to
// this object... you must use new to be safe, or better yet - stick to
// the built in factory methods ;-)
~ConfigurationAttribute(); // Crush, Kill, Destroy!
// Attributes can be probed for some simple, useful things.
string Name(); // Get the name of this attribute.
ConfigurationElement& Parent(); // Get the parent of this attribute.
int Line(); // Get the last line number.
int Index(); // Get the last data position.
int Length(); // Get the last length.
void notifyDirty(); // Attributes use this when they change.
// For convenience in building configurations, an Attribute offers
// some call-through methods to it's parrent Element. This allows for
// clear, concise .method() coding that mimics an outline of the
// configuration structure.
//// For switching back to the parent element and adding new sub-elements.
ConfigurationElement& Element(const char* Name); // Add a new sub element by c string name.
ConfigurationElement& Element(const string Name); // Add a new sub element by c++ string name.
//// Mapping element factory methods for convenience.
//// Root-Node elements are _usually_ empty and without attributes in xml
//// so we don't make any of that type of convenience constructor here.
// char* versions
ConfigurationElement& Element( // Mapping factory for convenience,
const char* Name, // requires a name, of course,
ConfigurationTranslator& newTranslator); // Add a Translator to this element.
ConfigurationElement& Element( // Mapping factory for convenience,
const char* Name, // requires a name, of course,
string& x, string init = string("")); // Map to a string.
ConfigurationElement& Element( // Mapping factory for convenience,
const char* Name, // requires a name, of course,
int& x, int init = 0, int radix = 0); // Map to an int.
ConfigurationElement& Element( // Mapping factory for convenience,
const char* Name, // requires a name, of course,
double& x, double init = 0.0); // Map to a double.
ConfigurationElement& Element( // Mapping factory for convenience,
const char* Name, // requires a name, of course,
bool& x, bool init = false); // Map to a boolean.
// string versions
ConfigurationElement& Element( // Mapping factory for convenience,
const string Name, // requires a name, of course,
ConfigurationTranslator& newTranslator); // Add a Translator to this element.
ConfigurationElement& Element( // Mapping factory for convenience,
const string Name, // requires a name, of course,
string& x, string init = string("")); // Map to a string.
ConfigurationElement& Element( // Mapping factory for convenience,
const string Name, // requires a name, of course,
int& x, int init = 0, int radix = 0); // Map to an int.
ConfigurationElement& Element( // Mapping factory for convenience,
const string Name, // requires a name, of course,
double& x, double init = 0.0); // Map to a double.
ConfigurationElement& Element( // Mapping factory for convenience,
const string Name, // requires a name, of course,
bool& x, bool init = false); // Map to a boolean.
// End methods for heading back up the tree at the end of an element.
ConfigurationElement& End(); // Return this element's parent.
ConfigurationElement& End(const char* Name); // Check the name and return the parent
ConfigurationElement& End(const string Name); // if the name is correct - or throw!
//// For adding new attributes to the parent element.
ConfigurationAttribute& Attribute(const char* Name); // Add an attribute using a cstring.
ConfigurationAttribute& Attribute(const string Name); // Add an attribute using a c++ string.
//// Mapping Attribute factory methods for convenience.
// char* versions
ConfigurationAttribute& Attribute( // Mapping factory for convenience,
const char* Name, // requires a name, of course,
ConfigurationTranslator& newTranslator); // Add a Translator to this element.
ConfigurationAttribute& Attribute( // Mapping factory for convenience,
const char* Name, // requires a name, of course,
string& x, string init = string("")); // Map to a string.
ConfigurationAttribute& Attribute( // Mapping factory for convenience,
const char* Name, // requires a name, of course,
int& x, int init = 0, int radix = 0); // Map to an int.
ConfigurationAttribute& Attribute( // Mapping factory for convenience,
const char* Name, // requires a name, of course,
double& x, double init = 0.0); // Map to a double.
ConfigurationAttribute& Attribute( // Mapping factory for convenience,
const char* Name, // requires a name, of course,
bool& x, bool init = false); // Map to a boolean.
// string versions
ConfigurationAttribute& Attribute( // Mapping factory for convenience,
const string Name, // requires a name, of course,
ConfigurationTranslator& newTranslator); // Add a Translator to this element.
ConfigurationAttribute& Attribute( // Mapping factory for convenience,
const string Name, // requires a name, of course,
string& x, string init = string("")); // Map to a string.
ConfigurationAttribute& Attribute( // Mapping factory for convenience,
const string Name, // requires a name, of course,
int& x, int init = 0, int radix = 0); // Map to an int.
ConfigurationAttribute& Attribute( // Mapping factory for convenience,
const string Name, // requires a name, of course,
double& x, double init = 0.0); // Map to a double.
ConfigurationAttribute& Attribute( // Mapping factory for convenience,
const string Name, // requires a name, of course,
bool& x, bool init = false); // Map to a boolean.
//// Set Init On Interprete for the parent element.
ConfigurationElement& setInitOnInterpret(); // Set the init on interpret flag.
//// For adding configurators to the parent element.
ConfigurationElement& atStartCall(Configurator& Functor); // Add an atStart call-back to this element.
ConfigurationElement& atEndCall(Configurator& Functor); // Add an atEnd call-back to this element.
// Of course, the most useful thing about attributes is that they can
// be mapped to variables using translators. The same as those that
// apply to the parent element's contents. Here they are for use on this
// attribute.
ConfigurationAttribute& mapTo(ConfigurationTranslator& newTranslator); // Add a Translator to this attribute.
ConfigurationAttribute& mapTo(string& x, string init = string("")); // Map to a string.
ConfigurationAttribute& mapTo(int& x, int init, int radix = 0); // Map to an int.
ConfigurationAttribute& mapTo(double& x, double init = 0.0); // Map to a double.
ConfigurationAttribute& mapTo(bool& x, bool init = false); // Map to a boolean.
// Attributes can have mnemonics just like elements.
ConfigurationAttribute& Mnemonic(const char* name, const char* value); // Add a mnemonic using a c string.
ConfigurationAttribute& Mnemonic(const char* name, const string value); // Add a mnemonic using c & c++ strings.
ConfigurationAttribute& Mnemonic(const string name, const char* value); // Add a mnemonic using c++ & c strings.
ConfigurationAttribute& Mnemonic(const string name, const string value); // Add a mnemonic using a c++ string.
// Attributes participate in the Interprete() task just like elements.
void initialize(); // Reset all translators to defaults.
bool interpret(ConfigurationData& Data); // (re) Interpret this data.
};
//// Configuration Data ////////////////////////////////////////////////////////
//
// A ConfigurationData object holds on to the configuration source data and
// provideds a place to log any information about how the configuration was
// interpreted. It also creates and destroys a handy char[] to contain the
// data. To make this beastie easier to handle, we use the Named Constructor
// Idiom and hide the true constructor in the private section.
class ConfigurationData { // Configuration Data Source
private:
char* myDataBuffer; // The actual data buffer.
int myBufferSize; // Size of the current buffer.
int myIndex; // The current interpretation index.
int myLine; // Current line number.
public:
ConfigurationData(const char* FileName); // Constructor from c string file name.
ConfigurationData(const string FileName); // Constructor from c++ string file name.
ConfigurationData(const char* Data, int Length); // Raw constructor from text buffer.
~ConfigurationData(); // Destroys the internal buffer etc.
char Data(int Index); // Returns char from Data[Index]
int Index(); // Reads the current Index.
int Index(int i); // Changes the current Index.
int Line(); // Reads the current Line number.
int addNewLines(int Count); // Increments the Line number.
stringstream Log; // Convenient Interpret log.
};
//// Configuration Translator //////////////////////////////////////////////////
//
// A Translator converts the contents provided to it in string form into some
// other data type. The object here is a prototype for that, followed by a
// collection of the basic translators used for built-in mapTo()s.
class ConfigurationTranslator { // Translators exist
public:
virtual ~ConfigurationTranslator(){}; // Stop No Virt Dtor warnings.
virtual void translate(const char* Value) = 0; // Pure virtual translator.
virtual void initialize() = 0; // Pure virtual initializer.
};
class StringTranslator : public ConfigurationTranslator {
private:
string& myVariable; // Variable to map.
string myInitializer; // Initial/Default value.
public:
StringTranslator( // Construct this with
string& Variable, // the variable to map,
string Inititializer); // and the default value.
void translate(const char* Value); // Provide a translation method.
void initialize(); // Provide an initialization method.
};
class IntegerTranslator : public ConfigurationTranslator {
private:
int& myVariable; // Variable to map.
int myInitializer; // Initial/Default value.
int myRadix; // Radix for strtol()
public:
IntegerTranslator( // Construct this with
int& Variable, // the variable to map,
int Inititializer, // and the default value.
int Radix); // For this one we also need a Radix.
void translate(const char* Value); // Provide a translation method.
void initialize(); // Provide an initialization method.
};
class DoubleTranslator : public ConfigurationTranslator {
private:
double& myVariable; // Variable to map.
double myInitializer; // Initial/Default value.
public:
DoubleTranslator( // Construct this with
double& Variable, // the variable to map,
double Inititializer); // and the default value.
void translate(const char* Value); // Provide a translation method.
void initialize(); // Provide an initialization method.
};
class BoolTranslator : public ConfigurationTranslator {
private:
bool& myVariable; // Variable to map.
bool myInitializer; // Initial/Default value.
public:
BoolTranslator( // Construct this with
bool& Variable, // the variable to map,
bool Inititializer); // and the default value.
void translate(const char* Value); // Provide a translation method.
void initialize(); // Provide an initialization method.
};
//// Configuration Mnemonic ////////////////////////////////////////////////////
//
// A Mnemonic allows the actual contents of an element or attribute to be
// exchanged for a different "named" value to help eliminate "magic numbers"
// and "secret codes" from configurations. One way this might be used is to
// map an enumeration to the appropriate integer values, or things like YES and
// NO to boolean true and false (respectively) when turning on/off program
// options.
class ConfigurationMnemonic { // Mnemonics
private:
string myName; // What is the Mnemonic?
string myValue; // What is the translation?
public:
ConfigurationMnemonic(string Name, string Value); // To make one, provide both parts.
bool test(string Name); // Test to see if this Mnemonic matches.
string Value(); // If it does then we will need it's value.
};
//// Configurator //////////////////////////////////////////////////////////////
//
// A configurator is a "functor" or "closure" or "callback" that can be used to
// support sophisticated interpretation options. The most basic and necessary
// of these is support for list building. Consider an object created to contain
// a list of records where each record might be represented as a collection of
// attributes and elements. The object would have data elements mapped to the
// attributes and elements in the configuration and then control elements which
// are functors for initializing the list and storing new entries as they are
// completed. The object here is a pure virtual prototype.
class Configurator { // Configurators exist
public:
virtual void operator()(ConfigurationElement& E, ConfigurationData& D) = 0; // Pure virtual configurator.
virtual ~Configurator() {} // Virtual dtor keeps warnings away.
};
//// Include our inline methods ////////////////////////////////////////////////
#include "configuration.inline.hpp"
//// Utilities /////////////////////////////////////////////////////////////////
// SetTrueOnComplete Configurator //////////////////////////////////////////////
class ConfiguratorSetTrueOnComplete : public Configurator { // Configurator set's a boolean true.
private:
bool* myBoolean; // The boolean to set.
public:
ConfiguratorSetTrueOnComplete(); // Must init to NULL for safety.
void setup(bool& Target); // Link to the target boolean.
void operator()(ConfigurationElement& E, ConfigurationData& D); // Handle the operation.
};
#endif
// End Of Include Only Once