问题
I have a property tree where all the data is stored in its leaf nodes. The Tree, however, has a complex structure. What I want to do now is:
- get all (and only the) leaf nodes of the tree, for they contain the data and
- recall the path leading to the respective leaf node
Eventually, I want to receive a key/value pair of all (and only the) leaf nodes where the key contains the complete path to the node and the value contains the node's value.
My questions are:
- Is there a more convenient way than to recursively iterate through the whole tree, store the respective path and read out the values of the nodes that don't have children (i.e. a "get_leaves()" function)?
- If I have some pointer to a subtree (ptree variable, iterator, whatever..) of a given tree, is there a method to easily determine the relative path of that subtree inside the tree?
回答1:
I'd just write some helper functions. They're really not that difficult. Here's a completely generic tree visitation function that optionally takes a predicate:
template <typename Tree, typename F, typename Pred/* = bool(*)(Tree const&)*/, typename PathType = std::string>
void visit_if(Tree& tree, F const& f, Pred const& p, PathType const& path = PathType())
{
if (p(tree))
f(path, tree);
for(auto& child : tree)
if (path.empty())
visit_if(child.second, f, p, child.first);
else
visit_if(child.second, f, p, path + "." + child.first);
}
template <typename Tree, typename F, typename PathType = std::string>
void visit(Tree& tree, F const& f, PathType const& path = PathType())
{
visit_if(tree, f, [](Tree const&){ return true; }, path);
}
You can use it with a predicate like
#include <boost/property_tree/ptree.hpp>
bool is_leaf(boost::property_tree::ptree const& pt) {
return pt.empty();
}
And here's a simple demo:
Live On Coliru
#include <iostream>
int main()
{
using boost::property_tree::ptree;
auto process = [](ptree::path_type const& path, ptree const& node) {
std::cout << "leave node at '" << path.dump() << "' has value '" << node.get_value("") << "'\n";
};
ptree pt;
pt.put("some.deeply.nested.values", "just");
pt.put("for.the.sake.of.demonstration", 42);
visit_if(pt, process, is_leaf);
}
Prints:
leave node at 'some.deeply.nested.values' has value 'just'
leave node at 'for.the.sake.of.demonstration' has value '42'
UPDATE
Just noted the second half of the question. here's how to do it using the same visitor:
template <typename Tree>
boost::optional<std::string> path_of_optional(Tree const& tree, Tree const& target) {
boost::optional<std::string> result;
visit(tree, [&](std::string const& path, Tree const& current) { if (&target == ¤t) result = path; });
return result;
}
template <typename Tree>
std::string path_of(Tree const& tree, Tree const& target) {
auto r = path_of_optional(tree, target);
if (!r) throw std::range_error("path_of");
return *r;
}
And a demo Live On Coliru
std::cout << "Path from node: " << path_of(pt, pt.get_child("for.the.sake")) << "\n";
来源:https://stackoverflow.com/questions/30571536/how-return-leaf-nodes-of-a-boostproperty-tree