2013-05-23 4 views
1

У меня есть список объектов boost :: function, и я пытаюсь найти конкретный, поэтому я могу удалить его из списка. Фактически функция зарегистрирована (нажата на вектор), и я хочу иметь возможность ее отменить (поиск вектора и удаление указателя соответствующей функции). Вот код:Сравнение указателей функций, хранящихся как boost :: function

#include <string> 
#include <vector> 

#include <boost/bind.hpp> 
#include <boost/function.hpp> 
#include <boost/shared_ptr.hpp> 

class DummyClass 
{ 
public: 
    std::string Data; 
}; 
typedef boost::shared_ptr<DummyClass> DummyClassPtrType; 

class UpdaterClass 
{ 
public: 
    void handle(DummyClassPtrType Dummy); 
}; 

class ManagerClass 
{ 
public: 
    typedef boost::function<void (DummyClassPtrType Dummy)> HandlerFunctionType; 
    typedef std::vector<HandlerFunctionType> HandlerFunctionListType; 
    // 
    HandlerFunctionListType HandlerFunctionList; 
    void registerHandler(HandlerFunctionType Handler) 
    { 
     HandlerFunctionList.push_back(Handler); 
    } 
    void unRegister(HandlerFunctionType Handler) 
    { 
     // find the function pointer in the list and delete it from the list if found 
     HandlerFunctionListType::iterator HandlerIter = HandlerFunctionList.begin(); 
     while (HandlerIter != HandlerFunctionList.end()) 
     { 
      if (*HandlerIter == Handler) // error C2666: 'boost::operator ==' : 4 overloads have similar conversions 
      { 
       HandlerIter = HandlerFunctionList.erase(HandlerIter); 
       break; 
      } 
      else 
      { 
       ++HandlerIter; 
      } 
     } 
    } 
}; 

int main() 
{ 
    ManagerClass Manager; 
    UpdaterClass Updater; 
    Manager.registerHandler(boost::bind(&UpdaterClass::handle, &Updater, _1)); 
    Manager.unRegister(boost::bind(&UpdaterClass::handle, &Updater, _1)); 
    return 0; 
} 

Компилятор (VS2008 SP1) не нравится строка:

if (*HandlerIter == Handler) 

, и я не могу понять, как достичь этого.

+3

Почему я не могу сравнивать объекты boost :: function с оператором == или оператором! =? http://www.boost.org/doc/libs/1_50_0/doc/html/function/faq.html#id1565973 – PiotrNycz

+0

См. соответствующую тему http://stackoverflow.com/questions/3629835/why-is-stdfunction- неравномерное сравнение –

+1

Вы можете использовать сигналы: http://www.boost.org/doc/libs/1_50_0/doc/html/signals/tutorial.html#id3161780 – PiotrNycz

ответ

2

В дополнение к ответу Якка другим обычным способом реализации этого является сохранение итератора в элементе в контейнере (этот итератор действует на «токен», о котором говорит Якк).

Поскольку вы, возможно, удаляете и добавляете другие элементы, прежде чем вы удаляете какой-либо конкретный элемент, вы должны выбрать контейнер, который не приведет к аннулированию его итераторов при вставке/удалении. std::vector явно не подходит для этого, но std::list есть.

Ваша registerHandler функция будет просто вернуть итератор, возвращаемый std::list::insert и unregisterHandler будет просто вопрос вызова HandlerFunctionList.erase(iteratorToken);.

Единственным недостатком этой реализации является то, что в отличие от Yakk's он не использует словарь для хранения токенов, поэтому он не может проверить действительность токена заранее, и все пойдет не так, если пользователь пройдет недействительный итератор на ваш unregisterHandler.

Тем не менее, повышается производительность, поскольку он позволяет избежать посреднического словаря в целом.

Выберите свой яд.

+0

Недостатком является то, что вы использовали 'std: : list'.Если количество вызовов, выполняемых для функций в списке, превышает количество функций в списке, вы не выполняете тест производительности для 'std :: list', а именно, что вы повторяете его меньше, чем вы удаляете из середины. Я бы поставил на то, что 'std :: vector , function>' (где 'std :: unique_ptr :: get()' действует как наш токен), который выполняет поиск по линейному и поисковому запросу, remove будет работать быстрее с меньшими накладными расходами, чем с помощью 'std :: list', но это было бы более сложным. – Yakk

+0

@Yakk: Итерирует над 'list' действительно *, что * медленно? Я понимаю, что кэш-трещина будет проблемой с тысячами или миллионами элементов, но в этом случае использования я сомневаюсь, что это имеет значение. – syam

+0

@Yakk: Я просто сделал быстрый тест: для 1000 элементов, итерация над «списком» примерно на 40% медленнее, чем «вектор». Для 100 предметов это примерно до 30%. 50 предметов: 25%. Так ты действительно прав, мой плохой. – syam

1

При регистрации обратных вызовов создайте жетоны (обычно я использую указатели или int) и возвращаю их вызывающему.

Вызывающий, который хочет удалить обратный вызов, должен использовать этот токен для запроса.

Это позволяет регистрировать один и тот же указатель функции дважды, с различными идентичностями, между прочим.

Если вы используете 64-битные целые числа и просто ослепляете приращение каждого токена, и вы регистрируете 1 миллион функций для каждого фрейма, и вы работаете со скоростью 1000 кадров в секунду, и вы оставите свой код на 100 тысяч лет, оберните вокруг не возникает. На 1 миллион лет это будет. Решите, следует ли использовать полнофункциональный указатель или int, в котором вы ищете разрывы и перерабатываете их.

Другой вариант - использовать std::unique_ptr<char>(new char()) для злоупотребления кучей и сделать свой токен void*.

Смежные вопросы