Skip to content

Instantly share code, notes, and snippets.

@BreadFish64
Created March 1, 2025 04:06
Show Gist options
  • Save BreadFish64/7e3a196548935c033a2f3454aacae1ed to your computer and use it in GitHub Desktop.
Save BreadFish64/7e3a196548935c033a2f3454aacae1ed to your computer and use it in GitHub Desktop.
#pragma once
#include <cassert>
#include <concepts>
#include <cstring>
#include <functional>
#include <memory>
#include <new>
#include <type_traits>
#include <utility>
#pragma warning(push)
#pragma warning(disable : 4201)
namespace breaded {
template <typename>
class Function;
template <typename R, typename... Args>
class Function<R(Args...)>;
template <typename R>
concept PointerToMemberReturn = !
std::is_same_v<R, void> && (std::is_reference_v<R> || std::is_copy_assignable_v<R>);
template <typename C>
concept AnyClass = std::is_class_v<C>;
template <typename F, typename R, typename... Args>
concept Functor = std::is_class_v<F> && std::is_copy_constructible_v<F> && std::is_invocable_r<R, F, Args...>::value;
template <typename F, typename R, typename... Args>
concept StatefullFunctor = //
Functor<F, R, Args...> && !
std::is_convertible_v<F, R (*)(Args...)> && !std::is_same_v<std::remove_cvref_t<F>, Function<R(Args...)>>;
template <AnyClass F>
struct FunctorTraits {
static void CopyConstruct(void* dst, const void* src) {
std::construct_at(static_cast<F*>(dst), *static_cast<const F*>(src));
}
static void DestructiveMove(void* dst, void* src) {
auto& srcRef = *static_cast<F*>(src);
std::construct_at(static_cast<F*>(dst), std::move(srcRef));
std::destroy_at(&srcRef);
}
static void Destroy(void* erased) { std::destroy_at(static_cast<F*>(erased)); }
static void* CopyNew(const void* erased) {
const auto& srcRef = *static_cast<const F*>(erased);
return new F{srcRef};
}
static void Delete(void* erased) { delete static_cast<F*>(erased); }
};
class FunctorTraitTable {
private:
void (*moveConstruct_)(void* dst, void* src) = nullptr;
void (*copyConstruct_)(void* dst, const void* src) = nullptr;
void (*destroy_)(void* erased) = nullptr;
void* (*copyNew_)(const void* erased) = nullptr;
void (*delete_)(void* erased) = nullptr;
public:
constexpr FunctorTraitTable() = default;
template <AnyClass F>
static constexpr FunctorTraitTable Make() {
FunctorTraitTable table;
if (!std::is_trivially_copy_constructible_v<F>) {
table.copyConstruct_ = FunctorTraits<F>::CopyConstruct;
}
table.copyNew_ = FunctorTraits<F>::CopyNew;
if (!std::is_trivially_move_constructible_v<F>) {
table.moveConstruct_ = FunctorTraits<F>::DestructiveMove;
}
if (!std::is_trivially_destructible_v<F>) {
table.destroy_ = FunctorTraits<F>::Destroy;
}
table.delete_ = FunctorTraits<F>::Delete;
return table;
}
void copyConstruct(void* dst, const void* src) const {
if (copyConstruct_) [[unlikely]] {
copyConstruct_(dst, src);
}
}
void moveConstruct(void* dst, void* src) const {
if (moveConstruct_) [[unlikely]] {
moveConstruct_(dst, src);
}
}
void destroy(void* erased) const {
if (destroy_) [[unlikely]] {
destroy_(erased);
}
}
void doDelete(void* erased) const { delete_(erased); }
void* copyNew(const void* src) const { return copyNew_(src); }
};
template <AnyClass F>
constexpr FunctorTraitTable kFunctorTraits{FunctorTraitTable::Make<F>()};
template <typename T>
struct MaybeMemberFunctionPointer {
static constexpr bool value = false;
using type = void*;
};
template <typename R, AnyClass T, typename... Args>
struct MaybeMemberFunctionPointer<R(T&, Args...)> {
static constexpr bool value = true;
using type = R (T::*)(Args...);
};
template <typename R, AnyClass T, typename... Args>
struct MaybeMemberFunctionPointer<R(const T&, Args...)> {
static constexpr bool value = true;
using type = R (T::*)(Args...) const;
};
template <typename T>
struct MaybePointerToMember {
static constexpr bool value = false;
using type = void*;
};
template <PointerToMemberReturn R, AnyClass T>
struct MaybePointerToMember<R(T&)> {
static constexpr bool value = true;
using type = R T::*;
};
template <typename>
class Function {};
template <typename R, typename... Args>
class Function<R(Args...)> {
public:
using FunctionPointerT = R (*)(Args... args);
static constexpr bool kCanBeMemberFunctionPointer = MaybeMemberFunctionPointer<R(Args...)>::value;
using MemberFunctionPointerT = MaybeMemberFunctionPointer<R(Args...)>::type;
static constexpr bool kCanBePointerToMember = MaybePointerToMember<R(Args...)>::value;
using PointerToMemberT = MaybePointerToMember<R(Args...)>::type;
using FunctorCallT = R (*)(void* self, Args... args);
static constexpr size_t kSmallFunctorSize = 31;
private:
enum class eFunctorType : uint8_t {
None,
FunctionPointer,
MemberFunctionPointer,
PointerToMember,
SmallFunctor,
LargeFunctor,
};
struct alignas(void*) {
mutable std::byte functorSmall_[kSmallFunctorSize];
eFunctorType functorType_{eFunctorType::None};
};
union {
FunctionPointerT functionPointer_;
MemberFunctionPointerT memberFunctionPointer_;
PointerToMemberT pointerToMember_;
struct {
FunctorCallT functorCall_;
const FunctorTraitTable* functorTraitTable_;
};
} functor_;
template <typename T>
#ifdef _MSC_VER
[[msvc::flatten]]
#else
[[gnu::flatten]]
#endif
static R
CallFunctor(void* functor, Args... args) {
auto& self = *static_cast<T*>(functor);
return self(std::forward<Args>(args)...);
}
static constexpr bool isFunctor(eFunctorType functorType) {
return static_cast<unsigned>(functorType) >= static_cast<unsigned>(eFunctorType::SmallFunctor);
}
void*& functorLarge() { return *reinterpret_cast<void**>(functorSmall_); }
void* functorLarge() const { return *reinterpret_cast<void**>(functorSmall_); }
void copyFrom(FunctionPointerT functionPointer) {
assert(functorType_ == eFunctorType::None);
functor_.functionPointer_ = functionPointer;
functorType_ = eFunctorType::FunctionPointer;
}
void copyFrom(MemberFunctionPointerT memberFunctionPointer)
requires(kCanBeMemberFunctionPointer)
{
assert(functorType_ == eFunctorType::None);
functor_.memberFunctionPointer = memberFunctionPointer;
functorType_ = eFunctorType::MemberFunctionPointer;
}
void copyFrom(PointerToMemberT pointerToMember)
requires(kCanBePointerToMember)
{
assert(functorType_ == eFunctorType::None);
functor_.pointerToMember_ = pointerToMember;
functorType_ = eFunctorType::PointerToMember;
}
template <StatefullFunctor<R, Args...> F>
void copyFrom(const F& functor) {
static_assert(!std::is_same_v<F, Function>);
assert(functorType_ == eFunctorType::None);
functor_.functorTraitTable_ = &kFunctorTraits<F>;
functor_.functorCall_ =
#ifdef _MSC_VER
CallFunctor<F>;
#else
functor_.functorCall_ =
reinterpret_cast<FunctorCallT>(reinterpret_cast<R (*)(F*, Args...)>(&F::operator()));
#endif
if constexpr (sizeof(F) <= kSmallFunctorSize) {
functorType_ = eFunctorType::SmallFunctor;
new (functorSmall_) F{functor};
} else {
functorType_ = eFunctorType::LargeFunctor;
functorLarge() = new F{functor};
}
}
template <StatefullFunctor<R, Args...> F>
void moveFrom(F&& functor) {
static_assert(!std::is_same_v<F, Function>);
assert(functorType_ == eFunctorType::None);
functor_.functorTraitTable_ = &kFunctorTraits<F>;
functor_.functorCall_ = CallFunctor<F>;
if constexpr (sizeof(F) <= kSmallFunctorSize) {
functorType_ = eFunctorType::SmallFunctor;
new (functorSmall_) F{std::move(functor)};
} else {
functorType_ = eFunctorType::LargeFunctor;
functorLarge() = new F{std::move(functor)};
}
}
void copyFrom(const Function& other) {
assert(functorType_ == eFunctorType::None);
std::memcpy(functorSmall_, other.functorSmall_, kSmallFunctorSize);
functor_ = other.functor_;
auto functorType{other.functorType_};
if (isFunctor(functorType)) {
if (functorType == eFunctorType::SmallFunctor) [[likely]] {
functor_.functorTraitTable_->copyConstruct(functorSmall_, other.functorSmall_);
} else {
functorLarge() = functor_.functorTraitTable_->copyNew(other.functorLarge());
}
}
functorType_ = functorType;
}
void moveFrom(Function&& other) {
assert(functorType_ == eFunctorType::None);
std::memcpy(functorSmall_, other.functorSmall_, kSmallFunctorSize);
functor_ = other.functor_;
auto functorType{other.functorType_};
if (functorType == eFunctorType::SmallFunctor) {
functor_.functorTraitTable_->moveConstruct(functorSmall_, other.functorSmall_);
}
other.functorType_ = eFunctorType::None;
functorType_ = functorType;
}
public:
using result_type = R;
Function() = default;
Function(FunctionPointerT functionPointer) { copyFrom(functionPointer); }
Function(std::convertible_to<FunctionPointerT> auto const& functionPointer) {
copyFrom(static_cast<FunctionPointerT>(functionPointer));
}
Function(MemberFunctionPointerT memberFunctionPointer)
requires(kCanBeMemberFunctionPointer)
{
copyFrom(memberFunctionPointer);
}
Function(PointerToMemberT pointerToMember)
requires(kCanBePointerToMember)
{
copyFrom(pointerToMember);
}
template <StatefullFunctor<R, Args...> F>
Function(const F& functor) {
copyFrom(functor);
}
template <StatefullFunctor<R, Args...> F>
Function(F&& functor) {
moveFrom(std::move(functor));
}
Function(const Function& other) { copyFrom(other); }
Function(Function&& other) { moveFrom(std::move(other)); }
Function& operator=(FunctionPointerT functionPointer) {
clear();
copyFrom(static_cast<FunctionPointerT>(functionPointer));
return *this;
}
Function& operator=(std::convertible_to<FunctionPointerT> auto const& functionPointer) {
clear();
copyFrom(static_cast<FunctionPointerT>(functionPointer));
return *this;
}
Function& operator=(MemberFunctionPointerT memberFunctionPointer)
requires(kCanBeMemberFunctionPointer)
{
clear();
copyFrom(memberFunctionPointer);
return *this;
}
Function& operator=(PointerToMemberT pointerToMember)
requires(kCanBePointerToMember)
{
clear();
copyFrom(pointerToMember);
return *this;
}
template <StatefullFunctor<R, Args...> F>
Function& operator=(const F& functor) {
clear();
copyFrom(functor);
return *this;
}
template <StatefullFunctor<R, Args...> F>
Function& operator=(F&& functor) {
clear();
moveFrom(std::move(functor));
return *this;
}
Function& operator=(const Function& other) {
clear();
copyFrom(other);
return *this;
}
Function& operator=(Function&& other) {
clear();
moveFrom(std::move(other));
return *this;
}
~Function() { clear(); }
void clear() {
auto functorType{functorType_};
if (isFunctor(functorType)) {
if (functorType == eFunctorType::SmallFunctor) {
functor_.functorTraitTable_->destroy(functorSmall_);
} else {
functor_.functorTraitTable_->doDelete(functorLarge());
}
}
functorType_ = eFunctorType::None;
}
operator bool() const { return functorType_ != eFunctorType::None; }
friend bool operator==(const Function& f, std::nullptr_t) { return !f; }
friend bool operator==(std::nullptr_t, const Function& f) { return !f; }
R operator()(Args... args) const {
const eFunctorType functorType{functorType_};
if (functorType == eFunctorType::FunctionPointer) [[likely]] {
return functor_.functionPointer_(std::forward<Args>(args)...);
}
if (isFunctor(functorType)) [[likely]] {
return functor_.functorCall_((functorType == eFunctorType::SmallFunctor) ? functorSmall_ : functorLarge(),
std::forward<Args>(args)...);
}
if constexpr (kCanBeMemberFunctionPointer) {
if (functorType == eFunctorType::MemberFunctionPointer) {
return std::invoke(functor_.memberFunctionPointer_, std::forward<Args>(args)...);
}
}
if constexpr (kCanBePointerToMember) {
if (functorType == eFunctorType::PointerToMember) {
return std::invoke(functor_.pointerToMember_, std::forward<Args>(args)...);
}
}
[[unlikely]] throw std::bad_function_call{};
}
};
} // namespace breaded
namespace Common {
template <typename T>
using Function = breaded::Function<T>;
}
#pragma warning(pop)
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment