Note: This HOWTO does not work with cxxtools <= 2.2.
The properties format is a very simple file format which is suitable for simple configuration files. It is normally not more than a list of key value pairs. The keys and values can be separated by '=', ':' or just white space. Here is a simple example:
encoding = UTF-8
plug-ins.0 = python
plug-ins.1 = c++
plug-ins.2 = ruby
indent.length: 3
indent.use_space true
cxxtools
has a serialization framework which can read properties files. It
has a intermediate data structure cxxtools::SerializationInfo
, which can be
used to read the properties file and access specific elements.
std::ifstream input("myconfiguration.properties");
cxxtools::SerializationInfo si;
input >> cxxtools::Properties(si); // parse properties data into a cxxtools::SerializationInfo
std::string encoding;
si.getMember("encoding") >>= encoding;
std::cout << encoding << std::endl; // prints "UTF-8"
unsigned length;
bool use_space;
si.getMember("indent.length") >>= length;
si.getMember("indent.use_space") >>= use_space;
// prints "length=3 use_space=true":
std::cout << "length=" << length << " use_space=" << use_space << std::endl;
cxxtools
uses the point in keys as a separator and so the indent
data can be
read hierarchically as well:
si.getMember("indent").getMember("length") >>= length;
si.getMember("indent").getMember("use_space") >>= use_space;
// prints "length=3 use_space=true":
std::cout << "length=" << length << " use_space=" << use_space << std::endl;
This is fine, if you want to access single members. But the nice thing about operator overloading is, that you can easily add your own overloads. That is possible even for complex classes.
To read the above properties file we define the structure as a C++ struct
and
overload the operator >>=
for that.
Since the operator(std::istream&, cxxtools::Properties)
, which we use to parse
the properties structure uses the operator >>=
also, we can read our structure
easily from a std::istream
.
// Configuration options
struct Configuration
{
// Default encoding for text
std::string encoding;
// Plug-ins loaded at start-up
std::vector<std::string> plug_ins;
// Tab indent size
struct {
unsigned length;
bool use_space;
} indent;
};
// overload the operator >>= for our struct Configuration
void operator>>= (const cxxtools::SerializationInfo& si, Configuration& c)
{
si.getMember("encoding") >>= c.encoding;
// if you want to make encoding optional with a default value use:
//
// if (!si.getMember("encoding", c.encoding))
// c.encoding = "UTF-8";
si.getMember("plug-ins") >>= c.plug_ins;
const cxxtools::SerializationInfo& si_indent = si.getMember("indent");
si_indent.getMember("length") >>= c.indent.length;
si_indent.getMember("use_space") >>= c.indent.use_space;
}
...
// read configuration from properties file
std::ifstream input("myconfiguration.properties");
Configuration configuration;
input >> cxxtools::Properties(configuration); // parse properties and put result into the configuration object
// print out the configuration structure
std::cout << configuration.encoding << "\n"
<< "length=" << configuration.indent.length << "\n"
<< "use_space=" << configuration.indent.use_space << std::endl;
for (unsigned n = 0; n < configuration.plug_ins.size(); ++n)
std::cout << "plugin[" << n << "]=" << configuration.plug_ins[n] << "\n";
Note that cxxtools::SerializationInfo::getMember(string)
throws an exception
of type cxxtools::SerializationMemberNotFound
, if the member is not found.
This makes error handling easy, since you can either catch the exception or just
let it propagate to a higher level.
For a full example, the header <cxxtools/properties.h>
must be included.