Boost C++ Libraries

...one of the most highly regarded and expertly designed C++ library projects in the world. Herb Sutter and Andrei Alexandrescu, C++ Coding Standards

Click here to view the latest version of this page.
PrevUpHomeNext

How to Access Data in a Property Tree

Property tree resembles (almost is) a standard container with value type of pair<string, ptree>. It has the usual member functions, such as insert, push_back, find, erase, etc. These can of course be used to populate and access the tree. For example the following code adds key "pi" with data (almost) equal to mathematical pi value:

ptree pt;
pt.push_back(ptree::value_type("pi", ptree("3.14159")));

To find the value of pi we might do the following:

ptree::const_iterator it = pt.find("pi");
double pi = boost::lexical_cast<double>(it->second._ptree_data__());

This looks quite cumbersome, and would be even more so if pi value was not stored so near the top of the tree, and we cared just a little bit more about errors. Fortunately, there is another, correct way of doing it:

ptree pt;
pt.put("pi", 3.14159);                // put double
double pi = pt.get<double>("pi");     // get double

It doesn't get simpler than that. Basically, there are 2 families of member functions, get and put, which allow intuitive access to data stored in the tree (direct children or not).

Three Ways of Getting Data

There are three versions of get: get, get (default-value version), and get_optional, which differ by failure handling strategy. All versions take path specifier, which determines in which key to search for a value. It can be a single key, or a path to key, where path elements are separated with a special character (a '.' if not specified differently). For example debug.logging.errorlevel might be a valid path with dot as a separator.

  1. The throwing version (get):
    ptree pt;
    /* ... */
    float v = pt.get<float>("a.path.to.float.value");
    
    This call locates the proper node in the tree and tries to translate its data string to a float value. If that fails, exception is thrown. If path does not exist, it will be ptree_bad_path exception. If value could not be translated, it will be ptree_bad_data. Both of them derive from ptree_error to make common handling possible.
  2. The default-value version (get):
    ptree pt;
    /* ... */
    float v = pt.get("a.path.to.float.value", -1.f);
    
    It will do the same as above, but if it fails, it will return the default value specified by second parameter (here -1.f) instead of throwing. This is very useful in common situations where one wants to allow omitting of some keys. Note that type specification needed in throwing version is normally not necessary here, because type is determined by the default value parameter.
  3. The optional version (get_optional):
    ptree pt;
    /* ... */
    boost::optional<float> v = pt.get_optional<float>("a.path.to.float.value");
    
    This version uses boost::optional class to handle extraction failure. On successful extraction, it will return boost::optional initialized with extracted value. Otherwise, it will return uninitialized boost::optional.

To retrieve value from this tree (not some subkey), use get_value, get_value (default-value version), and get_value_optional. They have identical semantics to get functions, except they don't take the path parameter. Don't call get with and empty path to do this as it will try to extract contents of subkey with empty name.

To use separator character other than default '.', each of the get versions has another form, which takes an additional parameter in front of path. This parameter of type char/wchar_t specifies the separating character. This is a lifesaving device for those who may have dots in their keys:

pt.get<float>('/', "p.a.t.h/t.o/v.a.l.u.e");
pt.get('/', "p.a.t.h/t.o/v.a.l.u.e", 0, NULL);
pt.get_optional<std::string>('/', "p.a.t.h/t.o/v.a.l.u.e");

One Way of Putting Data

To complement get, there is a put function. Contrary to get, it has only one variant. The reason is, there is no need to handle put failures so often (normally, put will only fail if conversion of the supplied value to data_type fails or the system runs out of memory. In the former case, put will throw ptree_bad_data). Sample usage of put might appear as:

ptree pt;
pt.put("a.path.to.float.value", 3.14f);

Calling put will insert a new value at specified path, so that a call to get specifying the same path will retrieve it. Further, put will insert any missing path elements during path traversal. For example, calling put("key1.key2.key3", 3.14f) on an empty tree will insert three new children: key1, key1.key2 and key1.key2.key3. The last one will receive a string "3.14" as data, while the two former ones will have empty data strings. put always inserts new keys at the back of the existing sequences.

Similar to get_value, there is also a put_value function. It does the same for this property tree what put does for its children. Thus, it does not require path:

ptree pt;
pt.__ptree_put_own__(3.14f);

PrevUpHomeNext