Selection in the codomain of interval_map - c++

In my current project processes, distinguishable intervals, needs to be combined, if they are adjacent.
For this purpose I wanted to use the fantastic boost::icl library. Every process can be uniquely identified by its id.
First I'm adding some intervals to my interval_map. Now I wanted to accomplish two things:
Iterate over all occurring process-types (Here id=1,4,7)
Secondly, iterate over all processes being in a certain subset of kinds, in such a way that merging of overlapping intervals is automatically done.
This is what I got so far:
#include <iostream>
#include <set>
#include <boost/icl/interval_map.hpp>
#include "boost/icl/closed_interval.hpp"
struct Process {
int id;
};
bool operator==(const Process& p, const Process& q) {
return p.id == q.id;
}
bool operator<(const Process& p, const Process& q) {
return p.id < q.id;
}
std::ostream& operator<<(std::ostream& str, const Process& p) {
str << "Process{" << p.id << "}";
return str;
}
int main(int, char**) {
using namespace boost::icl;
interval_map<double, std::set<Process>> imap;
imap.add({ interval<double>::closed(0., 4.),{ Process{ 4 } } });
imap.add({ interval<double>::closed(2., 6.),{ Process{ 1 } } });
imap.add({ interval<double>::closed(4., 9.),{ Process{ 4 } } });
imap.add({ interval<double>::closed(8., 8.),{ Process{ 7 } } });
for (auto&& iter : imap) {
std::cout << iter.first << " - " << iter.second<< std::endl;
}
for (auto iter : find(imap, { Process{4} })) { // How to implement find on codomain
// Should print:
// [0.,4.] - { Process{4}}
// [4.,9.] - { Process{4}}
std::cout << iter.first << " - " << iter.second << std::endl;
}
}

First, an observation, since the intervals are closed, [0,4] and [4,6] aren't actually adjacent, but overlapping. Did you mean right_open?
Second, the interval map models a function, the mapping is not guaranteed to be injective.
In the limited scope of your example, it seems you'd rather invert the datastructure, to arrive at:
#include "boost/icl/closed_interval.hpp"
#include <boost/icl/interval_map.hpp>
#include <iostream>
#include <set>
#include <map>
struct Process {
int id;
friend bool operator==(const Process& p, const Process& q) { return p.id == q.id; }
friend bool operator<(const Process& p, const Process& q) { return p.id < q.id; }
friend std::ostream& operator<<(std::ostream& str, const Process& p) {
return str << "Process{" << p.id << "}";
}
};
int main(int, char**) {
using namespace boost::icl;
using Map = std::map<Process, boost::icl::interval_set<double> >; // instead of boost::icl::interval_map<double, std::set<Process> >;
using IVal = Map::mapped_type::interval_type;
Map imap;
imap[{4}] += IVal::right_open(0, 4);
imap[{1}] += IVal::right_open(2, 6);
imap[{4}] += IVal::right_open(4, 9);
imap[{7}] += IVal::closed(8, 8);
//for (auto&& el : imap) { std::cout << el.first << " - " << el.second << std::endl; }
Process key{4};
std::cout << key << " - " << imap[key];
}
This results in:
Process{4} - {[0,9)}
Which is what I think you meant with "in such a way that merging of overlapping intervals is automatically done".
Having Both
Of course you can derive the inverse mappings from the original data-structure:
template <typename IMap>
auto inverted(IMap const& imap) {
std::map<typename IMap::codomain_type::value_type, boost::icl::interval_set<typename IMap::domain_type> > output;
for (auto& el : imap)
for (auto& key: el.second)
output[key] += el.first;
return output;
}
See it Live On Coliru
#include "boost/icl/closed_interval.hpp"
#include <boost/icl/interval_map.hpp>
#include <iostream>
#include <set>
struct Process {
int id;
friend bool operator==(const Process& p, const Process& q) { return p.id == q.id; }
friend bool operator<(const Process& p, const Process& q) { return p.id < q.id; }
};
std::ostream& operator<<(std::ostream& str, const Process& p) {
str << "Process{" << p.id << "}";
return str;
}
template <typename IMap>
auto inverted(IMap const& imap) {
std::map<typename IMap::codomain_type::value_type, boost::icl::interval_set<typename IMap::domain_type> > output;
for (auto& el : imap)
for (auto& key: el.second)
output[key] += el.first;
return output;
}
int main(int, char**) {
using namespace boost::icl;
using IMap = boost::icl::interval_map<double, std::set<Process> >;
using IVal = IMap::interval_type;
IMap imap;
imap.add({ IVal::right_open(0, 4), {Process{ 4 }} });
imap.add({ IVal::right_open(2, 6), {Process{ 1 }} });
imap.add({ IVal::right_open(4, 9), {Process{ 4 }} });
imap.add({ IVal::closed(8, 8), {Process{ 7 }} });
std::cout << imap << "\n\n";
for (auto&& iter : imap) {
std::cout << iter.first << " - " << iter.second << std::endl;
}
Process key{4};
std::cout << key << " - " << inverted(imap)[key] << "\n";
}
More Notes
Querying multiple keys in the domain is directly supported, see a good assortion of pointers here:
split_interval_map usage, efficient find all interval intersecting a point
boost multi_index_container search for records that fall within intervals defined by two fields
You can always construct your own data-structure that affords bi-directional indexes, such as shown e.g.
here boost::multi_index_container, operations on std::set inside container

