Chromium's callback<>, bind<> from scratch
I've been working on chromium source for few years. I want to analyze how
Callback<> works because it looks like a magic at least to me.
Let's start with simple function void func()
1 Mission 1: Implement Callback for void func()
void void_func() { } int main(int, char* []) { Callback<void()> cb = Bind(&void_func); cb.Run(); return 0; }
template <typename Sig> class Callback; template <typename R> class Callback<R()> { public: Callback(R (*functor)()) : functor_(functor) {} R Run() { return functor_(); } private: R (*functor_)(); }; template <typename R> Callback<R()> Bind(R (*functor)()) { return Callback<R()>(functor); } // ...
One thing worth to mention is declaring template <typename Sig>class
Callback. Sig can be combination of any return type with any number of
parameter type. For example, we can use Callback<void()>,
Callback<void(int)>, Callback<void(Obj::*)(int, double)> with only one
parameter type. This trick is used very often for other template code, so we need
to understand how to use this trick.
If we compile this Callback<void()> cb = Bind(&void_func);, In Bind, type
deduce [R = void].
2 Mission 2: Support void(*)(int)
Let's support void int_func(int).
void int_func(int); Callback<void(int)> cb2 = Bind(&int_func); cb2.Run(1);
The most simple way of supporting this is overloading. We can create another
Callback for void(*)(int).
template <typename R, typename P> class Callback<R(P)> { public: Callback(R (*functor)(P)) : functor_(functor) {} R Run(P p) { return functor_(p); } private: R (*functor_)(P); }; template <typename R, typename P> Callback<R(P)> Bind(R (*functor)(P)) { return Callback<R(P)>(functor); }
Both in Bind and Callback, type deduce [R = void, P = int].
3 Mission 3: Support member method Bind(&Obj::void_func, &obj)
3.1 Overloading fail
Support this.
struct Obj { void void_func() {} }; Obj obj; Callback<void()> cb3 = Bind(&Obj::void_func, &obj); cb3.Run();
Since Bind binds both its method and instance of Obj (Obj*), we need to keep
Obj* as well as its method. Let's call it RunnableAdapter.
Like we've done so far, let's overcome this by overloading.
template <typename R> class Callback<R()> { // ... }; template <typename R, typename T> class Callback<R()> { // TODO: T cannot be a part of Callback<>'s type }; template <typename R, typename T> Callback<R()> Bind(R (T::*functor)(), T* t) { return Callback<R()>(functor, t); } // Usage: { Callback<void()> cb = Bind(&void_func); cb.Run(); Obj o; Callback<void()> cb3 = Bind(&Obj::void_func, &o); cb3.Run(); }
We have problem. Because Binding void void_func() and Obj::void_func with Obj*
have same type Callback<void()>, there is no way to put T in Callback's
type. So, we need to find a way to fix this.
In Callback<T>, T means that actual RunType. For example,
Obj::void_func( void (Obj::*)() ) need to be called with its
instance(Obj*). After combining method and its instance, we'll get its
RunType as void().
struct Obj { void void_func() {} }; void Run(void (Obj::*functor)(), Obj* obj) { (obj->*functor)(); // RunType: void() } int main(int, char* []) { void (Obj::*functor)() = &Obj::void_func; Obj* obj = new Obj; Run(functor, obj); return 0; }
In this example, you maybe observe RunType (void()) of this inside of Run. So, we
need to do 2 things
- Add indirection between
BindandCallback<>. Callback<>maybe store instance(Obj*) for method.Let's introduce
RunnableAdapter<>andBindState<>.RunnableAdapter<>- accepting normal function(
void (*)()) or method(void (T::*)()) and provideRunmethod. BindState<>- storing method(
void (Obj::*)()) with its instance(Obj*)
3.2 RunnableAdapter<>, BindState<> and Bind overhaul
template <typename Sig> class RunnableAdapter; template <typename R, typename T> class RunnableAdapter<R (T::*)()> { public: RunnableAdapter(R (T::*functor)()) : functor_(functor) {} R Run(T* t) { return (t->*functor_)(); } private: R (T::*functor_)(); }; template <typename Runnable, typename RunType, typename BoundArgsType> struct BindState; template <typename Runnable, typename RunType, typename P1> struct BindState<Runnable, RunType, void(P1)> { BindState(Runnable runnable, P1 p1) : runnable_(runnable), p1_(p1) {} Runnable runnable_; P1 p1_; }; template <typename R, typename T> Callback<R()> Bind(R (T::*functor)(), T* t) { return Callback<R()>(new BindState<RunnableAdapter<R (T::*)()>, R(), R(T*)>( RunnableAdapter<R (T::*)()>(functor), t)); }
RunnableAdapter<> encapsulates method(possibly normal function later) and
BindState<> accepts RunnableAdapter<> and its instance, finally
Callback<> accepts BindState<>. Now it's time to implement
Callback<R()>.
// template <typename R> // class Callback<R()> { // public: // Callback(R (*functor)()) : functor_(functor) {} // R Run() { return functor_(); } // private: // R (*functor_)(); // }; struct BindStateBase {}; template <typename Runnable, typename RunType, typename P1> struct BindState<Runnable, RunType, void(P1)> : public BindStateBase { using UnboundType = RunType; BindState(Runnable runnable, P1 p1) : runnable_(runnable), p1_(p1) {} Runnable runnable_; P1 p1_; }; template <typename R> class Callback<R()> { public: template <typename BindState> Callback(BindState* bind_state) : bind_state_(bind_state) {} R Run() { // TODO: } private: BindStateBase* bind_state_; }; template <typename R, typename T> Callback<R()> Bind(R (T::*functor)(), T* t) { return Callback<R()>(new BindState<RunnableAdapter<R(T::*)()>, R(), R(T*)>( RunnableAdapter<R (T::*)()>(functor), t)); }
At first, old Callback<R()> commented out. Callback<> now have Function
Template as its constructor with its param type as BindStateBase. This
enables to accept different type of BindState<>.
Let's implement Callback<>::Run() in the next phase.
3.3 Invoker<> (depends on BindState<>) and Invoker<>::Run (static method)
To implement R Run(), it requires another trick. Since we have BindState<>
and RunnableAdapter<>, we can call any functor in Callback<>. But,
Callback<> accept different type derived classes of BindStateBase and it
requires different invoking syntax. In other words, invoking BindState
depends on BindState. So, let's add Invoker<> type in BindState<>.
template <typename Storage, typename R> struct Invoker<Storage, R()> { static R Run(BindStateBase* bind_state) { Storage* storage = static_cast<Storage*>(bind_state); return storage->runnable_.Run(storage->p1_); } }; template <typename Runnable, typename RunType, typename P1> struct BindState<Runnable, RunType, void(P1)> : public BindStateBase { using UnboundType = RunType; using InvokerType = Invoker<BindState<Runnable, RunType, void(P1)>, RunType>; BindState(Runnable runnable, P1 p1) : runnable_(runnable), p1_(p1) {} Runnable runnable_; P1 p1_; };
Invoker<> has static method named Run() and it depends on
BindState<>. BindState<>::InvokerType will be used to run in
Callback<>::Run().
3.4 Callback<> constructor Functor Template
template <typename R> class Callback<R()> { public: template <typename BindState> Callback(BindState* bind_state) : bind_state_(bind_state) { polymorphic_invoke_ = &BindState::InvokerType::Run; } R Run() { return polymorphic_invoke_(bind_state_); } private: using PolymorphicInvoke = R(*)(BindStateBase*); PolymorphicInvoke polymorphic_invoke_; BindStateBase* bind_state_; };
Finally we've implemented Callback<>::Run(). Please note that
polymorphic_invoke_ depends on BindState<> and know how to run functor
from BindState<>.
One trivial thing in here is that call function that returns void with return statement. For example, this is valid though it looks weird.
void return_void() {} void func_void() { return return_void(); }
Let's test whether Obj::void_func() called.
#include <cassert> struct Obj { Obj() : called(false) {} ~Obj() { assert(called); } void void_func() { called = true; } bool called; }; int main(int, char* []) { Obj o; Callback<void()> cb3 = Bind(&Obj::void_func, &o); cb3.Run(); }
During stack unwind at the end of main(), Obj::Obj() checks ~Obj::called is true. No
assertion found.
3.5 Whole source code
template <typename Sig> class Callback; template <typename R, typename P> class Callback<R(P)> { public: Callback(R (*functor)(P)) : functor_(functor) {} R Run(P p) { return functor_(p); } private: R (*functor_)(P); }; // template <typename R> // class Callback<R()> { // public: // Callback(R (*functor)()) : functor_(functor) {} // R Run() { return functor_(); } // private: // R (*functor_)(); // }; template <typename R, typename P> Callback<R(P)> Bind(R (*functor)(P)) { return Callback<R(P)>(functor); } template <typename R> Callback<R()> Bind(R (*functor)()) { return Callback<R()>(functor); } template <typename Sig> class RunnableAdapter; template <typename R, typename T> class RunnableAdapter<R (T::*)()> { public: RunnableAdapter(R (T::*functor)()) : functor_(functor) {} R Run(T* t) { return (t->*functor_)(); } private: R (T::*functor_)(); }; template <typename Runnable, typename RunType, typename BoundArgsType> struct BindState; struct BindStateBase {}; template <typename Storage, typename RunType> struct Invoker; template <typename Storage, typename R> struct Invoker<Storage, R()> { static R Run(BindStateBase* bind_state) { Storage* storage = static_cast<Storage*>(bind_state); return storage->runnable_.Run(storage->p1_); } }; template <typename Runnable, typename RunType, typename P1> struct BindState<Runnable, RunType, void(P1)> : public BindStateBase { using UnboundType = RunType; using InvokerType = Invoker<BindState<Runnable, RunType, void(P1)>, RunType>; BindState(Runnable runnable, P1 p1) : runnable_(runnable), p1_(p1) {} Runnable runnable_; P1 p1_; }; template <typename R> class Callback<R()> { public: template <typename BindState> Callback(BindState* bind_state) : bind_state_(bind_state) { polymorphic_invoke_ = &BindState::InvokerType::Run; } R Run() { return polymorphic_invoke_(bind_state_); } private: using PolymorphicInvoke = R (*)(BindStateBase*); PolymorphicInvoke polymorphic_invoke_; BindStateBase* bind_state_; }; template <typename R, typename T> Callback<R()> Bind(R (T::*functor)(), T* t) { return Callback< typename BindState<RunnableAdapter<R (T::*)()>, R(), R(T*)>::UnboundType>( new BindState<RunnableAdapter<R (T::*)()>, R(), R(T*)>( RunnableAdapter<R (T::*)()>(functor), t)); } void void_func() { } void int_func(int) { } #include <cassert> struct Obj { Obj() : called(false) {} ~Obj() { assert(called); } void void_func() { called = true; } bool called; }; int main(int, char* []) { // Callback<void()> cb = Bind(&void_func); // cb.Run(); // Callback<void(int)> cb2 = Bind(&int_func); // cb2.Run(1); Obj o; Callback<void()> cb3 = Bind(&Obj::void_func, &o); cb3.Run(); return 0; }
4 Mission 4: Bring back void(*)()
It was commented out while completing mission 3. So, it's time to bring it back.
Add RunnableAdapter<R(*)()>, Invoker<>, BindState<,,void()> and
Bind(R(*)()) to support void(*)(). But, we have problem in Invoker<>,
they have same signature, so we have 2 options to fix this.
template <typename Storage, typename R> struct Invoker<Storage, R()> { // ... }
- Replace
typename StoragewithBindState<,,>,BindState<>for each type are definitely different, so it fix this problem, but very long verbose typename is not readable. - Introduce integer type representing number of bound types. I think this is also good to implement currying(See callback.h) in the future.
+template <typename R> +class RunnableAdapter<R(*)()> { + public: + RunnableAdapter(R(*functor)()) : functor_(functor){} + + R Run() { + return (*functor_)(); + } + + private: + R (*functor_)(); +}; -template <typename Storage, typename RunType> +template <int NumBound, typename Storage, typename RunType> struct Invoker; template <typename Storage, typename R> -struct Invoker<Storage, R()> { +struct Invoker<1, Storage, R()> { static R Run(BindStateBase* bind_state) { Storage* storage = static_cast<Storage*>(bind_state); return storage->runnable_.Run(storage->p1_); } }; +template <typename Storage, typename R> +struct Invoker<0, Storage, R()> { + static R Run(BindStateBase* bind_state) { + Storage* storage = static_cast<Storage*>(bind_state); + return storage->runnable_.Run(); + } +}; template <typename Runnable, typename RunType, typename P1> struct BindState<Runnable, RunType, void(P1)> : public BindStateBase { using UnboundType = RunType; - using InvokerType = Invoker<BindState<Runnable, RunType, void(P1)>, RunType>; + using InvokerType = Invoker<1, BindState<Runnable, RunType, void(P1)>, RunType>; BindState(Runnable runnable, P1 p1) : runnable_(runnable), p1_(p1) {} Runnable runnable_; P1 p1_; }; +template <typename Runnable, typename RunType> +struct BindState<Runnable, RunType, void()> : public BindStateBase { + using UnboundType = RunType; + using InvokerType = Invoker<0, BindState<Runnable, RunType, void()>, RunType>; + BindState(Runnable runnable) : runnable_(runnable) {} + + Runnable runnable_; +}; +template <typename R> +Callback<R()> Bind(R (*functor)()) { + return Callback< + typename BindState<RunnableAdapter<R (*)()>, R(), R()>::UnboundType>( + new BindState<RunnableAdapter<R (*)()>, R(), R()>( + RunnableAdapter<R (*)()>(functor))); +} + int main(int, char* []) { - // Callback<void()> cb = Bind(&void_func); - // cb.Run(); + Callback<void()> cb = Bind(&void_func); + cb.Run();
This is very easy and straightforward.
5 Mission 5: Bring back void(*)(int)
There is nothing special but just fill the blank.
template <typename Sig> class Callback; -template <typename R, typename P> -class Callback<R(P)> { - public: - Callback(R (*functor)(P)) : functor_(functor) {} - R Run(P p) { return functor_(p); } - - private: - R (*functor_)(P); -}; - -template <typename R, typename P> -Callback<R(P)> Bind(R (*functor)(P)) { - return Callback<R(P)>(functor); -} - template <typename Sig> class RunnableAdapter; @@ -33,6 +18,19 @@ class RunnableAdapter<R(*)()> { R (*functor_)(); }; +template <typename R, typename P> +class RunnableAdapter<R(*)(P)> { + public: + RunnableAdapter(R(*functor)(P)) : functor_(functor){} + + R Run(P p) { + return (*functor_)(p); + } + + private: + R (*functor_)(P); +}; + template <typename R, typename T> class RunnableAdapter<R(T::*)()> { public: @@ -70,6 +68,14 @@ struct Invoker<0, Storage, R()> { } }; +template <typename Storage, typename R, typename P> +struct Invoker<0, Storage, R(P)> { + static R Run(BindStateBase* bind_state, P p) { + Storage* storage = static_cast<Storage*>(bind_state); + return storage->runnable_.Run(p); + } +}; + template <typename Runnable, typename RunType, typename P1> struct BindState<Runnable, RunType, void(P1)> : public BindStateBase { using UnboundType = RunType; @@ -108,6 +114,25 @@ class Callback<R()> { BindStateBase* bind_state_; }; +template <typename R, typename P> +class Callback<R(P)> { + public: + template <typename BindState> + Callback(BindState* bind_state) : bind_state_(bind_state) { + polymorphic_invoke_ = &BindState::InvokerType::Run; + } + + R Run(P p) { + return polymorphic_invoke_(bind_state_, p); + } + + private: + using PolymorphicInvoke = R(*)(BindStateBase*, P); + + PolymorphicInvoke polymorphic_invoke_; + BindStateBase* bind_state_; +}; + template <typename R> Callback<R()> Bind(R (*functor)()) { return Callback< @@ -116,6 +141,14 @@ Callback<R()> Bind(R (*functor)()) { RunnableAdapter<R (*)()>(functor))); } +template <typename R, typename P> +Callback<R(P)> Bind(R (*functor)(P)) { + return Callback< + typename BindState<RunnableAdapter<R (*)(P)>, R(P), R()>::UnboundType>( + new BindState<RunnableAdapter<R (*)(P)>, R(P), R()>( + RunnableAdapter<R (*)(P)>(functor))); +} + template <typename R, typename T> Callback<R()> Bind(R (T::*functor)(), T* t) { return Callback< @@ -142,8 +175,8 @@ int main(int, char* []) { Callback<void()> cb = Bind(&void_func); cb.Run(); - // Callback<void(int)> cb2 = Bind(&int_func); - // cb2.Run(1); + Callback<void(int)> cb2 = Bind(&int_func); + cb2.Run(1); Obj o; Callback<void()> cb3 = Bind(&Obj::void_func, &o);
6 Mission 6: Support member method Bind(&Foo::int_func, &foo)
I know you feel tired, but I have to support member method accepting int.
struct Foo { void int_func(int) {} }; Foo foo; Callback<void(int)> cb4 = Bind(&Foo::int_func, &foo); cb4.Run(1);
As you may expect, it's very easy.
+template <typename R, typename T, typename P> +class RunnableAdapter<R(T::*)(P)> { + public: + RunnableAdapter(R(T::*functor)(P)) : functor_(functor){} + + R Run(T* t, P p) { + return (t->*functor_)(p); + } + + private: + R (T::*functor_)(P); +}; + template <typename Runnable, typename RunType, typename BoundArgsType> struct BindState; @@ -60,6 +73,14 @@ struct Invoker<1, Storage, R()> { } }; +template <typename Storage, typename R, typename P2> +struct Invoker<1, Storage, R(P2)> { + static R Run(BindStateBase* bind_state, P2 p2) { + Storage* storage = static_cast<Storage*>(bind_state); + return storage->runnable_.Run(storage->p1_, p2); + } +}; + template <typename Storage, typename R> struct Invoker<0, Storage, R()> { static R Run(BindStateBase* bind_state) { @@ -157,6 +178,14 @@ Callback<R()> Bind(R (T::*functor)(), T* t) { RunnableAdapter<R (T::*)()>(functor), t)); } +template <typename R, typename T, typename P> +Callback<R(P)> Bind(R (T::*functor)(P), T* t) { + return Callback< + typename BindState<RunnableAdapter<R (T::*)(P)>, R(P), R(T*)>::UnboundType>( + new BindState<RunnableAdapter<R (T::*)(P)>, R(P), R(T*)>( + RunnableAdapter<R (T::*)(P)>(functor), t)); +} + bool called; void void_func() { @@ -175,6 +204,13 @@ struct Obj { bool called; }; +struct Foo { + Foo() : called(false) {} + ~Foo() { assert(called); } + void int_func(int) { called = true; } + bool called; +}; + int main(int, char* []) { called = false; Callback<void()> cb = Bind(&void_func); @@ -190,5 +226,8 @@ int main(int, char* []) { Callback<void()> cb3 = Bind(&Obj::void_func, &o); cb3.Run(); + Foo foo; + Callback<void(int)> cb4 = Bind(&Foo::int_func, &foo); + cb4.Run(1); return 0; }