Last active
August 13, 2022 00:04
-
-
Save StrikerX3/dde0c26a9967c7a24266ea2c922a8d00 to your computer and use it in GitHub Desktop.
This file contains 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
#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 |
This file contains 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 "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