Last active
May 11, 2017 07:02
-
-
Save sillykelvin/93f317c14a0c17edc648b87012eba037 to your computer and use it in GitHub Desktop.
A callback dispatcher with generic types
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
#include <memory> | |
#include <iostream> | |
#include <unordered_map> | |
// this is useful when dealing with google protobuf messages, what | |
// you get in your message callback is a google::protobuf::Message | |
// type, however, what you need is a concrete type, something like | |
// LoginRequest/LoginResponse, so you have to do the following: | |
// | |
// const LoginRequest *req = static_cast<const LoginRequest*>(msg); | |
// | |
// doing so in each callback is a tedious job, so I extracted this | |
// into the message dispatcher itself, concrete type callbacks can | |
// be registered to the dispatcher without any extra effort. | |
// K stands for key type | |
// R stands for return type | |
// M stands for message type | |
// D stands for derived message type | |
template<typename R, typename M, typename... Args> | |
class callback_base { | |
public: | |
virtual ~callback_base() = default; | |
virtual R on_msg(Args&&..., M&&) const = 0; | |
}; | |
template<typename R, typename M, typename D, typename... Args> | |
class callback : public callback_base<R, M, Args...> { | |
public: | |
typedef R(*handler)(Args..., D); | |
callback(handler h) : h_(h) {} | |
virtual ~callback() = default; | |
virtual R on_msg(Args&&... args, M&& m) const override { | |
D d = static_cast<D>(std::forward<M>(m)); | |
return h_(std::forward<Args>(args)..., d); | |
} | |
private: | |
handler h_; | |
}; | |
template<typename K, typename R, typename M, typename... Args> | |
class callback_dispatcher { | |
public: | |
typedef callback_base<R, M, Args...> callback_type; | |
template<typename D> | |
void register_handler(const K& key, typename callback<R, M, D, Args...>::handler fn) { | |
auto cb = std::unique_ptr<callback_type>(new callback<R, M, D, Args...>(fn)); | |
key2handler_[key] = std::move(cb); | |
} | |
R dispatch(const K& key, Args&&... args, M&& m) { | |
auto it = key2handler_.find(key); | |
if (it == key2handler_.end()) { | |
std::cout << "no handler found: " << key << std::endl; | |
return R(); | |
} | |
return it->second->on_msg(std::forward<Args>(args)..., std::forward<M>(m)); | |
} | |
private: | |
std::unordered_map<K, std::unique_ptr<callback_type>> key2handler_; | |
}; | |
struct base { | |
int print() const { std::cout << "base" << std::endl; return 0; } | |
}; | |
struct derived : public base { | |
int print() const { std::cout << "derived" << std::endl; return 1; } | |
}; | |
int cb0(char, float, const base* b) { | |
return b->print(); | |
} | |
int cb1(char, float, const derived* d) { | |
return d->print(); | |
} | |
int cb2(char, float, const base*) { | |
std::cout << "another cb" << std::endl; | |
return 2; | |
} | |
int main() { | |
callback_dispatcher<int, int, const base*, char, float> cd; | |
cd.register_handler<const base*>(0, cb0); | |
cd.register_handler<const derived*>(1, cb1); | |
cd.register_handler<const base*>(2, cb2); | |
base b; | |
std::cout << "cb0 returns " << cd.dispatch(0, 'a', 1.0, &b) << std::endl; | |
std::cout << "cb1 returns " << cd.dispatch(1, 'a', 1.0, &b) << std::endl; | |
std::cout << "cb2 returns " << cd.dispatch(2, 'a', 1.0, &b) << std::endl; | |
return 0; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment