2011-11-20 15 views
6

Estoy tratando de almacenar objetos en un std :: set. Esos objetos son boost :: shared_ptr <>, que provienen del entorno python. agregar valores al conjunto no causará ningún problema. Pero cuando intento borrar un valor, aunque esté pasando la misma referencia, no funcionará. Aquí está un ejemplo:boost :: python y set :: erase -> comportamiento raro

#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");  
} 

compilación:

%> 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 

y ahora, un pequeño script en Python:

from test import * 

f = Foo() 
b = Bar() 

f.add(b) 

f.remove(b) 

Aquí está el resultado:

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 ! 
  • almaceno 0x8e89c58 dentro del conjunto y fuera, por si acaso
  • estoy pasando la misma referencia a ambas llamadas (0x8e89c58)
  • sólo para asegurarse puedo comprobar si v == val
  • Trato de borrar usando v - no funciona
  • Intento borrar usando val - ¡funciona!

Estoy completamente perdido allí - no puedo ver lo que está causando esto. ¿Alguna entrada?

+0

El uso de "valor" "val" y "v", todos como nombres de variables en la misma función, hacen que el código sea innecesariamente difícil de seguir. –

+0

de hecho, lo siento por eso. Espero que esté más claro ahora. – girodt

Respuesta

11

que corrió su ejemplo a continuación, añade algunas afirmaciones que pensé que debería sostener en del():

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

uno de ellos no!

Indagué en la implementación de operator< para boost::shared_ptr y encontré algo extraño: ¡compara los recuentos de referencia en lugar de los punteros internos! Un poco de excavación encontró un mailing list post sobre este tema con algunos enlaces útiles a dos documentos de C++: N1590 que explica por qué la gente pensó que esta era una buena idea, y N2637 que explica por qué no lo fue.

Parece que la gente Boost no (¿todavía?) Adoptó la recomendación N2637, pero C++ 11 sí. Así que construí su prueba nuevamente usando C++ 11 (g++ -std=c++0x), habiendo eliminado using namespace boost; para usar std::shared_ptr. Esto dio lugar a un mensaje de error plantilla plagada horrible, el cual fue resuelto mediante la adición de este en la parte superior (fácilmente derivada de boost/smart_ptr/shared_ptr.hpp):

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

Y funciona!

Si no puede utilizar C++ 11, acaba de poner en práctica su propio comparador personalizado para su conjunto, que compara los punteros con cordura:

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

entonces esto va a trabajar:

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

Wow. Estoy impresionado por la calidad de tu respuesta. De hecho, resolvió mi problema. Muchas gracias por su tiempo ! – girodt

Cuestiones relacionadas