boost :: python и set :: erase -> странное поведение - PullRequest
6 голосов
/ 20 ноября 2011

Я пытаюсь хранить объекты в std :: set. Это объекты boost :: shared_ptr <>, происходящие из среды python. добавление значений в набор не вызовет никаких проблем. Но когда я пытаюсь стереть значение, хотя я передаю ту же самую ссылку, это не сработает. Вот пример:

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

компиляция:

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

и теперь небольшой скрипт на python:

from test import *

f = Foo()
b = Bar()

f.add(b)

f.remove(b)

Вот результат:

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 !
  • Я храню 0x8e89c58 внутри набора и снаружи, на всякий случай
  • Я передаю одну и ту же ссылку на оба вызова (0x8e89c58)
  • просто чтобы убедиться, что я проверил, v == val
  • Я пытаюсь стереть с помощью v - он не работает
  • Я пытаюсь стереть с помощью val - все работает!

Я полностью потерян там - не могу понять, что вызывает это. Любой вход?

1 Ответ

11 голосов
/ 20 ноября 2011

Я запустил ваш пример, затем добавил несколько утверждений, которые, как я думал, должны содержаться в del():

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

Одна из них не удалась!

Я копался в реализации operator<для boost::shared_ptr и нашел что-то странное: он сравнивает счетчики ссылок, а не внутренние указатели!Небольшое копание нашло сообщение в списке рассылки об этой проблеме с некоторыми полезными ссылками на два документа C ++: N1590, которое объясняет, почему люди думают, что это хорошая идея, и N2637, которое объясняет, почему это не так.

Похоже, что люди из Boost еще не приняли рекомендацию N2637, а C ++ 11.Поэтому я снова построил ваш тест, используя C ++ 11 (g++ -std=c++0x), удалив using namespace boost;, чтобы использовать std::shared_ptr.Это привело к появлению ужасного сообщения об ошибке на основе шаблона, которое было решено путем добавления этого сверху (легко выводится из boost/smart_ptr/shared_ptr.hpp):

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

И это работает!

Если вы можетене используйте C ++ 11, просто реализуйте свой собственный компаратор для вашего набора, который разумно сравнивает указатели:

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

Тогда это будет работать:

set< shared_ptr<Bar>, SmartComparator<Bar> > v_set;
...