Rename movable_function -> function & better SSO

This commit is contained in:
Nikita Lisitsa 2020-11-22 12:06:55 +03:00
parent 8e3a012fd6
commit a1cf646633
5 changed files with 169 additions and 94 deletions

View file

@ -1,6 +1,6 @@
#pragma once #pragma once
#include <psemek/util/movable_function.hpp> #include <psemek/util/function.hpp>
#include <future> #include <future>
#include <chrono> #include <chrono>
@ -135,7 +135,7 @@ namespace psemek::async
struct executor struct executor
{ {
using task = util::movable_function<void()>; using task = util::function<void()>;
using clock = std::chrono::high_resolution_clock; using clock = std::chrono::high_resolution_clock;
// Post the task for execution. Where and when will // Post the task for execution. Where and when will

View file

@ -1,10 +1,8 @@
#pragma once #pragma once
#include <psemek/async/executor.hpp> #include <psemek/async/executor.hpp>
#include <psemek/util/thread.hpp> #include <psemek/util/thread.hpp>
#include <psemek/util/synchronyzed_queue.hpp> #include <psemek/util/synchronyzed_queue.hpp>
#include <psemek/util/movable_function.hpp>
#include <future> #include <future>
#include <vector> #include <vector>

View file

@ -0,0 +1,166 @@
#pragma once
#include <type_traits>
#include <memory>
namespace psemek::util
{
namespace detail
{
// Implemented in cpp to prevent dependency on <functional>
[[noreturn]] void bad_function_call();
}
template <typename Signature>
struct function;
template <typename R, typename ... Args>
struct function<R(Args...)>
{
using signature = R(Args...);
function() noexcept = default;
template <typename F>
function(F && f);
function (function &&) noexcept;
function & operator = (function &&) noexcept;
function (function const &) = delete;
function & operator = (function const &) = delete;
template <typename F>
function & operator = (F && f);
explicit operator bool() const
{
return static_cast<bool>(vtable_);
}
template <typename ... Args1>
R operator()(Args1 && ... args) const;
void reset();
private:
static constexpr std::size_t storage_size = sizeof(void*) * 3;
static constexpr std::size_t storage_align = alignof(void*);
std::aligned_storage_t<storage_size, storage_align> storage_;
struct vtable
{
using move_func = void(*)(void *, void *);
using destroy_func = void(*)(void *);
using call_func = R(*)(void *, Args ...);
move_func move;
destroy_func destroy;
call_func call;
};
vtable * vtable_ = nullptr;
template <typename F>
void assign(F && f);
};
template <typename R, typename ... Args>
template <typename F>
function<R(Args...)>::function(F && f)
{
assign(std::forward<F>(f));
}
template <typename R, typename ... Args>
template <typename F>
function<R(Args...)> & function<R(Args...)>::operator = (F && f)
{
reset();
assign(std::forward<F>(f));
return *this;
}
template <typename R, typename ... Args>
function<R(Args...)>::function (function && other) noexcept
{
vtable_ = other.vtable_;
vtable_->move(&other.storage_, &storage_);
other.reset();
}
template <typename R, typename ... Args>
function<R(Args...)> & function<R(Args...)>::operator = (function && other) noexcept
{
if (this == &other)
return *this;
vtable_ = other.vtable_;
vtable_->move(&other.storage_, &storage_);
other.reset();
return *this;
}
template <typename R, typename ... Args>
template <typename ... Args1>
R function<R(Args...)>::operator()(Args1 && ... args) const
{
if (!vtable_)
detail::bad_function_call();
return vtable_->call(const_cast<void *>(static_cast<void const *>(&storage_)), std::forward<Args1>(args)...);
}
template <typename R, typename ... Args>
void function<R(Args...)>::reset()
{
if (!vtable_) return;
vtable_->destroy(&storage_);
vtable_ = nullptr;
}
template <typename R, typename ... Args>
template <typename F>
void function<R(Args...)>::assign(F && f)
{
using T = std::decay_t<F>;
constexpr bool static_storage = true
&& sizeof(T) <= storage_size
&& alignof(T) <= storage_align
&& ((storage_align % alignof(T)) == 0)
&& std::is_nothrow_move_constructible_v<T>
;
if constexpr (static_storage)
{
new (reinterpret_cast<T *>(&storage_)) T(std::move(f));
static vtable m = {
[](void * src, void * dst){ new (reinterpret_cast<T*>(dst)) T(std::move(*reinterpret_cast<T*>(src))); },
[](void * src){ reinterpret_cast<T*>(src)->~T(); },
[](void * src, Args ... args) -> R { return (*reinterpret_cast<T*>(src))(static_cast<Args&&>(args)...); }
};
vtable_ = &m;
}
else
{
*reinterpret_cast<T**>(&storage_) = new T(std::move(f));
static vtable m = {
[](void * src, void * dst){ *reinterpret_cast<T**>(dst) = *reinterpret_cast<T**>(src); *reinterpret_cast<T**>(src) = nullptr; },
[](void * src){ delete *reinterpret_cast<T**>(src); },
[](void * src, Args ... args) -> R { return (**reinterpret_cast<T**>(src))(static_cast<Args&&>(args)...); }
};
vtable_ = &m;
}
}
}

View file

@ -1,89 +0,0 @@
#pragma once
#include <psemek/util/polymorphic_storage.hpp>
#include <memory>
namespace psemek::util
{
namespace detail
{
template <typename Signature>
struct movable_function_node_base;
template <typename R, typename ... Args>
struct movable_function_node_base<R(Args...)>
{
virtual R call(Args ...) = 0;
virtual ~movable_function_node_base() = default;
};
template <typename Signature, typename F>
struct movable_function_node;
template <typename R, typename ... Args, typename F>
struct movable_function_node<R(Args...), F>
: movable_function_node_base<R(Args...)>
{
F f;
movable_function_node(F && f)
: f(std::move(f))
{}
R call(Args ... args) override
{
return f(args...);
}
};
// Implemented in cpp to prevent dependency on <functional>
[[noreturn]] void bad_function_call();
}
template <typename Signature>
struct movable_function;
template <typename R, typename ... Args>
struct movable_function<R(Args...)>
{
using signature = R(Args...);
movable_function() noexcept = default;
template <typename F>
movable_function(F f)
{
storage_.template emplace<detail::movable_function_node<signature, std::decay_t<F>>>(std::move(f));
}
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>(storage_);
}
template <typename ... Args1>
R operator()(Args1 && ... args) const
{
if (!storage_)
detail::bad_function_call();
return storage_.get()->call(std::forward<Args1>(args)...);
}
private:
static constexpr std::size_t small_storage_size = sizeof(void*) * 3;
polymorhpic_storage<detail::movable_function_node_base<signature>, small_storage_size> storage_;
};
}

View file

@ -1,4 +1,4 @@
#include <psemek/util/movable_function.hpp> #include <psemek/util/function.hpp>
#include <functional> #include <functional>