Related

Recursive tabulation while printing map of custom class

I have a class Twhich has a map of string class T
For convinience, I would like to print the content in the class in an organized manner with tabulations.
As an example, I have:
#include <iostream>
#include <map>
#include <string>
class test;
typedef std::map<std::string, std::string> sMap;
typedef std::map<std::string, test> testMap;
class test {
public:
sMap map1;
testMap map2;
};
std::ostream& operator<<(std::ostream& os, const sMap& smap) {
for(const auto& s_smap : smap) {
os << s_smap.first
<< "\t"
<< s_smap.second
<< "\n";
}
return os;
}
std::ostream& operator<<(std::ostream& os, const test& t) {
os << t.map1
<< "\n";
for (const auto& s_map : t.map2) {
os << s_map.first
<< "\t"
<< s_map.second
<< std::endl;
}
return os;
}
int main() {
sMap myMap;
myMap["a"] = "b";
test obj;
obj.map1 = myMap;
test obj2;
obj2.map2.insert({"one", obj});
obj2.map2["one"].map2.insert({"two", obj});
obj2.map2["one"].map2["two"].map2.insert({"three", obj});
obj2.map2["one"].map2["two"].map2["three"].map2.insert({"four", obj});
std::cout << obj2 << std::endl;
return 0;
}
I would like the output to be:
one a b
two a b
three a b
four a b
How can the operator<< be overloaded to achieve this?
Doing this means you need to pass along extra information. You could create a printMap kind of fucntion that takes an indentation level as an argument, but you've said you want to achieve this by overloading operator<< so you can't pass that along unless you wrap your test objects in another class/struct that does carry that info.
That might look something like the below, which outputs:
% ./a.out
a b
a b
a b
a b
Tweaking this is left as an exercise for the OP.
#include <iostream>
#include <map>
#include <string>
struct test;
using sMap = std::map<std::string, std::string>;
using testMap = std::map<std::string, test>;
struct test {
sMap map1;
testMap map2;
};
struct indent {
const test &ref;
int indentLevel;
indent(const test &ref, int indentLevel=0)
: ref(ref), indentLevel(indentLevel)
{ }
};
std::ostream& operator<<(std::ostream& os, const indent &i) {
int j = i.indentLevel;
for (const auto &[f, s] : i.ref.map1) {
for (int k = 0; k < j; k++) os << "\t";
os << f << "\t" << s << "\n";
}
for (const auto &[f, s] : i.ref.map2) {
os << indent(s, j + 1);
}
return os;
}
std::ostream& operator<<(std::ostream& os, const sMap& smap) {
for (const auto &[f, s] : smap) {
os << f << "\t" << s << "\n";
}
return os;
}
std::ostream& operator<<(std::ostream& os, const test& t) {
os << t.map1
<< "\n";
for (const auto& s_map : t.map2) {
os << s_map.first
<< "\t"
<< s_map.second
<< std::endl;
}
return os;
}
int main() {
sMap myMap;
myMap["a"] = "b";
test obj;
obj.map1 = myMap;
test obj2;
obj2.map2.insert({"one", obj});
obj2.map2["one"].map2.insert({"two", obj});
obj2.map2["one"].map2["two"].map2.insert({"three", obj});
obj2.map2["one"].map2["two"].map2["three"].map2.insert({"four", obj});
std::cout << indent(obj2) << std::endl;
return 0;
}
Some aspects of this case incorporate post-C++11 features like structured bindings. If a newer standard cannot be used, these techniques can be converted to C++11 friendly constructs.

