based

Opinionated 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

Diffstat:
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