Created
March 1, 2025 04:06
-
-
Save BreadFish64/7e3a196548935c033a2f3454aacae1ed to your computer and use it in GitHub Desktop.
This file contains hidden or 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 <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