basedOpinionated utility library |
git clone git://git.dimitrijedobrota.com/based.git |
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING |
commit | 46ecfeefdd364d5bc73cdcfca5daf588c1d6e654 |
parent | 94b5faad6dfe05fd0766da1f43ce57c0e02529f8 |
author | Dimitrije Dobrota < mail@dimitrijedobrota.com > |
date | Tue, 15 Apr 2025 20:26:18 +0200 |
Playing with type erasure
M | example/template.cpp | | | +++++++++++ |
M | include/based/template.hpp | | | +++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ |
2 files changed, 101 insertions(+), 0 deletions(-)
diff --git a/ example/template.cpp b/ example/template.cpp
@@ -1,3 +1,4 @@
#include <functional>
#include <iostream>
#include "based/template.hpp"
@@ -14,5 +15,15 @@
int main()
double d = 7.3;
l(&d);
{
const based::Function f = [](int a) { return a + 1; };
f(3);
}
{
const std::function f = [](int a) { return a + 1; };
f(3);
}
return 0;
}
diff --git a/ include/based/template.hpp b/ include/based/template.hpp
@@ -1,8 +1,38 @@
#pragma once
#include <array>
#include <cstddef>
#include <functional>
#include <type_traits>
namespace based
{
/* ----- Function Signature ----- */
template<typename>
struct signature;
template<typename Ret, typename... Args>
struct signature<Ret(Args...)>
{
using sig_type = Ret(Args...);
};
template<typename Ret, typename Obj, typename... Args>
struct signature<Ret (Obj::*)(Args...)>
{
using sig_type = Ret(Args...);
};
template<typename Ret, typename Obj, typename... Args>
struct signature<Ret (Obj::*)(Args...) const>
{
using sig_type = Ret(Args...);
};
/* ----- Overload Lambdas ----- */
template<typename... F>
struct overload : public F...
{
@@ -12,4 +42,64 @@
struct overload : public F...
template<typename... F>
overload(F&&...) -> overload<F...>;
/* ----- Function Wrapper with type erasure ----- */
template<typename Signature, std::size_t Size = 16, std::size_t Alignment = 8>
class Function;
template<std::size_t Size,
std::size_t Alignment,
typename Res,
typename... Args>
class Function<Res(Args...), Size, Alignment>
{
alignas(Alignment) char m_space[Size] = {0}; // NOLINT *array
using executor_t = Res (*)(Args..., void*);
static constexpr Res default_executor(Args... /* args */, void* /* func */)
{
throw std::bad_function_call();
}
constexpr static executor_t m_default_executor = default_executor;
executor_t m_executor = m_default_executor;
template<typename Callable>
static Res executor(Args... args, void* func)
{
return (*reinterpret_cast<Callable*>( // NOLINT reinterpret_cast
static_cast<Function*>(func)->m_space))(std::forward<Args>(args)...);
}
public:
Function() = default;
template<typename CallableArg, typename Callable = std::decay_t<CallableArg>>
requires(requires {
!std::same_as<Function, Callable>;
sizeof(Callable) <= Size;
alignof(Callable) <= Alignment;
std::is_trivially_destructible_v<Callable>;
std::is_trivially_copyable_v<Callable>;
})
Function(CallableArg&& callable) // NOLINT *explicit
: m_executor(executor<Callable>)
{
::new (static_cast<void*>(m_space))
Callable(std::forward<CallableArg>(callable));
}
template<typename... CallArgs>
Res operator()(CallArgs&&... callargs) const
{
return this->m_executor(std::forward<CallArgs>(callargs)...,
const_cast<Function*>(this)); // NOLINT *const_cast
}
};
template<typename T>
Function(T) -> Function<typename signature<decltype(&T::operator())>::sig_type>;
} // namespace based