2011-11-20 8 views
6

Sto provando a memorizzare oggetti in un oggetto std :: set. Questi oggetti sono boost :: shared_ptr <>, provenienti dall'ambiente python. l'aggiunta di valori al set non causerà alcun problema. Ma quando provo a cancellare un valore, anche se sto passando lo stesso riferimento, non funzionerà. Ecco un esempio:boost :: python e set :: erase -> comportamento strano

#include <set> 
#include <iostream> 

#include <boost/shared_ptr.hpp> 
#include <boost/python.hpp> 

using namespace std; 
using namespace boost; 
using namespace boost::python; 

struct Bar 
{ 
    Bar() {} 
}; 

struct Foo 
{ 
    set< shared_ptr<Bar> > v_set; 
    shared_ptr<Bar> v_ptr; 

    Foo() {} 

    void add(shared_ptr<Bar> v_param) { 
    cout << "storing " << v_param << "in v_set and v_ptr" << endl; 
    v_set.insert(v_param); 
    v_ptr = v_param; 

    } 

    void del(shared_ptr<Bar> v_param) { 
    cout << "deleting " << v_param << endl; 
    if (v_param == v_ptr) { 
     cout << "v_param == v_ptr" << endl; 
    } else { 
     cout << "v_param != v_ptr" << endl; 
    } 

    cout << "erasing from v_set using v_param" << endl; 
    if (v_set.erase(v_param) == 0) { 
     cout << "didn't erase anything" << endl; 
    } else { 
     cout << "erased !" << endl; 
    } 

    cout << "erasing from v_set using v_ptr" << endl; 
    if (v_set.erase(v_ptr) == 0) { 
     cout << "didn't erase anything" << endl; 
    } else { 
     cout << "erased !" << endl; 
    } 
    } 
}; 

BOOST_PYTHON_MODULE (test) 
{ 
    class_< Foo, shared_ptr<Foo> >("Foo") 
     .def("add",&Foo::add) 
     .def("remove",&Foo::del); 

    class_< Bar, shared_ptr<Bar> >("Bar");  
} 

compilazione:

%> gcc -pthread -fno-strict-aliasing -march=i686 -mtune=generic -O2 -pipe -DNDEBUG -march=i686 -mtune=generic -O2 -pipe -fPIC -I/usr/include/python2.7 -c test.cpp -o test.o 

%> g++ -pthread -shared -Wl,--hash-style=gnu -Wl,--as-needed build/temp.linux-i686-2.7/test.o -L/usr/lib -lboost_python -lpython2.7 -o test.so 

e ora, un piccolo script python:

from test import * 

f = Foo() 
b = Bar() 

f.add(b) 

f.remove(b) 

Ecco il risultato:

storing 0x8c8bc58in v_set and v_ptr 
deleting 0x8c8bc58 
v_param == v_ptr 
erasing from v_set using v_param 
didn't erase anything 
erasing from v_set using v_ptr 
erased ! 
  • I memorizzare 0x8e89c58 all'interno del set e fuori, nel caso in cui
  • sto passando lo stesso riferimento a entrambe le chiamate (0x8e89c58)
  • solo per assicurarsi che posso controllare se v == val
  • provo a cancellare usando v - non funziona
  • Cerco di cancellare usando val - funziona!

Sono completamente perso lì - non riesco a vedere cosa sta causando questo. Qualche input?

+0

L'utilizzo di "valore" "val" e "v" tutti come nomi di variabili nella stessa funzione rendono il tuo codice inutilmente difficile da seguire. –

+0

davvero, mi dispiace per quello. Spero che ora sia più chiaro. – girodt

risposta

11

mi sono imbattuto il tuo esempio poi aggiunto alcune affermazioni che ho pensato dovrebbe tenere in del():

assert(!(v_param < v_ptr)); 
assert(!(v_ptr < v_param)); 

uno di essi non!

Ho esaminato l'implementazione di operator< per boost::shared_ptr e ho trovato qualcosa di strano: confronta i conteggi di riferimento anziché i puntatori interni! Un piccolo scavo ha trovato uno mailing list post su questo problema con alcuni link utili a due documenti C++: N1590 che spiega perché la gente pensava che fosse una buona idea, e N2637 che spiega il motivo per cui non lo era.

Sembra che le persone Boost non abbiano (ancora?) Adottato la raccomandazione N2637, ma C++ 11 lo ha. Così ho ricostruito il tuo test usando C++ 11 (g++ -std=c++0x), dopo aver rimosso using namespace boost; in modo da utilizzare std::shared_ptr. Ciò ha provocato un messaggio di errore modello di colpa orribile che è stato risolto aggiungendo questo alla parte superiore (facilmente derivato da boost/smart_ptr/shared_ptr.hpp):

template<class T> inline T * get_pointer(std::shared_ptr<T> const & p) 
{ 
    return p.get(); 
} 

E funziona!

Se non è possibile utilizzare C++ 11, basta implementare il proprio confronto personalizzato per il set, che mette a confronto i puntatori in modo sano:

template <typename T> 
struct SmartComparator 
{ 
    bool operator()(shared_ptr<T> const& lhs, shared_ptr<T> const& rhs) { 
     return lhs.get() < rhs.get(); 
    } 
}; 

allora questo lavoro:

set< shared_ptr<Bar>, SmartComparator<Bar> > v_set; 
+0

Wow. Sono impressionato dalla qualità della tua risposta. Ha davvero risolto il mio problema. Grazie mille per il vostro tempo ! – girodt