From 65b30fdbc24d31fafae6097f8503917bbb2e5372 Mon Sep 17 00:00:00 2001 From: BlackMATov Date: Sun, 6 Feb 2022 11:59:51 +0700 Subject: [PATCH] fixed_function instead std::function --- headers/meta.hpp/meta_base.hpp | 1 + headers/meta.hpp/meta_base/fixed_function.hpp | 203 ++++++++++++++++++ headers/meta.hpp/meta_states.hpp | 80 +++---- headers/meta.hpp/meta_value.hpp | 9 +- headers/meta.hpp/meta_value/value.hpp | 37 ++-- untests/meta_base/fixed_function_tests.cpp | 122 +++++++++++ 6 files changed, 394 insertions(+), 58 deletions(-) create mode 100644 headers/meta.hpp/meta_base/fixed_function.hpp create mode 100644 untests/meta_base/fixed_function_tests.cpp diff --git a/headers/meta.hpp/meta_base.hpp b/headers/meta.hpp/meta_base.hpp index 24c5330..cec6fac 100644 --- a/headers/meta.hpp/meta_base.hpp +++ b/headers/meta.hpp/meta_base.hpp @@ -35,6 +35,7 @@ #include "meta_base/cvref_traits.hpp" #include "meta_base/enum_bitflags.hpp" #include "meta_base/enum.hpp" +#include "meta_base/fixed_function.hpp" #include "meta_base/noncopyable.hpp" #include "meta_base/overloaded.hpp" #include "meta_base/select_overload.hpp" diff --git a/headers/meta.hpp/meta_base/fixed_function.hpp b/headers/meta.hpp/meta_base/fixed_function.hpp new file mode 100644 index 0000000..8bac775 --- /dev/null +++ b/headers/meta.hpp/meta_base/fixed_function.hpp @@ -0,0 +1,203 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/meta.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2021, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#pragma once + +#include +#include +#include +#include + +namespace meta_hpp::detail +{ + template < typename Function, std::size_t MaxFunctorSize = sizeof(void*) * 3 > + class fixed_function; + + template < typename R, typename... Args, std::size_t MaxFunctorSize > + class fixed_function final { + public: + fixed_function() = default; + ~fixed_function() { reset(); } + + fixed_function(const fixed_function& other) = delete; + fixed_function& operator=(const fixed_function& other) = delete; + + fixed_function(fixed_function&& other) noexcept { + if ( other.vtable_ ) { + other.vtable_->move(other, *this); + } + } + + fixed_function& operator=(fixed_function&& other) noexcept { + if ( this != &other ) { + fixed_function{std::move(other)}.swap(*this); + } + return *this; + } + + template < typename Functor > + fixed_function(Functor&& functor) { + vtable_t::construct(*this, std::forward(functor)); + } + + template < typename Functor > + fixed_function& operator=(Functor&& functor) { + fixed_function{std::forward(functor)}.swap(*this); + return *this; + } + + [[nodiscard]] bool is_valid() const noexcept { + return !!vtable_; + } + + [[nodiscard]] explicit operator bool() const noexcept { + return is_valid(); + } + + R operator()(Args... args) const { + return vtable_ + ? vtable_->call(*this, std::forward(args)...) + : throw std::bad_function_call(); + } + + void reset() noexcept { + if ( vtable_ ) { + vtable_->destroy(*this); + } + } + + void swap(fixed_function& other) noexcept { + vtable_t::swap(*this, other); + } + private: + struct vtable_t; + vtable_t* vtable_{}; + private: + using storage_t = std::aligned_storage_t; + storage_t storage_{}; + }; + + template < typename Function, std::size_t MaxFunctorSize > + inline void swap(fixed_function& l, fixed_function& r) noexcept { + l.swap(r); + } +} + +namespace meta_hpp::detail +{ + template < typename R, typename... Args, std::size_t MaxFunctorSize > + struct fixed_function::vtable_t final { + R (*const call)(const fixed_function& self, Args... args); + void (*const move)(fixed_function& from, fixed_function& to) noexcept; + void (*const destroy)(fixed_function& self); + + template < typename T > + static T* storage_cast(storage_t& storage) noexcept { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + return reinterpret_cast(&storage); + } + + template < typename T > + static const T* storage_cast(const storage_t& storage) noexcept { + // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) + return reinterpret_cast(&storage); + } + + template < typename FunctionType > + static vtable_t* get() { + static vtable_t table{ + .call = +[](const fixed_function& self, Args... args) -> R { + assert(self); + + const FunctionType& src = *storage_cast(self.storage_); + return std::invoke(src, std::forward(args)...); + }, + .move = +[](fixed_function& from, fixed_function& to) noexcept { + assert(from && !to); + + FunctionType& src = *storage_cast(from.storage_); + ::new (&to.storage_) FunctionType(std::move(src)); + + to.vtable_ = from.vtable_; + from.vtable_->destroy(from); + }, + .destroy = +[](fixed_function& self){ + assert(self); + + FunctionType& src = *storage_cast(self.storage_); + src.~FunctionType(); + + self.vtable_ = nullptr; + }, + }; + return &table; + } + + template < typename Functor > + static void construct(fixed_function& dst, Functor&& functor) { + using Fp = std::decay_t; + + static_assert( + sizeof(Fp) <= MaxFunctorSize && + alignof(Fp) <= alignof(storage_t) && + std::is_invocable_r_v && + std::is_nothrow_move_constructible_v); + + ::new (&dst.storage_) Fp(std::forward(functor)); + dst.vtable_ = vtable_t::get(); + } + + static void swap(fixed_function& l, fixed_function& r) noexcept { + if ( (&l == &r) || (!l && !r) ) { + return; + } + + if ( l && r ) { + fixed_function temp; + r.vtable_->move(r, temp); + l.vtable_->move(l, r); + temp.vtable_->move(temp, l); + } else { + if ( l ) { + l.vtable_->move(l, r); + } else { + r.vtable_->move(r, l); + } + } + } + }; +} + +namespace meta_hpp::detail +{ + namespace impl + { + template < typename F > + struct strip_signature_impl; + + template < typename R, typename C, bool NoExcept, typename... Args > + struct strip_signature_impl { using type = R(Args...); }; + + template < typename R, typename C, bool NoExcept, typename... Args > + struct strip_signature_impl { using type = R(Args...); }; + + template < typename R, typename C, bool NoExcept, typename... Args > + struct strip_signature_impl { using type = R(Args...); }; + + template < typename R, typename C, bool NoExcept, typename... Args > + struct strip_signature_impl { using type = R(Args...); }; + + template < typename F > + using strip_signature_impl_t = typename strip_signature_impl::type; + } + + template < typename R, typename... Args > + fixed_function(R(*)(Args...)) -> fixed_function; + + template < typename Functor + , typename Signature = impl::strip_signature_impl_t > + fixed_function(Functor) -> fixed_function; +} diff --git a/headers/meta.hpp/meta_states.hpp b/headers/meta.hpp/meta_states.hpp index 9f0b877..3f590cd 100644 --- a/headers/meta.hpp/meta_states.hpp +++ b/headers/meta.hpp/meta_states.hpp @@ -367,81 +367,81 @@ namespace meta_hpp namespace meta_hpp::detail { struct ctor_state final { - using invoke_impl = std::function)>; - using is_invocable_with_impl = std::function)>; + using invoke_impl = fixed_function)>; + using is_invocable_with_impl = fixed_function)>; - const ctor_index index; - const invoke_impl invoke; - const is_invocable_with_impl is_invocable_with; + ctor_index index; + invoke_impl invoke; + is_invocable_with_impl is_invocable_with; template < ctor_policy_kind Policy, class_kind Class, typename... Args > [[nodiscard]] static ctor_state_ptr make(); }; struct dtor_state final { - using invoke_impl = std::function; - using is_invocable_with_impl = std::function; + using invoke_impl = fixed_function; + using is_invocable_with_impl = fixed_function; - const dtor_index index; - const invoke_impl invoke; - const is_invocable_with_impl is_invocable_with; + dtor_index index; + invoke_impl invoke; + is_invocable_with_impl is_invocable_with; template < class_kind Class > [[nodiscard]] static dtor_state_ptr make(); }; struct evalue_state final { - const evalue_index index; - const value enum_value; - const value underlying_value; + evalue_index index; + value enum_value; + value underlying_value; template < enum_kind Enum > [[nodiscard]] static evalue_state_ptr make(std::string name, Enum evalue); }; struct function_state final { - using invoke_impl = std::function)>; - using is_invocable_with_impl = std::function)>; + using invoke_impl = fixed_function)>; + using is_invocable_with_impl = fixed_function)>; - const function_index index; - const invoke_impl invoke; - const is_invocable_with_impl is_invocable_with; + function_index index; + invoke_impl invoke; + is_invocable_with_impl is_invocable_with; template < function_policy_kind Policy, function_kind Function > [[nodiscard]] static function_state_ptr make(std::string name, Function function); }; struct member_state final { - using getter_impl = std::function; - using setter_impl = std::function; + using getter_impl = fixed_function; + using setter_impl = fixed_function; - using is_gettable_with_impl = std::function; - using is_settable_with_impl = std::function; + using is_gettable_with_impl = fixed_function; + using is_settable_with_impl = fixed_function; - const member_index index; - const getter_impl getter; - const setter_impl setter; - const is_gettable_with_impl is_gettable_with; - const is_settable_with_impl is_settable_with; + member_index index; + getter_impl getter; + setter_impl setter; + is_gettable_with_impl is_gettable_with; + is_settable_with_impl is_settable_with; template < member_policy_kind Policy, member_kind Member > [[nodiscard]] static member_state_ptr make(std::string name, Member member); }; struct method_state final { - using invoke_impl = std::function)>; - using is_invocable_with_impl = std::function)>; + using invoke_impl = fixed_function)>; + using is_invocable_with_impl = fixed_function)>; - const method_index index; - const invoke_impl invoke; - const is_invocable_with_impl is_invocable_with; + method_index index; + invoke_impl invoke; + is_invocable_with_impl is_invocable_with; template < method_policy_kind Policy, method_kind Method > [[nodiscard]] static method_state_ptr make(std::string name, Method method); }; struct scope_state final { - const scope_index index; + scope_index index; class_map classes{}; enum_map enums{}; @@ -452,14 +452,14 @@ namespace meta_hpp::detail }; struct variable_state final { - using getter_impl = std::function; - using setter_impl = std::function; - using is_settable_with_impl = std::function; + using getter_impl = fixed_function; + using setter_impl = fixed_function; + using is_settable_with_impl = fixed_function; - const variable_index index; - const getter_impl getter; - const setter_impl setter; - const is_settable_with_impl is_settable_with; + variable_index index; + getter_impl getter; + setter_impl setter; + is_settable_with_impl is_settable_with; template < variable_policy_kind Policy, pointer_kind Pointer > [[nodiscard]] static variable_state_ptr make(std::string name, Pointer pointer); diff --git a/headers/meta.hpp/meta_value.hpp b/headers/meta.hpp/meta_value.hpp index 91d5c4b..0a08d50 100644 --- a/headers/meta.hpp/meta_value.hpp +++ b/headers/meta.hpp/meta_value.hpp @@ -12,7 +12,10 @@ namespace meta_hpp::detail { template < typename T > - concept value_kind = stdex::same_as; + inline constexpr bool is_value_kind_v = std::is_same_v; + + template < typename T > + concept value_kind = is_value_kind_v; template < typename T > concept decay_value_kind = value_kind>; @@ -26,7 +29,7 @@ namespace meta_hpp class value final { public: value() = default; - ~value() noexcept; + ~value(); value(value&& other) noexcept; value(const value& other); @@ -45,7 +48,7 @@ namespace meta_hpp [[nodiscard]] bool is_valid() const noexcept; [[nodiscard]] explicit operator bool() const noexcept; - void reset() noexcept; + void reset(); void swap(value& other) noexcept; [[nodiscard]] const any_type& get_type() const noexcept; diff --git a/headers/meta.hpp/meta_value/value.hpp b/headers/meta.hpp/meta_value/value.hpp index d301396..fcd7222 100644 --- a/headers/meta.hpp/meta_value/value.hpp +++ b/headers/meta.hpp/meta_value/value.hpp @@ -40,13 +40,13 @@ namespace meta_hpp std::ostream& (*const ostream)(std::ostream&, const value&); template < typename T > - static T* storage_cast(buffer_t& buffer) noexcept { + static T* buffer_cast(buffer_t& buffer) noexcept { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) return reinterpret_cast(&buffer); } template < typename T > - static const T* storage_cast(const buffer_t& buffer) noexcept { + static const T* buffer_cast(const buffer_t& buffer) noexcept { // NOLINTNEXTLINE(cppcoreguidelines-pro-type-reinterpret-cast) return reinterpret_cast(&buffer); } @@ -55,7 +55,7 @@ namespace meta_hpp static T* storage_cast(storage_u& storage) noexcept { return std::visit(detail::overloaded { [](void* ptr) { return static_cast(ptr); }, - [](buffer_t& buffer) { return storage_cast(buffer); }, + [](buffer_t& buffer) { return buffer_cast(buffer); }, [](...) -> T* { return nullptr; }, }, storage); } @@ -64,7 +64,7 @@ namespace meta_hpp static const T* storage_cast(const storage_u& storage) noexcept { return std::visit(detail::overloaded { [](const void* ptr) { return static_cast(ptr); }, - [](const buffer_t& buffer) { return storage_cast(buffer); }, + [](const buffer_t& buffer) { return buffer_cast(buffer); }, [](...) -> const T* { return nullptr; }, }, storage); } @@ -115,6 +115,7 @@ namespace meta_hpp } template < typename Tp > + // NOLINTNEXTLINE(readability-function-cognitive-complexity) static vtable_t* get() { static vtable_t table{ .type = detail::resolve_type(), @@ -128,15 +129,17 @@ namespace meta_hpp }, .move = [](value& from, value& to) noexcept { + assert(from && !to); + std::visit(detail::overloaded { [&to](void* ptr) { Tp* src = static_cast(ptr); to.storage_.emplace(src); }, [&to](buffer_t& buffer) { - Tp* src = storage_cast(buffer); - ::new (&to.storage_.emplace()) Tp(std::move(*src)); - src->~Tp(); + Tp& src = *buffer_cast(buffer); + ::new (&to.storage_.emplace()) Tp(std::move(src)); + src.~Tp(); }, [](...){} }, from.storage_); @@ -146,14 +149,16 @@ namespace meta_hpp }, .copy = [](const value& from, value& to){ + assert(from && !to); + std::visit(detail::overloaded { [&to](void* ptr) { - const Tp* src = static_cast(ptr); - to.storage_.emplace(new Tp{*src}); + const Tp& src = *static_cast(ptr); + to.storage_.emplace(new Tp(src)); }, [&to](const buffer_t& buffer) { - const Tp* src = storage_cast(buffer); - ::new (&to.storage_.emplace()) Tp(*src); + const Tp& src = *buffer_cast(buffer); + ::new (&to.storage_.emplace()) Tp(src); }, [](...){} }, from.storage_); @@ -162,13 +167,16 @@ namespace meta_hpp }, .destroy = [](value& self) noexcept { + assert(self); + std::visit(detail::overloaded { [](void* ptr) { Tp* src = static_cast(ptr); std::unique_ptr{src}.reset(); }, [](buffer_t& buffer) { - storage_cast(buffer)->~Tp(); + Tp& src = *buffer_cast(buffer); + src.~Tp(); }, [](...){} }, self.storage_); @@ -232,7 +240,7 @@ namespace meta_hpp namespace meta_hpp { - inline value::~value() noexcept { + inline value::~value() { reset(); } @@ -283,10 +291,9 @@ namespace meta_hpp return is_valid(); } - inline void value::reset() noexcept { + inline void value::reset() { if ( vtable_ != nullptr ) { vtable_->destroy(*this); - vtable_ = nullptr; } } diff --git a/untests/meta_base/fixed_function_tests.cpp b/untests/meta_base/fixed_function_tests.cpp new file mode 100644 index 0000000..45c8256 --- /dev/null +++ b/untests/meta_base/fixed_function_tests.cpp @@ -0,0 +1,122 @@ +/******************************************************************************* + * This file is part of the "https://github.com/blackmatov/meta.hpp" + * For conditions of distribution and use, see copyright notice in LICENSE.md + * Copyright (C) 2021, by Matvey Cherevko (blackmatov@gmail.com) + ******************************************************************************/ + +#include "../meta_untests.hpp" + +namespace +{ +} + +TEST_CASE("meta/meta_base/fixed_function") { + namespace meta = meta_hpp; + using meta::detail::fixed_function; + + SUBCASE("is_valid") { + { + fixed_function ff; + CHECK_FALSE(ff); + CHECK_FALSE(ff.is_valid()); + } + { + fixed_function ff = []{}; + CHECK(ff); + CHECK(ff.is_valid()); + } + } + + SUBCASE("ctor") { + { + auto f1 = []{return 1;}; + fixed_function ff{std::move(f1)}; + CHECK(ff() == 1); + + auto f2 = fixed_function{[]{return 2;}}; + ff = std::move(f2); + CHECK(ff() == 2); + } + } + + SUBCASE("reset") { + fixed_function ff = []{return 0;}; + + ff.reset(); + CHECK_FALSE(ff); + CHECK_FALSE(ff.is_valid()); + + ff = []{return 1;}; + CHECK(ff); + CHECK(ff.is_valid()); + + CHECK(ff() == 1); + } + + SUBCASE("move") { + fixed_function ff = []{return 1;}; + fixed_function ff2 = std::move(ff); + CHECK(ff2() == 1); + CHECK_FALSE(ff); + } + + SUBCASE("operator=") { + { + fixed_function ff; + ff = []{return 0;}; + CHECK(ff() == 0); + ff = []{return 1;}; + CHECK(ff() == 1); + + fixed_function ff2 = []{return 2;}; + ff = std::move(ff2); + CHECK(ff() == 2); + CHECK_FALSE(ff2); + } + } + + SUBCASE("swap") { + { + fixed_function ff1; + fixed_function ff2; + ff1.swap(ff2); + CHECK_FALSE(ff1); + CHECK_FALSE(ff2); + } + { + fixed_function ff1 = []{return 1;}; + fixed_function ff2 = []{return 2;}; + ff1.swap(ff2); + CHECK(ff1() == 2); + CHECK(ff2() == 1); + } + { + fixed_function ff1; + fixed_function ff2 = []{return 2;}; + + ff1.swap(ff2); + CHECK(ff1() == 2); + CHECK_FALSE(ff2); + + ff1.swap(ff2); + CHECK_FALSE(ff1); + CHECK(ff2() == 2); + } + } + + SUBCASE("-> int") { + auto f = [ + s = std::make_unique(10) + ](){ return 0; }; + fixed_function ff = std::move(f); + CHECK(ff() == 0); + } + + SUBCASE("-> void") { + auto f = [ + s = std::make_unique(10) + ](){}; + fixed_function ff = std::move(f); + CHECK_NOTHROW(ff()); + } +}