Skip to content

Instantly share code, notes, and snippets.

@omegacoleman
Last active January 18, 2025 14:24
Show Gist options
  • Save omegacoleman/5db7ad7c810a177d0db1f80abefe94de to your computer and use it in GitHub Desktop.
Save omegacoleman/5db7ad7c810a177d0db1f80abefe94de to your computer and use it in GitHub Desktop.
bounded callbacks -- a heap-alloc free, 16 byte impl
#include <type_traits>
#include <utility>
#include <cstdint>
namespace ark {
template <class>
class callback;
template <class R, class... Args>
class callback<R(Args...)> {
public:
using thunk_ptr_t = std::add_pointer_t<R(uintptr_t, Args...)>;
callback() {}
callback(thunk_ptr_t thunk_ptr, uintptr_t user_data)
: thunk_ptr_(thunk_ptr), user_data_(user_data) {}
operator bool() const noexcept {
return thunk_ptr_ != nullptr;
}
R operator()(Args&&... args) const {
return (*thunk_ptr_)(user_data_, std::forward<Args>(args)...);
}
private:
thunk_ptr_t thunk_ptr_ = nullptr;
uintptr_t user_data_ = 0;
};
namespace detail {
template <class FnPtrClass, FnPtrClass Fn>
struct make_callback_impl0;
template <class R, class... Args, R(*Fn)(Args...)>
struct make_callback_impl0<R(*)(Args...), Fn> {
callback<R(Args...)> operator()() const noexcept {
return callback<R(Args...)>{
+[](uintptr_t obj_addr, Args... args) -> R {
return (*Fn)(std::move(args)...);
},
0
};
}
};
template <class FnPtrClass, FnPtrClass Fn>
struct make_callback_impl1;
template <class R, class T, class... Args, R(*Fn)(T*, Args...)>
struct make_callback_impl1<R(*)(T*, Args...), Fn> {
callback<R(Args...)> operator()(T* obj) const noexcept {
return callback<R(Args...)>{
[](uintptr_t obj_addr, Args... args) -> R {
return (*Fn)(reinterpret_cast<T*>(obj_addr), std::move(args)...);
},
reinterpret_cast<uintptr_t>(obj)
};
}
};
template <class FnPtrClass, FnPtrClass Fn>
struct make_callback_impl1;
template <class R, class T, class... Args, R(T::*Fn)(Args...)>
struct make_callback_impl1<R(T::*)(Args...), Fn> {
callback<R(Args...)> operator()(T* obj) const noexcept {
return callback<R(Args...)>{
[](uintptr_t obj_addr, Args... args) -> R {
return (reinterpret_cast<T*>(obj_addr)->*Fn)(std::move(args)...);
},
reinterpret_cast<uintptr_t>(obj)
};
}
};
}
template <auto Fn> auto make_callback() {
detail::make_callback_impl0<decltype(Fn), Fn> impl;
return impl();
}
template <auto Fn, class T> auto make_callback(T* obj) {
detail::make_callback_impl1<decltype(Fn), Fn> impl;
return impl(obj);
}
}
#include <iostream>
#include "ark_callback.hpp"
void print_int0(int a) {
std::cout << a << std::endl;
}
struct store_int{
int u;
void print_int1() {
std::cout << u << std::endl;
}
};
void print_int2(store_int* s) {
std::cout << s->u << std::endl;
}
int main(void) {
auto cb0 = ark::make_callback<&print_int0>();
cb0(42);
store_int si; si.u = 42;
auto cb1 = ark::make_callback<&store_int::print_int1>(&si);
cb1();
auto cb2 = ark::make_callback<&print_int2>(&si);
cb2();
return 0;
}
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment