2011-11-16 9 views
15

Ho letto la documentazione per boost :: property_tree e non ho trovato un modo per aggiornare o unire un ptree con un altro ptree. Come faccio a fare questo?Come faccio a unire/aggiornare un boost :: property_tree :: ptree?

Dato il seguente codice, come sarebbe la funzione update_ptree?

#include <iostream> 
#include <boost/property_tree/ptree.hpp> 
using boost::property_tree::ptree; 

class A 
{ 
    ptree pt_; 
public: 
    void set_ptree(const ptree &pt) 
    { 
    pt_ = pt; 
    }; 
    void update_ptree(const ptree &pt) 
    { 
    //How do I merge/update a ptree? 
    }; 
    ptree get_ptree() 
    { 
    return pt_; 
    }; 
}; 

int main() 
{ 
    A a; 
    ptree pt; 
    pt.put<int>("first.number",0); 
    pt.put<int>("second.number",1); 
    pt.put<int>("third.number",2); 
    a.set_ptree(pt); 
    ptree pta = a.get_ptree(); 

    //prints "0 1 2" 
    std::cout << pta.get<int>("first.number") << " " 
      << pta.get<int>("second.number") << " " 
      << pta.get<int>("third.number") << "\n"; 


    ptree updates; 
    updates.put<int>("first.number",7); 
    a.update_ptree(updates); 
    pta = a.get_ptree(); 

    //Because the update_tree function doesn't do anything it just prints "0 1 2". 
    //I would like to see "7 1 2" 
    std::cout << pta.get<int>("first.number") << " " 
      << pta.get<int>("second.number") << " " 
      << pta.get<int>("third.number") << "\n"; 

    return 0; 
} 

Ho pensato di iterare sul nuovo ptree e di usare "put" per inserire valori. Ma "put" richiede un tipo e non so come ottenere quell'informazione dal nuovo ptree e usarlo come argomento per il vecchio ptree.

Una cosa che ho provato nella funzione update_ptree sta usando:

pt_.add_child(".",pt); 

Fondamentalmente cerco di aggiungere il pt come un bambino alla radice del pt_. Sfortunatamente questo non sembra funzionare.

Qualche idea?

Sono grato per qualsiasi aiuto.

Grazie.

(ho cercato di aggiungere il tag e property_tree ptree a questa domanda, ma non mi era permesso a)

risposta

17

Penso che devi attraversare in modo ricorsivo il property_tree.

È possibile definire una funzione che itera su ciascun nodo in modo ricorsivo e chiama un metodo per ogni nodo:

template<typename T> 
void traverse_recursive(const boost::property_tree::ptree &parent, const boost::property_tree::ptree::path_type &childPath, const boost::property_tree::ptree &child, T &method) 
{ 
    using boost::property_tree::ptree; 

    method(parent, childPath, child); 
    for(ptree::const_iterator it=child.begin();it!=child.end();++it) { 
    ptree::path_type curPath = childPath/ptree::path_type(it->first); 
    traverse_recursive(parent, curPath, it->second, method); 
    } 
} 

possiamo definire una funzione più semplice per chiamare alla precedente:

template<typename T> 
void traverse(const boost::property_tree::ptree &parent, T &method) 
{ 
    traverse_recursive(parent, "", parent, method); 
} 

Ora, è possibile modificare la classe a per aggiungere un metodo per unire un solo nodo e riempire il metodo update_ptree:

#include <boost/bind.hpp> 

class A { 
    ptree pt_; 

public: 
    void set_ptree(const ptree &pt) {  
    pt_ = pt; 
    } 

    void update_ptree(const ptree &pt) { 
    using namespace boost; 
    traverse(pt, bind(&A::merge, this, _1, _2, _3)); 
    } 

    ptree get_ptree() { 
    return pt_; 
    } 

protected: 
    void merge(const ptree &parent, const ptree::path_type &childPath, const ptree &child) { 
    pt_.put(childPath, child.data()); 
    }  
}; 

L'unica limitazione è che è possibile avere più nodi con lo stesso percorso. Ognuno di essi verrebbe utilizzato, ma solo l'ultimo verrà unito.

+0

Grazie. Questa è una soluzione interessante. Compilerai una visita! Ma cosa intendi con "è possibile avere più nodi con lo stesso percorso"? Tree_1 = "a.b.c" = 0 aggiornamento albero Tree_2 = "a.b.c" = 1, "a.b.d" = 2. Verrà aggiornato solo "a.b.d" = 2? (Eseguire il debug e vedere) – mantler

+0

È possibile avere più di un nodo con esattamente lo stesso percorso. Quando Tree_1 contiene "a.b.c" = 1, "a.b.c" = 2 e Tree_2 contiene "a.b.c" = 1, dopo aver aggiornato Tree_2 con Tree_1, Tree_2 conterrà "a.b.c" = 2. –

+0

Questo è un bel pezzo di codice. Come sapevi che questo operatore avrebbe funzionato su path_type ?: 'ptree :: path_type curPath = childPath/ptree :: path_type (it-> first);' Non riesco a vedere che l'opeator è definito nella documentazione. – 2NinerRomeo

5

Boost.Property albero non supporta questa, eppure: boost.org/doc/libs/1_48_0/doc/html/property_tree/appendices.html. Guarda la sezione di lavoro futura.

Relazioni matematiche: differenza di confine, unione, intersezione.

Un aggiornamento è semplicemente una differenza seguita da un'unione. a = (a - b) + b.

La soluzione generale richiederebbe ricorsivamente l'aggiornamento di ptree e il posizionamento di ciascuna foglia.

Tuttavia è possibile creare una soluzione sufficientemente buona con put_child. Questo può fare tutto il necessario, senza la complessità di una soluzione generale.

void merge(ptree& pt, const ptree& updates) 
{ 
    BOOST_FOREACH(auto& update, updates) 
    { 
     pt.put_child(update.first, update.second); 
    } 
} 

La soluzione abbastanza buono ha due limitazioni, per coincidenza sono le stesse limitazioni della ini_parser ha.

  • l'albero può essere solo due strati (ad esempio "first.number", ma non "first.again.number")
  • valori possono essere memorizzati solo nodi foglia.
+0

Grazie. Proverò questo e vedrò come funziona. È interessante quello che dici di "l'albero può essere solo due strati ..", non ci avevo pensato. Quindi potrebbe essere che non ci sia un aggiramento o un algoritmo "generico" per fare ciò che voglio fare (per una profondità di un albero arbitrario)? – mantler

Problemi correlati