Skip to content

Instantly share code, notes, and snippets.

@StrikerX3
Last active August 13, 2022 00:04
Show Gist options
  • Save StrikerX3/dde0c26a9967c7a24266ea2c922a8d00 to your computer and use it in GitHub Desktop.
Save StrikerX3/dde0c26a9967c7a24266ea2c922a8d00 to your computer and use it in GitHub Desktop.
#pragma once
#include <functional>
#include <type_traits>
#include <utility>
namespace util {
namespace detail {
template <typename T>
constexpr bool alwaysFalse = false;
template <typename TReturn, typename... TArgs>
struct FuncClass {
using ReturnType = TReturn;
using FnType = ReturnType (*)(TArgs... args, void *context);
FuncClass()
: m_context(nullptr)
, m_fn(nullptr) {}
FuncClass(void *context, FnType fn)
: m_context(context)
, m_fn(fn) {}
FuncClass(const FuncClass &) = default;
FuncClass(FuncClass &&) = default;
FuncClass &operator=(const FuncClass &) = default;
FuncClass &operator=(FuncClass &&) = default;
void Rebind(FnType fn) {
Rebind(nullptr, fn);
}
void Rebind(void *context, FnType fn) {
m_context = context;
m_fn = fn;
}
ReturnType operator()(TArgs... args) {
if (m_fn != nullptr) {
return m_fn(std::forward<TArgs>(args)..., m_context);
} else if constexpr (!std::is_void_v<ReturnType>) {
return {};
}
}
private:
void *m_context;
FnType m_fn;
};
template <typename TFunc>
struct FuncImpl {
static_assert(alwaysFalse<TFunc>, "Callback requires a function argument");
};
template <typename TReturn, typename... TArgs>
struct FuncImpl<TReturn(TArgs...)> {
using type = FuncClass<TReturn, TArgs...>;
};
} // namespace detail
template <typename TFunc>
class Callback : public detail::FuncImpl<TFunc>::type {
using FnType = typename detail::FuncImpl<TFunc>::type::FnType;
public:
Callback() = default;
Callback(FnType fn)
: detail::FuncImpl<TFunc>::type(nullptr, fn) {}
Callback(void *context, FnType fn)
: detail::FuncImpl<TFunc>::type(context, fn) {}
Callback(const Callback &) = default;
Callback(Callback &&) = default;
Callback &operator=(const Callback &) = default;
Callback &operator=(Callback &&) = default;
};
// -------------------------------------------------------------------------------------------------
namespace detail {
template <typename>
struct MFPCallbackMaker;
template <typename Return, typename Object, typename... Args>
struct MFPCallbackMaker<Return (Object::*)(Args...)> {
using class_type = Object;
template <Return (Object::*mfp)(Args...)>
static auto GetCallback(Object *context) {
return Callback<Return(Args...)>{context, [](Args... args, void *context) {
auto &obj = *static_cast<Object *>(context);
return (obj.*mfp)(std::forward<Args>(args)...);
}};
}
};
template <typename Return, typename Object, typename... Args>
struct MFPCallbackMaker<Return (Object::*)(Args...) const> {
using class_type = Object;
template <Return (Object::*mfp)(Args...)>
static auto GetCallback(Object *context) {
return Callback<Return(Args...)>{context, [](Args... args, void *context) {
auto &obj = *static_cast<Object *>(context);
return (obj.*mfp)(std::forward<Args>(args)...);
}};
}
};
} // namespace detail
template <auto mfp, typename = std::enable_if_t<std::is_member_function_pointer_v<decltype(mfp)>>>
auto MakeClassMemberCallback(typename detail::MFPCallbackMaker<decltype(mfp)>::class_type *context) {
return detail::MFPCallbackMaker<decltype(mfp)>::template GetCallback<mfp>(context);
}
} // namespace util
#include "callback.hpp"
using MyCallback = util::Callback<int(int x, int y)>;
int freeFunction(int x, int y, void *) {
return x + y;
}
int freeFunctionCtx(int x, int y, void *z) {
return x + y + *(int*)z;
}
int testFreeFunction() {
MyCallback cb{freeFunction};
return cb(4, 5); // returns 9
}
int testFreeFunctionCtx() {
int z = 6;
MyCallback cb{&z, freeFunctionCtx};
return cb(4, 5); // returns 15
}
int testLambda() {
MyCallback cb{[](int x, int y, void *) -> int { return x - y; }};
return cb(4, 5); // returns -1
}
int testLambdaCtx() {
int z = 6;
MyCallback cb{&z, [](int x, int y, void *z) -> int { return x - y - *(int*)z; }};
return cb(4, 5); // returns -7
}
int testMFP() {
struct S {
int mf(int x, int y) {
return x * y;
}
} s;
MyCallback cb = util::MakeClassMemberCallback<&S::mf>(&s);
return cb(4, 5); // returns 20
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment