2013-06-02 3 views
4

Я пытаюсь написать простой диспетчер, код пользователя может прикреплять к нему обратные вызовы. Каждое событие имеет известную подпись, а код пользователя должен вызвать диспетчеризацию с правильными номерами и типами аргументов. Это управляется переменными аргументами. Но, freestandingInt не принимается, поскольку вектор не имеет правильного типа. Как сделать его общим?Сборник std :: functions с различными аргументами

Следует минимальный пример

void freestanding() { 
std::cout << "freestanding" << std::endl; 
} 

void freestandingInt(int iArg) { 
    std::cout << "freestandingInt " << iArg << std::endl; 
} 


struct Dispatcher { 
typedef struct Event_ { 
    std::vector<std::function<void()> > listeners; 
} Event; 

template<class... Args> 
void dispatch(int eventNr, Args&&... args) { 
    for (auto listener: events[eventNr].listeners) { 
    std::function<void()> f(std::bind(listener, std::forward<Args>(args)...)); 
    f(); 
    } 
} 

std::map<int, Event> events; 
}; 

int main (int argc, char **argv) { 
    Dispatcher disp; 
    disp.events[0].listeners.push_back(freestanding); 
    disp.dispatch(0); // OK 

    // error here 
    //disp.events[1].listeners.push_back(freestandingInt); 

} 
+0

Почему только один вид события? Как другой вопрос, сколько подписей? Может ли список быть централизованным? Какие ошибки при несоответствии аргументов в порядке? – Yakk

+0

Вот и все, у меня нет надежды на сохранение только одного, я вынужден использовать наследование и хранить Event * на карте? –

+0

'Событие *' бессмысленно. Перед этим я использую стирание типа стирания. Если есть более одного события, может быть более одного диспетчера. Почему нет? – Yakk

ответ

5

Здесь подход, основанный на создании std::multimap от std::type_index функции к std::function соответствующего типа:

#include <functional> 
#include <iostream> 
#include <map> 
#include <memory> 
#include <typeindex> 

void freestanding() { 
    std::cout << "freestanding" << std::endl; 
} 

void freestandingInt(int iArg) { 
    std::cout << "freestandingInt " << iArg << std::endl; 
} 

// Base class for all functions so that we can store all functions 
// in a single container. 
struct Function { 
    virtual ~Function() { } 
}; 

// Derived class template for functions with a particular signature. 
template <typename T> 
struct BasicFunction : Function { 
    std::function<T> function; 
    BasicFunction(std::function<T> function) : function(function) { } 
}; 

// Generic container of listeners for any type of function 
typedef std::multimap<std::type_index,std::unique_ptr<Function> > Listeners; 

template <typename Func> 
static void addListener(Listeners &listeners,Func &function) 
{ 
    std::type_index index(typeid(Func)); 
    std::unique_ptr<Function> 
    func_ptr(new BasicFunction<Func>(std::function<Func>(function))); 
    listeners.insert(Listeners::value_type(index,std::move(func_ptr))); 
} 

template <typename... Args> 
static void callListeners(const Listeners &listeners,Args&&... args) 
{ 
    typedef void Func(typename std::remove_reference<Args>::type...); 
    std::type_index index(typeid(Func)); 
    Listeners::const_iterator i = listeners.lower_bound(index); 
    Listeners::const_iterator j = listeners.upper_bound(index); 
    for (;i!=j; ++i) { 
    const Function &f = *i->second; 
    std::function<Func> func = 
     static_cast<const BasicFunction<Func> &>(f).function; 
    func(std::forward<Args>(args)...); 
    } 
} 

struct Dispatcher { 
    typedef struct Event_ { 
    Listeners listeners; 
    } Event; 

    template<class... Args> 
    void dispatch(int eventNr, Args&&... args) { 
    callListeners(events[eventNr].listeners,std::forward<Args>(args)...); 
    } 

    std::map<int, Event> events; 
}; 

int main (int argc, char **argv) { 
    Dispatcher disp; 
    addListener(disp.events[0].listeners,freestanding); 
    addListener(disp.events[0].listeners,freestandingInt); 
    disp.dispatch(0,5); 
} 

Выход:

freestandingInt 5 
+0

Я использовал его, и мне это очень нравится. Это также очень хороший пример использования multimap с type_index, спасибо –

+0

Как минимум, 'Function' нужен виртуальный деструктор. И вся идея восстановления типа функции из типа аргумента, когда аргументы берутся при пересылке ссылки, имеет мало смысла. В качестве простого примера этот код разбивается на 'int i = 0; disp.dispatch (0, i); '. –

+0

@ T.C .: Оба хороших момента. Я добавил виртуальный деструктор. Я также изменил код на использование 'std :: remove_reference', чтобы он обрабатывал упомянутый вами случай, хотя есть много других случаев, которые все еще не сработают. Сделать обращение с разрешением перегрузки эквивалентным стандартным правилам было бы довольно сложно. –

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