Skip to content

Instantly share code, notes, and snippets.

@xenobrain
Last active December 22, 2025 14:30
Show Gist options
  • Select an option

  • Save xenobrain/ed90e8a54ad948cebf480fbbc3d0772b to your computer and use it in GitHub Desktop.

Select an option

Save xenobrain/ed90e8a54ad948cebf480fbbc3d0772b to your computer and use it in GitHub Desktop.
delegate
#ifndef DELEGATE_H
#define DELEGATE_H
#if defined(_MSC_VER)
#define DEBUG_BREAK __debugbreak()
#else
#include <csignal>
#ifdef __EMSCRIPTEN__
#include <emscripten.h>
#define DEBUG_BREAK EM_ASM({ debugger; })
#else
#define DEBUG_BREAK raise(SIGTRAP)
#endif
#endif
template<class T> struct IsVoid { static constexpr bool value = false; };
template<> struct IsVoid<void> { static constexpr bool value = true; };
template<> struct IsVoid<void const> { static constexpr bool value = true; };
template<> struct IsVoid<void volatile> { static constexpr bool value = true; };
template<> struct IsVoid<void const volatile> { static constexpr bool value = true; };
template <typename T> class Delegate;
template <typename R, typename... Args> class Delegate<R(Args...)> {
public:
template <R(*function)(Args...)> auto bind() -> Delegate& {
_instance = nullptr;
_proxy = &function_proxy<function>;
return *this;
}
template <class C, R(C::* function)(Args...)> auto bind(C* instance) -> Delegate& {
_instance = instance;
_proxy = &method_proxy<C,function>;
return *this;
}
template <class C, R(C::*function)(Args...) const> auto bind(const C* instance) -> Delegate& {
_instance = const_cast<C*>(instance);
_proxy = &const_method_proxy<C,function>;
return *this;
}
auto invoke(Args... args) const -> R {
if (_proxy) {
if constexpr (IsVoid<R>::value) {
_proxy(_instance, static_cast<Args&&>(args)...);
return;
} else {
return _proxy(_instance, static_cast<Args&&>(args)...);
}
}
DEBUG_BREAK;
if constexpr (IsVoid<R>::value) {
return;
} else {
return R();
}
}
void clear() {
_instance = nullptr;
_proxy = nullptr;
}
explicit operator bool() const { return _proxy != nullptr; }
private:
typedef R(*proxy_function)(void*, Args...);
template <R(*function)(Args...)> auto static function_proxy(void*, Args... args) -> R {
return function(static_cast<Args&&>(args)...);
}
template <class C, R(C::* function)(Args...)> auto static method_proxy(void* instance, Args... args) -> R {
return (static_cast<C*>(instance)->*function)(static_cast<Args&&>(args)...);
}
template <class C, R(C::* function)(Args...) const> auto static const_method_proxy(void* instance, Args... args) -> R {
return (static_cast<const C*>(instance)->*function)(static_cast<Args&&>(args)...);
}
void* _instance{};
proxy_function _proxy{};
};
#endif // DELEGATE_H
// pre-C++11 version
//#include "Delegate.h"
// C++11 version
#include "delegate.h"
#include <windows.h>
void Func0(void)
{
}
int Func1(int a)
{
return a + 10;
}
float Func2(float a, float b)
{
return a + b;
}
struct Test
{
void Func0(void)
{
}
void Func0(void) const
{
}
int Func1(int a)
{
return a + 20;
}
int Func1(int a) const
{
return a + 20;
}
float Func2(float a, float b)
{
return a + b;
}
float Func2(float a, float b) const
{
return a + b;
}
};
int main(void)
{
// delegate with zero arguments
{
typedef delegate<void(void)> TestDelegate;
TestDelegate d;
d.bind<&Func0>();
d.invoke();
}
// delegate with one argument
{
typedef delegate<int(int)> TestDelegate;
TestDelegate d;
d.bind<&Func1>();
d.invoke(10);
Test t;
d.bind<Test, &Test::Func1>(&t);
d.invoke(10);
const Test ct;
d.bind<Test, &Test::Func1>(&ct);
d.invoke(10);
}
// delegate with two arguments
{
typedef delegate<float(float, float)> TestDelegate;
TestDelegate d;
d.bind<&Func2>();
d.invoke(10.0f, 20.0f);
Test t;
d.bind<Test, &Test::Func2>(&t);
d.invoke(10.0f, 20.0f);
const Test ct;
d.bind<Test, &Test::Func2>(&ct);
d.invoke(10.0f, 20.0f);
}
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment