Add small object optimization to movable_function
This commit is contained in:
parent
0f1479abbd
commit
8e3a012fd6
2 changed files with 170 additions and 10 deletions
|
|
@ -1,5 +1,7 @@
|
|||
#pragma once
|
||||
|
||||
#include <psemek/util/polymorphic_storage.hpp>
|
||||
|
||||
#include <memory>
|
||||
|
||||
namespace psemek::util
|
||||
|
|
@ -51,34 +53,37 @@ namespace psemek::util
|
|||
{
|
||||
using signature = R(Args...);
|
||||
|
||||
movable_function() = default;
|
||||
movable_function() noexcept = default;
|
||||
|
||||
template <typename F>
|
||||
movable_function(F f)
|
||||
: p {std::make_unique<detail::movable_function_node<signature, std::remove_cv_t<F>>>(std::move(f))}
|
||||
{}
|
||||
{
|
||||
storage_.template emplace<detail::movable_function_node<signature, std::decay_t<F>>>(std::move(f));
|
||||
}
|
||||
|
||||
movable_function (movable_function &&) = default;
|
||||
movable_function & operator = (movable_function &&) = default;
|
||||
movable_function (movable_function &&) noexcept = default;
|
||||
movable_function & operator = (movable_function &&) noexcept = default;
|
||||
|
||||
movable_function (movable_function const &) = delete;
|
||||
movable_function & operator = (movable_function const &) = delete;
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return static_cast<bool>(p);
|
||||
return static_cast<bool>(storage_);
|
||||
}
|
||||
|
||||
R operator()(Args ... args) const
|
||||
template <typename ... Args1>
|
||||
R operator()(Args1 && ... args) const
|
||||
{
|
||||
if (!p)
|
||||
if (!storage_)
|
||||
detail::bad_function_call();
|
||||
|
||||
return p->call(args...);
|
||||
return storage_.get()->call(std::forward<Args1>(args)...);
|
||||
}
|
||||
|
||||
private:
|
||||
std::unique_ptr<detail::movable_function_node_base<signature>> p;
|
||||
static constexpr std::size_t small_storage_size = sizeof(void*) * 3;
|
||||
polymorhpic_storage<detail::movable_function_node_base<signature>, small_storage_size> storage_;
|
||||
};
|
||||
|
||||
}
|
||||
|
|
|
|||
155
libs/util/include/psemek/util/polymorphic_storage.hpp
Normal file
155
libs/util/include/psemek/util/polymorphic_storage.hpp
Normal file
|
|
@ -0,0 +1,155 @@
|
|||
#pragma once
|
||||
|
||||
#include <type_traits>
|
||||
#include <algorithm>
|
||||
|
||||
namespace psemek::util
|
||||
{
|
||||
|
||||
template <typename I, std::size_t Size = sizeof(I)>
|
||||
struct polymorhpic_storage
|
||||
{
|
||||
static constexpr std::size_t align = std::max(alignof(I*), alignof(I));
|
||||
static constexpr std::size_t size = std::max(sizeof(I*), std::max(sizeof(I), Size));
|
||||
|
||||
polymorhpic_storage() noexcept = default;
|
||||
|
||||
polymorhpic_storage(polymorhpic_storage && other) noexcept;
|
||||
polymorhpic_storage & operator = (polymorhpic_storage && other) noexcept;
|
||||
|
||||
polymorhpic_storage(polymorhpic_storage const &) = delete;
|
||||
polymorhpic_storage & operator = (polymorhpic_storage const &) = delete;
|
||||
|
||||
template <typename T, typename ... Args>
|
||||
void emplace(Args && ... args);
|
||||
|
||||
I * get() const noexcept;
|
||||
|
||||
void reset() noexcept;
|
||||
|
||||
~polymorhpic_storage() noexcept
|
||||
{
|
||||
reset();
|
||||
}
|
||||
|
||||
explicit operator bool() const
|
||||
{
|
||||
return static_cast<bool>(manager_);
|
||||
}
|
||||
|
||||
private:
|
||||
std::aligned_storage_t<size, align> storage_;
|
||||
|
||||
using manager = void*(*)(void *, void *, int);
|
||||
manager manager_ = nullptr;
|
||||
|
||||
static constexpr int op_get = 1;
|
||||
static constexpr int op_move = 2;
|
||||
static constexpr int op_destroy = 3;
|
||||
};
|
||||
|
||||
template <typename I, std::size_t Size>
|
||||
polymorhpic_storage<I, Size>::polymorhpic_storage(polymorhpic_storage && other) noexcept
|
||||
{
|
||||
manager_ = other.manager_;
|
||||
if (manager_)
|
||||
{
|
||||
manager_(&other.storage_, &storage_, op_move);
|
||||
}
|
||||
|
||||
other.reset();
|
||||
}
|
||||
|
||||
template <typename I, std::size_t Size>
|
||||
polymorhpic_storage<I, Size> & polymorhpic_storage<I, Size>::operator = (polymorhpic_storage && other) noexcept
|
||||
{
|
||||
if (this == &other)
|
||||
return *this;
|
||||
|
||||
reset();
|
||||
manager_ = other.manager_;
|
||||
if (manager_)
|
||||
{
|
||||
manager_(&other.storage_, &storage_, op_move);
|
||||
}
|
||||
|
||||
other.reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename I, std::size_t Size>
|
||||
template <typename T, typename ... Args>
|
||||
void polymorhpic_storage<I, Size>::emplace(Args && ... args)
|
||||
{
|
||||
constexpr bool static_storage = true
|
||||
&& (sizeof(T) <= size)
|
||||
&& (alignof(T) <= align)
|
||||
&& ((align % alignof(T)) == 0)
|
||||
&& std::is_nothrow_move_constructible_v<T>
|
||||
;
|
||||
|
||||
reset();
|
||||
if constexpr (static_storage)
|
||||
{
|
||||
static_assert(sizeof(T) <= size);
|
||||
new (reinterpret_cast<T *>(&storage_)) T(std::forward<Args>(args)...);
|
||||
manager_ = [](void * src, void * dst, int op) -> void*
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case op_get:
|
||||
return src;
|
||||
case op_move:
|
||||
new (reinterpret_cast<T *>(dst)) T(std::move(*reinterpret_cast<T *>(src)));
|
||||
break;
|
||||
case op_destroy:
|
||||
reinterpret_cast<T *>(src)->~T();
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
};
|
||||
}
|
||||
else
|
||||
{
|
||||
*reinterpret_cast<T**>(&storage_) = new T(std::forward<Args>(args)...);
|
||||
manager_ = [](void * src, void * dst, int op) -> void*
|
||||
{
|
||||
switch (op)
|
||||
{
|
||||
case op_get:
|
||||
return *reinterpret_cast<T**>(src);
|
||||
case op_move:
|
||||
*reinterpret_cast<T**>(dst) = *reinterpret_cast<T**>(src);
|
||||
*reinterpret_cast<T**>(src) = nullptr;
|
||||
break;
|
||||
case op_destroy:
|
||||
delete *reinterpret_cast<T**>(src);
|
||||
break;
|
||||
default:
|
||||
break;
|
||||
}
|
||||
|
||||
return nullptr;
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
template <typename I, std::size_t Size>
|
||||
I * polymorhpic_storage<I, Size>::get() const noexcept
|
||||
{
|
||||
if (!manager_) return nullptr;
|
||||
return static_cast<I *>(manager_(const_cast<void *>(static_cast<void const *>(&storage_)), nullptr, op_get));
|
||||
}
|
||||
|
||||
template <typename I, std::size_t Size>
|
||||
void polymorhpic_storage<I, Size>::reset() noexcept
|
||||
{
|
||||
if (!manager_) return;
|
||||
manager_(&storage_, nullptr, op_destroy);
|
||||
manager_ = nullptr;
|
||||
}
|
||||
|
||||
}
|
||||
Loading…
Add table
Reference in a new issue