Skip to content

Instantly share code, notes, and snippets.

@sillykelvin
Last active May 11, 2017 07:02
Show Gist options
  • Save sillykelvin/93f317c14a0c17edc648b87012eba037 to your computer and use it in GitHub Desktop.
Save sillykelvin/93f317c14a0c17edc648b87012eba037 to your computer and use it in GitHub Desktop.
A callback dispatcher with generic types
#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