The any-event dispatcher
The std::function class allows us to easily store any function object. We can use this feature in order to avoid type-related problem, like in events systems.
#include <iostream> #include <functional> #include <type_traits> #include <unordered_map> class EventsDispatcher { public: /// \brief Registers a callback which is going to be called by the injectEvent function template<typename TEvent, typename TListener> void registerListener(const TListener& function) { _registerListener<TEvent>(function); } /// \brief Calls all the corresponding callbacks template<typename TEvent> void injectEvent(TEvent& ev) { _injectEvent(ev); } private: // for each event type, we have a list of callbacks // these callbacks are of the form std::function<void (void*)> because we will convert between the &event and void* std::unordered_multimap<const std::type_info*,std::function<void (void*)>> _listeners; template<typename TEvent, typename TListener> void _registerListener(const TListener& function) { static_assert(!std::is_array<TEvent>::value, "You cannot register an array as an event type"); static_assert(std::is_convertible<TListener,std::function<void (TEvent&)>>::value, "Unvalid callback for this event type"); _listeners.insert(std::make_pair(&typeid(std::decay<TEvent>::type), [=](void* ev) { function(*static_cast<TEvent*>(ev)); })); } template<typename TEvent> void _injectEvent(TEvent& ev) { auto range = _listeners.equal_range(&typeid(std::decay<TEvent>::type)); for (auto i = range.first; i != range.second; ++i) (i->second)(static_cast<void*>(&ev)); } }; // example of an event type struct KeyboardEvent { explicit KeyboardEvent(wchar_t text) : text(text) {} wchar_t text; }; // example of an event callback void printKeyboard(const KeyboardEvent& ev) { std::wcout << ev.text << std::endl; } int main() { EventsDispatcher dispatcher; dispatcher.registerListener<KeyboardEvent>(&printKeyboard); dispatcher.injectEvent(KeyboardEvent(L'a')); #ifdef _WIN32 system("pause"); #endif return 0; }Lazy evaluation
Instead of writing setters like
void setXXX(int);you can writevoid setXXX(std::function<int ()>);. The class will then store the function and call it only when needed, thus avoiding the need for the class owner to call the setter repeatedly.This is especially useful for continuous data, eg:
object.setPosition([]() { return float(clock()) / float(CLOCKS_PER_SEC); });#include <ctime> #include <iostream> #include <functional> class TextPrinter { public: void setTextToPrint(wchar_t text) { setTextToPrint([=]() { return text; }); } void setTextToPrint(const std::function<wchar_t ()>& f) { _function = f; } void print() const { std::wcout << _function() << std::endl; } private: std::function<wchar_t ()> _function; }; int main() { TextPrinter printer; wchar_t character = L'a'; // 'classical' way to do: updating the data manually printer.setTextToPrint(character++); printer.print(); printer.setTextToPrint(character++); printer.print(); printer.setTextToPrint(character++); printer.print(); // more convenient way printer.setTextToPrint([&character]() { return character++; }); printer.print(); printer.print(); printer.print(); #ifdef _WIN32 system("pause"); #endif return 0; }Storing objects thanks to shared_ptr
Thanks to shared_ptr you can use a std::function as a storage for any object type, even if not a function object.
#include <functional> #include <iostream> #include <memory> #include <string> // example of a class which handles the keyboard (real implementation not shown) class KeyboardHandle { public: void setTextCallback(std::function<void (const std::wstring&)> f) { _callback = f; } void simulateText(const std::wstring& text) const { _callback(text); } private: std::function<void (const std::wstring&)> _callback; }; // example of a class which can print text on the screen class TextPrinter { public: void print(const std::wstring& text) const { std::wcout << text << std::endl; } }; // this function sets the keyboard so that it will automatically print the text written on it void initKeyboard(KeyboardHandle& kb) { auto printer = std::make_shared<TextPrinter>(); kb.setTextCallback([printer](const std::wstring& txt) { printer->print(txt); }); } int main() { KeyboardHandle kb; initKeyboard(kb); // apparently there isn't any trace of the TextPrinter instance but it still exists in memory // and whenever a text is written on the keyboard it will automatically be printed kb.simulateText(L"hello world"); #ifdef _WIN32 system("pause"); #endif return 0; }And more...
The
std::functionis really one of the most interesting features of C++0x.
There are a lot of different ways to use it, so don't hesitate to try out!