How to print elements of a std::vector<std::any> vector in C++?

The system is able to figure out the type of each element using a.type().name() but seriously is not able to print them?
#include <iostream>
#include <vector>
#include <any>
#include <string>
#include <algorithm>
template<typename Last>
void addElement(std::vector<std::any>& container, Last last) {
std::cout << "Last = " << last << std::endl;
container.push_back(last);
}
template<typename First, typename... Rest>
void addElement(std::vector<std::any>& container, First first, Rest... rest) {
std::cout << "Elem = " << first << std::endl;
container.push_back(first);
addElement(container, rest...);
}
template<typename... Ts>
std::vector<std::any> createAnyVector(Ts... ts) {
std::vector<std::any> container;
addElement(container, ts...);
return container;
}
int main() {
std::cout << "ANYVECTOR" << std::endl;
std::vector<std::any> container = createAnyVector("Hello", 3.14, 'A', true, 42);
std::cout << "Number of elements in container = " << container.size() << std::endl; // 5 correct.
for (const auto& a : container) {
std::cout << a.type().name() << ", " << "HERE?" << std::endl;
}
}
If I just write a at the place where now HERE? stands, it returns the error:
No operator << matches these operands
operand types are: std::basic_ostream<char, std::char_traits<char>> << const << std::any
As the comments point out, you cannot do anything directly with std::any, you can just hold them.
If you control the API and have the option to create a wrapper around std::any, you can maintain a function pointer to print the current std::any's contents like so:
struct printable_any {
template <class T>
printable_any(T&& t) : m_val(std::forward<T>(t)) {
m_print_fn = [](std::ostream& os, const std::any& val) {
os << std::any_cast<std::decay_t<T>>(val);
};
}
private:
using print_fn_t = void(*)(std::ostream&, const std::any&);
std::any m_val;
print_fn_t m_print_fn;
friend std::ostream& operator<<(std::ostream& os, const printable_any& val) {
val.m_print_fn(os, val.m_val);
return os;
}
};
Note that even with this, you cannot take a random std::any and print it, you have to construct it yourself along with the function pointer.
Usage:
int main() {
std::vector<printable_any> vals {
"Hello", 3.14, 'A', true, 42
};
for (auto& v : vals) {
std::cout << v << '\n';
}
}
Prints
Hello
3.14
A
1
42
https://godbolt.org/z/o1sejrv1e
Well, what would this print:
struct Foo {
void *p;
};
any v = Foo{};
cout << v << '\n'; // what should this print?
There is no toString equivalent in C++. If you want that behavior, you may create your own hierarchy:
struct Printable {
virtual void print(ostream&) const = 0;
};
auto& operator<<(ostream& os, any const& obj) {
any_cast<Printable const&>(obj).print(os);
return os;
}
Or customize it as you wish and handle the error cases as you like, also add special cases for the integer types if you want, etc.
Does the vector have to be of type any or are you allowed to at least specify the kind of types that are stored in the vector? Because if you can at least specify which types are allowed, I recommend using std::variant.
example: std::vector<variant<int, string>> myVectorVariant;
After that, you can actually print to the console the items in your vector through the use of std::get_if from std::variant. copy-able function below to output your vector for the most of the main primitive types: int, float, char, string.
#include <variant>
#include <vector>
#include <iostream>
using namespace std;
void printVectorVariantValues(vector<variant<int, float, char, string>> arg) {
try {
for (auto val : arg) {
if (const auto intPtr (get_if<int>(&val)); intPtr)
cout << *intPtr << " ";
else if (const auto floatPtr (get_if<float>(&val)); floatPtr)
cout << *floatPtr << " ";
else if (const auto charPtr (get_if<char>(&val)); charPtr)
cout << *charPtr << " ";
else if (const auto stringPtr (get_if<string>(&val)); stringPtr)
cout << *stringPtr << " ";
}
} catch (bad_variant_access err) {
cout << "something" << " ";
}
cout << "DONE" << endl;
}
int main() {
vector<variant<int, float, char, string>> myVectorVariant = {1, 2, 'a', 'b', 3, 0.4f, 0.5f, "c", "DeF", 0.6f, "gHi", "JkL" };
printVectorVariantValues(myVectorVariant);
return 0;
}
/*
output below:
1 2 a b 3 0.4 0.5 c DeF 0.6 gHi JkL DONE
*/

