basedOpinionated 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
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>