based

Opinionated utility library
git clone git://git.dimitrijedobrota.com/based.git
Log | Files | Refs | README | LICENSE | HACKING | CONTRIBUTING | CODE_OF_CONDUCT | BUILDING

commit 6e46d26b128258ef84271466f4631d04ae678d75
parent b2a856714ad1dec4785d944da60114131f4c7169
author Dimitrije Dobrota < mail@dimitrijedobrota.com >
date Sun, 27 Apr 2025 20:51:08 +0200

Buffer used for Local Buffer Optimization

Diffstat:
M example/template.cpp | ++ --
M include/based/template.hpp | ++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++ ------------

2 files changed, 81 insertions(+), 14 deletions(-)


diff --git a/ example/template.cpp b/ example/template.cpp

@@ -17,12 +17,12 @@ int main()


{
const based::Function f = [](int a) { return a + 1; };
f(3);
std::cout << f(3) << '\n';
}

{
const std::function f = [](int a) { return a + 1; };
f(3);
std::cout << f(3) << '\n';
}

return 0;

diff --git a/ include/based/template.hpp b/ include/based/template.hpp

@@ -1,8 +1,11 @@

#pragma once

#include <concepts>
#include <cstddef>
#include <cstring>
#include <functional>
#include <type_traits>
#include <utility>

namespace based
{

@@ -212,6 +215,69 @@ struct signature<Ret (Obj::*)(Args...) const volatile && noexcept(Ne)>

using noexcept_val = std::integral_constant<bool, Ne>;
};

/* ----- Buffer used for Local Buffer Optimization ----- */

template<size_t S, size_t A = alignof(void*)>
struct Buffer
{
static constexpr auto size = S;
static constexpr auto alignment = A;

template<typename T>
static constexpr bool valid_type()
{
return sizeof(T) <= S && (A % sizeof(T)) == 0;
}

alignas(alignment) char m_space[size] = {0}; // NOLINT array

Buffer() = default;

template<typename T, typename... Args>
requires(valid_type<T>() && std::constructible_from<T, Args...>)
explicit Buffer(std::in_place_type_t<T> /* t */, Args&&... args) noexcept(
std::is_nothrow_constructible_v<T, Args...>)
{
static_assert(std::is_trivially_destructible_v<T>);
static_assert(std::is_trivially_copyable_v<T>);
::new (static_cast<void*>(as<T>())) T(std::forward<Args>(args)...);
}

template<typename T, typename... Args>
requires(valid_type<T>() && std::constructible_from<T, Args...>)
T* emplace(Args&&... args) noexcept(
std::is_nothrow_constructible_v<T, Args...>)
{
static_assert(std::is_trivially_destructible_v<T>);
static_assert(std::is_trivially_copyable_v<T>);

// NOLINTNEXTLINE owning-memory
return ::new (static_cast<void*>(as<T>())) T(std::forward<Args>(args)...);
}

template<typename T>
requires(valid_type<T>())
T* as() noexcept
{
return reinterpret_cast<T*>(&m_space); // NOLINT reinterpret_cast
}

template<typename T>
requires(valid_type<T>())
const T* as() const noexcept
{
return const_cast<Buffer*>(this)->as<T>(); // NOLINT const_cast
}

void swap(Buffer& that) noexcept
{
alignas(alignment) char tmp[size]; // NOLINT array
::memcpy(tmp, this->m_space, size);
::memcpy(this->m_space, that.m_space, size);
::memcpy(that.m_space, tmp, size);
}
};

/* ----- Overload Lambdas ----- */

template<typename... F>

@@ -234,7 +300,7 @@ template<std::size_t Size,

typename... Args>
class Function<Res(Args...), Size, Alignment>
{
alignas(Alignment) char m_space[Size] = {0}; // NOLINT *array
Buffer<Size, Alignment> m_space;

using executor_t = Res (*)(Args..., void*);

@@ -249,8 +315,9 @@ class Function<Res(Args...), Size, Alignment>

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)...);
return std::invoke(
*static_cast<Function*>(func)->m_space.template as<Callable>(),
std::forward<Args>(args)...);
}

public:

@@ -258,18 +325,18 @@ public:


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>;
})
!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>)
: m_space(std::in_place_type<Callable>,
std::forward<CallableArg>(callable))
, m_executor(executor<Callable>)
{
::new (static_cast<void*>(m_space))
Callable(std::forward<CallableArg>(callable));
}

template<typename... CallArgs>