MessagePack C++ - How to iterate through an unknown data structure?

I want to share structured data between C++ and Python languages using MessagePack like this one:
{
"t" : [ [t00,...,t0N], ... , [tM0,...,tMN] ],
"x" : [ x0,..,xN],
"P" : [ [P00, ..., P0N], ..., [PM0,...,PMN] ]
}
The number of variables is optional so in some cases I will have for example only:
{
"t" : [ [t00,...,t0N], ... , [tM0,...,tMN] ]
}
Decoding this in Python is pretty simple, my problem is to figure out
how to decode this in C++ if I don't know in advance the structure of
the data ? or the exact number of variables that I would have; is it
possible to iterate the structure in these cases?
I managed to handle a "fixed" data structure ( always with the same
number of variables ) defining a struct for example:
struct variables
{
std::vector< std::vector<double> > t;
std::vector< double > x;
std::vector< std::vector<double> > P;
MSPACK_DEFINE_MAP( t, x, P );
};
std::stringstream inBuffer;
.... (read data )
std::string str( inBuffer.str() );
msgpack::object_handle oh = msgpack::unpack( str.data(), str.size() );
msgpack::object deserialized = oh.get();
variables var;
deserialized.convert( var );
Is there a better way to accomplish this ?, how could manage optional
variables that could not appear in the structure ?; I repeat the
previous question: could I iterate an unknown data structure in C++?,
how ?
Thanks in advance!
Regards, Ernesto
There are two ways to treat unknown data structure.
The first way is using parse/visitor mechanism.
Here is an example:
#include <msgpack.hpp>
#include <sstream>
#include <iostream>
// This is a simple print example visitor.
// You can do any processing in your visitor.
struct my_visitor : msgpack::null_visitor {
bool start_map_key() {
processing_map_key = true;
return true;
}
bool end_map_key() {
processing_map_key = false;
return true;
}
bool start_array(uint32_t size) {
std::cout << "array (size:" << size << ")[" << std::endl;
return true;
}
bool end_array() {
std::cout << "]" << std::endl;
return true;
}
bool visit_str(const char* v, uint32_t size) {
if (processing_map_key) {
std::cout << "map key:" << std::string(v, size) << std::endl;
}
return true;
}
bool visit_positive_integer(uint64_t v) {
std::cout << "found value:" << v << std::endl;
return true;
}
bool processing_map_key = false;
std::string indent;
};
int main() {
// create test data
std::stringstream ss;
msgpack::packer<std::stringstream> pk(ss);
pk.pack_map(1);
pk.pack("t");
pk.pack_array(2);
pk.pack_array(3);
pk.pack(1);
pk.pack(2);
pk.pack(3);
pk.pack_array(3);
pk.pack(4);
pk.pack(5);
pk.pack(6);
// print data (for debug)
{
auto oh = msgpack::unpack(ss.str().data(), ss.str().size());
std::cout << oh.get() << std::endl;
}
// apply visitor
{
my_visitor mv;
msgpack::parse(ss.str().data(), ss.str().size(), mv);
}
}
Running demo: https://wandbox.org/permlink/3NrR4IMDIuLTk9e9
See https://github.com/msgpack/msgpack-c/wiki/v2_0_cpp_visitor.
The other way is using msgpack::type::variant or `msgpack::type::variant_ref.
The former copies data, you can update it. The latter doesn't copy data. You cannot update it.
This approach requires boost. So you need to define MSGPACK_USE_BOOST. I recommend defining as a compiler option.
// Boost is required
#define MSGPACK_USE_BOOST
#include <msgpack.hpp>
#include <sstream>
#include <iostream>
struct my_visitor:boost::static_visitor<void> {
void operator()(uint64_t v) const {
std::cout << "positive insteger:" << v << std::endl;
}
// const is required for map key because std::multimap's key (first) is const.
void operator()(std::string const& v) const {
std::cout << "string:" << v << std::endl;
}
void operator()(std::vector<msgpack::type::variant>& v) const {
std::cout << "array found" << std::endl;
for (auto& e : v) {
boost::apply_visitor(*this, e);
}
}
void operator()(std::multimap<msgpack::type::variant, msgpack::type::variant>& v) const {
std::cout << "map found" << std::endl;
for (auto& e : v) {
std::cout << "key:" << std::endl;
boost::apply_visitor(*this, e.first);
std::cout << "value:" << std::endl;
boost::apply_visitor(*this, e.second);
}
}
template <typename T>
void operator()(T const&) const {
std::cout << " match others" << std::endl;
}
};
int main() {
// create test data
std::stringstream ss;
msgpack::packer<std::stringstream> pk(ss);
pk.pack_map(1);
pk.pack("t");
pk.pack_array(2);
pk.pack_array(3);
pk.pack(1);
pk.pack(2);
pk.pack(3);
pk.pack_array(3);
pk.pack(4);
pk.pack(5);
pk.pack(6);
auto oh = msgpack::unpack(ss.str().data(), ss.str().size());
std::cout << oh.get() << std::endl;
msgpack::type::variant v = oh.get().as<msgpack::type::variant>();
boost::apply_visitor(my_visitor(), v);
}
Running demo: https://wandbox.org/permlink/HQwJjfwW8rLEMi0d
See https://github.com/msgpack/msgpack-c/wiki/v2_0_cpp_variant
Here are exampless:
https://github.com/msgpack/msgpack-c/blob/master/example/boost/msgpack_variant_capitalize.cpp
https://github.com/msgpack/msgpack-c/blob/master/example/boost/msgpack_variant_mapbased.cpp
Both ways can treat unpredictable data structure. You need to do some visitor processing. If the data structure is predictable some extent, your original approach is also good way.
Actually there is a simpler way, if you are dealing with maps (like stated in the question), not arrays.
msgpack::object_handle oh = msgpack::unpack(/* some data */);
std::map<std::string,msgpack::type::variant> map = obj.convert();
This way you will get a map with all the data, no need for a visitor or boost.

C++ display map that has vector

std::map<std::string, std::vector<string>> data;
In order to print out this by using copy, how should my std::ostream_iterator be?
Apparently std::ostream_iterator<std::pair<std::string, std::vector<std::string>>> out_it(std::cout, "\n"); did not make it.
My operator<< overload is the following std::ostream& operator<<(std::ostream& out, const std::pair<std::string, std::vector<std::string>>& p) and it writes out the p.first and p.second and returns it.
If you do any serious programming in C++, you will eventually need a generic way to print out collections.
Here is the basis of one:
#include <iostream>
#include <map>
#include <vector>
#include <string>
// introduce the concept of an object that emits values to an ostream
// by default it simply calls operator <<
template<class T> struct emitter
{
using arg_type = T;
emitter(const T& v) : v_(v) {}
friend std::ostream& operator<<(std::ostream& os, const emitter& e) {
return os << e.v_;
}
const T& v_;
};
// introduce the concept of an io manipulator called emit
template<class T> auto emit(const T& v) -> emitter<T>
{
return emitter<std::decay_t<T>>(v);
}
// specialise the emitter for maps
template<class K, class V, class C, class A>
struct emitter<std::map<K, V, C, A>>
{
using arg_type = std::map<K, V, C, A>;
emitter(const arg_type& v) : v_(v) {}
friend std::ostream& operator<<(std::ostream& os, const emitter& e) {
const char* elem_sep = "\n\t";
const char* end_sep = " ";
os << "{";
for (const auto& elem : e.v_)
{
os << elem_sep << emit(elem.first) << ": " << emit(elem.second);
end_sep = "\n";
}
return os << end_sep << "}";
}
const arg_type& v_;
};
// specialise the emitter for vectors
template<class V, class A>
struct emitter<std::vector<V, A>>
{
using arg_type = std::vector<V, A>;
emitter(const arg_type& v) : v_(v) {}
friend std::ostream& operator<<(std::ostream& os, const emitter& e) {
const char* elem_sep = " ";
const char* end_sep = " ";
os << "[";
for (const auto& elem : e.v_)
{
os << elem_sep << emit(elem);
elem_sep = ", ";
}
return os << end_sep << "]";
}
const arg_type& v_;
};
int main() {
// build test data
std::map<std::string, std::vector<std::string>> data;
data.emplace("a", std::vector<std::string>{ "now", "is", "the", "time" });
data.emplace("b", std::vector<std::string>{ "for", "all", "good", "men" });
data.emplace("c", std::vector<std::string>{ "to", "come", "to", "the" });
data.emplace("d", std::vector<std::string>{ "aid", "of", "their", "party" });
// request an emitter manipulator
std::cout << emit(data) << std::endl;
}
Expected output:
{
a: [ now, is, the, time ]
b: [ for, all, good, men ]
c: [ to, come, to, the ]
d: [ aid, of, their, party ]
}
So here is a operator<< that will print out the contents of one pair from your map:
std::ostream& operator<<(std::ostream& out, const std::pair<std::string, std::vector<std::string>>& p) {
out << p.first << ": "; // prints the string from key
for (const auto& i : p.second) // loops throught the whole vector that is asociated with that key
out << i << ", ";
return out;
}
So to use it in this example. If you ennter this into your map:
std::map<std::string, std::vector<string>> data;
std::vector<std::string> vec = {"VAL1", "VAL2", "VAL3"};
data.insert(std::make_pair("KEY", vec));
auto it = data.find("KEY");
std::cout << *it;
This would be what wil get printed out using the operator<< above:
KEY: VAL1, VAL2, VAL3,
You can also change the formatting a bit so the comma isn't after the last value as well but that's only a cosmetic problem. Your problem was in that you wanted to print vector while it doesn't have std operator<<. So in order to print vector you must manually loop through it's content like in my example with the ranged for.
To custom print your vector, you'd have to write some code yourself. To make sure your custom operator is used, I suggest you use std::stringstream to turn your key-value pair into a string, which you'd then feed into an std::ostream_iterator<std::string>.
Something, like this (pardon the using namespace std;):
#include <iostream>
#include <vector>
#include <string>
#include <iterator>
#include <algorithm>
#include <sstream>
#include <map>
using namespace std;
int main() {
map<string, vector<string>> m {
{"a", {"1", "2"}},
{"b", {"1", "2"}},
{"b", {"1", "2"}},
};
transform(begin(m), end(m), ostream_iterator<string>(cout), [](auto& p){
stringstream ss;
ss << p.first << ", {";
bool first = true;
for (auto& s : p.second)
{
ss << (first ? "" : ", ") << s;
first = false;
}
ss << "}\n";
return ss.str();
});
return 0;
}
I didn't actually write the operator<<, but you can substitue yours to make the lambda body short);

Parse XML with boost property tree

I have the following XML file and I want to store it using the below structures.
the data structs:
struct transitions
{
string oldstate;
string event;
string newstate;
};
struct XML_Diagram
{
string diag_name;
string diag_defaultstate;
list<string> diag_states;
list<string> diag_events;
list<transitions> diag_transitions;
};
the xml file:
<diagram>
<diagname>DiagaX</diagname>
<states>
<state>A</state>
.............
</states>
<events>
<event>ev1</event>
.................
</events>
<defaultstate>A</defaultstate>
<transitions>
<transition>
<oldstate>A</oldstate>
<event>ev1</event>
<newstate>B</newstate>
</transition>
<transition>
<oldstate>B</oldstate>
<event>ev2</event>
<newstate>C</newstate>
</transition>
.........................
</transitions>
</diagram>
It is clear to me how can I to access diagram.states .
I can do it with the folowing code:
using boost::property_tree::ptree;
ptree pt;
// Get diagram states
BOOST_FOREACH(ptree::value_type &v, pt.get_child("diagram.states"))
{
diag_states.push_back(v.second.data());
}
What is not clear to me is how can I access the data from at the level diagram.transitions.transition ?
My problem is that I could not find any examples in the documentation on how to parse more complex xml files with several levels.
This useful utility function traverses and pretty-prints an entire property tree:
using boost::property_tree::ptree;
std::string q(const std::string& s) { return "\"" + s + "\""; }
void print_tree(const ptree& pt, int level)
{
const std::string sep(2 * level, ' ');
BOOST_FOREACH(const ptree::value_type &v, pt) {
std::cout
<< sep
<< q(v.first) << " : " << q(v.second.data()) << "\n";
print_tree(v.second, level + 1);
}
}
void print_tree(const ptree& pt) { print_tree(pt, 0); }
The v.second values are trees themselves that can be accessed with the usual get methods. The transitions
can for example be accessed and printed like this:
using std::string;
void print_transitions(const ptree& pt)
{
BOOST_FOREACH(
const ptree::value_type &v,
pt.get_child("diagram.transitions"))
{
const ptree& child = v.second;
std::cout
<< "Event "
<< child.get<string>("event")
<< " in state "
<< child.get<string>("oldstate")
<< " leads to state "
<< child.get<string>("newstate")
<< "\n";
}
}
Here is another example of how to print ptree with attributes:
namespace pt = boost::property_tree;
// worker
typedef std::pair<const pt::ptree&, unsigned> tree_printer;
// empty ptree helper
const pt::ptree& empty_ptree()
{
static pt::ptree t;
return t;
}
std::ostream& operator <<(std::ostream& os, const tree_printer& p)
{
const pt::ptree& tree = p.first;
if(tree.empty()) return os;
const std::string indent(p.second, ' ');
BOOST_FOREACH(const pt::ptree::value_type& v, tree)
{
const std::string& nodeName = v.first;
if(nodeName == "<xmlattr>") continue;
os << indent << nodeName;
const pt::ptree& attributes =
v.second.get_child("<xmlattr>", empty_ptree());
if(!attributes.empty())
{
os << " [ ";
BOOST_FOREACH(const pt::ptree::value_type& attr, attributes)
{
const std::string& attrName = attr.first;
const std::string& attrVal = attr.second.data();
os << attrName << " = '" << attrVal << "'; ";
}
os << "]";
}
os << "\n";
const pt::ptree& childNode = v.second;
os << tree_printer(childNode, p.second + 1);
}
return os;
}
std::ostream& operator <<(std::ostream& os, const pt::ptree& tree)
{
os << tree_printer(tree, 0);
return os;
}
Hope this helps.