property tree put/erase with const iterator, or how to convert const_iterator to iterator
Asked Answered
S

2

2

I am using boost 1.55.0 on ubuntu 12.04lts with clang 3.4.

I have a boost::property_tree::ptree whose xml input looks like:

<root>
    <persons>
        <person>
            <name>dad</name>
            <age>43</age>
        </person>
        <person>
            <name>me</name>
            <age>4</age>
        </person>
    </persons>
</root>

So I have a list of nodes with the same tag.

To read them I iterate over the tree, and depending on a condition I want to erase a node. This looks like:

boost::property_tree::ptree pt;
boost::property_tree::read_xml(inputFileName, pt);

boost::property_tree::ptree& persons = pt.get_child("root");
for(boost::property_tree::ptree::const_iterator it = persons.begin(); it != persons.end(); ++it)
{
    std::string name = it->second.get<std::string>("name");
    if(name == "dad")
        // erase that name node from pt
        persons.erase(it->second.find("name"); // this doesn't work
}

[Edit]As the answer from pmr supposes, I wrote the following code:

boost::property_tree::ptree::iterator i = persons.begin();
auto assoc_i = it->second.find("name");
auto ci = persons.to_iterator(assoc_i);
std::advance(i, std::distance<boost::property_tree::ptree::const_iterator>(iterator, ci)); // --> here it hangs
persons.erase(i);

Now it compiles, and the application does not crash, but it hangs at the mentioned position. And I don't know why. [/Edit]

Many thanks in advance.

Storekeeper answered 29/1, 2014 at 9:56 Comment(0)
R
4

Your issue at hand has little to do with the constness of the iterators, you are simply erasing with the wrong ptree iterator.

ptree& persons = pt.get_child("root.persons");
for(auto it = persons.begin(); it != persons.end();) {
   if(it->second.get<string>("name") == "dad")
        it = persons.erase(it);
    else
        ++it;
}

The ptree uses a multi_index for sub-nodes, and to keep an iteration stable over erasures, you must use the iterator returned from the erase() operation to continue the iteration, a common pattern.

Note that the XML in the question has an / (</age>) termination missing, it wouldn't validate. I am not editing the question to fix it since this could be one of the problems that you experience with the execution of the program.

Output of processing the ptree with the above code via write_xml(std::out, pt):

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <persons>
    <person>
      <name>me</name>
      <age>4</age>
    </person>
  </persons>
</root>
Ravenous answered 31/1, 2014 at 8:53 Comment(0)
F
4

C++11 APIs for containers specify a member function iterator container::erase(const_iterator, const_iterator). Unfortunately, basic_ptree does not do that, so you are stuck with old C++ way of converting a const_iterator to an iterator:

// tree is your ptree, my_const_iter a ptree::const_iterator
ptree::iterator i = tree.begin();
advance (i, std::distance<ConstIter>(i,my_const_iter));
Flagg answered 29/1, 2014 at 10:3 Comment(9)
@DieterLücking See signature 2 here: en.cppreference.com/w/cpp/container/vector/eraseFlagg
thank you. But now I have another problem. I explained it in an edit of my question. Do you know, why the application hangs now?Storekeeper
@Storekeeper I really recommend keeping to one topic per question. It is hard to see what is begin asked now. You might also want to split the sub-operations of the statement to get the exact point where the program hangs and look at it in a debugger.Flagg
@Flagg Thank you. I deleted all other questions, and splitted the code into sub-operations (edited my post). And the std::advance still does not work. Since I am using clang, I don't have a debugger. Or is there a clang debugger?Storekeeper
You can use GDB (preferably 7+) or LLDB (since 6.3 ships with Xcode). I'm not sure why you'd think a compiler toolchain as widely used as clang wouldn't support the same tools for debugging as you'd expect. It even now supports limited integration with Visual Studio on Windows. Quite impressive, really.Rheinlander
See also: #21132694Rheinlander
@LouisSt-Amour: I am sorry, but according to this link it seems to me, that there is no stable version of lldb. Or could you provide another link where I can find a stable version of lldbStorekeeper
The homepage states: Note that LLDB generally builds from top-of-trunk on Mac OS X with Xcode and on Linux (with clang and libstdc++/libc++).Rheinlander
It also says: LLDB is the default debugger in Xcode on Mac OS X and supports debugging C, Objective-C and C++ on the desktop and iOS devices and simulator.Rheinlander
R
4

Your issue at hand has little to do with the constness of the iterators, you are simply erasing with the wrong ptree iterator.

ptree& persons = pt.get_child("root.persons");
for(auto it = persons.begin(); it != persons.end();) {
   if(it->second.get<string>("name") == "dad")
        it = persons.erase(it);
    else
        ++it;
}

The ptree uses a multi_index for sub-nodes, and to keep an iteration stable over erasures, you must use the iterator returned from the erase() operation to continue the iteration, a common pattern.

Note that the XML in the question has an / (</age>) termination missing, it wouldn't validate. I am not editing the question to fix it since this could be one of the problems that you experience with the execution of the program.

Output of processing the ptree with the above code via write_xml(std::out, pt):

<?xml version="1.0" encoding="UTF-8"?>
<root>
  <persons>
    <person>
      <name>me</name>
      <age>4</age>
    </person>
  </persons>
</root>
Ravenous answered 31/1, 2014 at 8:53 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.