diff --git a/headers/meta.hpp/meta_base/fixed_function.hpp b/headers/meta.hpp/meta_base/fixed_function.hpp index 9cb0675..0cfc6a8 100644 --- a/headers/meta.hpp/meta_base/fixed_function.hpp +++ b/headers/meta.hpp/meta_base/fixed_function.hpp @@ -44,22 +44,22 @@ namespace meta_hpp::detail return *this; } - template < typename Functor > - requires (!std::same_as>) + template < typename F > + requires (!std::same_as>) // NOLINTNEXTLINE(*-forwarding-reference-overload) - fixed_function(Functor&& functor) { - vtable_t::construct(*this, std::forward(functor)); + fixed_function(F&& fun) { + vtable_t::construct(*this, std::forward(fun)); } - template < typename Functor > - requires (!std::same_as>) - fixed_function& operator=(Functor&& functor) { - fixed_function{std::forward(functor)}.swap(*this); + template < typename F > + requires (!std::same_as>) + fixed_function& operator=(F&& fun) { + fixed_function{std::forward(fun)}.swap(*this); return *this; } [[nodiscard]] bool is_valid() const noexcept { - return !!vtable_; + return vtable_ != nullptr; } [[nodiscard]] explicit operator bool() const noexcept { @@ -118,6 +118,8 @@ namespace meta_hpp::detail template < typename Fp > static vtable_t* get() { + static_assert(std::same_as>); + static vtable_t table{ .call = +[](const fixed_function& self, Args... args) -> R { assert(self); // NOLINT @@ -149,16 +151,18 @@ namespace meta_hpp::detail return &table; } - template < typename Functor > - static void construct(fixed_function& dst, Functor&& functor) { - using Fp = std::decay_t; + template < typename F, typename Fp = std::decay_t > + static void construct(fixed_function& dst, F&& fun) { + assert(!dst); // NOLINT static_assert(sizeof(Fp) <= sizeof(buffer_t)); static_assert(alignof(Fp) <= alignof(buffer_t)); static_assert(std::is_invocable_r_v); static_assert(std::is_nothrow_move_constructible_v); - std::construct_at(buffer_cast(dst.buffer_), std::forward(functor)); + std::construct_at( + buffer_cast(dst.buffer_), + std::forward(fun)); dst.vtable_ = vtable_t::get(); } @@ -216,7 +220,6 @@ namespace meta_hpp::detail 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; + template < typename F, typename S = impl::strip_signature_impl_t > + fixed_function(F) -> fixed_function; } diff --git a/headers/meta.hpp/meta_base/stdex.hpp b/headers/meta.hpp/meta_base/stdex.hpp index 095087d..d13bf68 100644 --- a/headers/meta.hpp/meta_base/stdex.hpp +++ b/headers/meta.hpp/meta_base/stdex.hpp @@ -7,6 +7,19 @@ #pragma once #include +#include + +namespace meta_hpp::stdex +{ + template < typename T > + struct is_in_place_type : std::false_type {}; + + template < typename U > + struct is_in_place_type> : std::true_type {}; + + template < typename T > + inline constexpr bool is_in_place_type_v = is_in_place_type::value; +} namespace meta_hpp::stdex { diff --git a/headers/meta.hpp/meta_uvalue.hpp b/headers/meta.hpp/meta_uvalue.hpp index 86b008f..368b3b9 100644 --- a/headers/meta.hpp/meta_uvalue.hpp +++ b/headers/meta.hpp/meta_uvalue.hpp @@ -39,6 +39,7 @@ namespace meta_hpp template < detail::decay_non_value_kind T > requires std::is_copy_constructible_v> + && (!stdex::is_in_place_type_v>) // NOLINTNEXTLINE(*-forwarding-reference-overload) explicit uvalue(T&& val); @@ -46,6 +47,26 @@ namespace meta_hpp requires std::is_copy_constructible_v> uvalue& operator=(T&& val); + template < typename T, typename... Args > + requires std::is_copy_constructible_v> + && std::is_constructible_v, Args...> + explicit uvalue(std::in_place_type_t, Args&&... args); + + template < typename T, typename U, typename... Args > + requires std::is_copy_constructible_v> + && std::is_constructible_v, std::initializer_list&, Args...> + explicit uvalue(std::in_place_type_t, std::initializer_list ilist, Args&&... args); + + template < typename T, typename... Args > + requires std::is_copy_constructible_v> + && std::is_constructible_v, Args...> + std::decay_t& emplace(Args&&... args); + + template < typename T, typename U, typename... Args > + requires std::is_copy_constructible_v> + && std::is_constructible_v, std::initializer_list&, Args...> + std::decay_t& emplace(std::initializer_list ilist, Args&&... args); + [[nodiscard]] bool is_valid() const noexcept; [[nodiscard]] explicit operator bool() const noexcept; @@ -96,4 +117,14 @@ namespace meta_hpp inline void swap(uvalue& l, uvalue& r) noexcept { l.swap(r); } + + template < typename T, typename... Args > + uvalue make_uvalue(Args&&... args) { + return uvalue(std::in_place_type, std::forward(args)...); + } + + template < typename T, typename U, typename... Args > + uvalue make_uvalue(std::initializer_list ilist, Args&&... args) { + return uvalue(std::in_place_type, ilist, std::forward(args)...); + } } diff --git a/headers/meta.hpp/meta_uvalue/uvalue.hpp b/headers/meta.hpp/meta_uvalue/uvalue.hpp index 53e8b43..6f9db24 100644 --- a/headers/meta.hpp/meta_uvalue/uvalue.hpp +++ b/headers/meta.hpp/meta_uvalue/uvalue.hpp @@ -65,9 +65,9 @@ namespace meta_hpp }, storage); } - template < typename T > - static void construct(uvalue& dst, T&& val) { - using Tp = std::decay_t; + template < typename T, typename... Args, typename Tp = std::decay_t > + static Tp& construct(uvalue& dst, Args&&... args) { + assert(!dst); // NOLINT constexpr bool in_buffer = sizeof(Tp) <= sizeof(buffer_t) && @@ -75,12 +75,16 @@ namespace meta_hpp std::is_nothrow_move_constructible_v; if constexpr ( in_buffer ) { - std::construct_at(buffer_cast(dst.storage_.emplace()), std::forward(val)); + std::construct_at( + buffer_cast(dst.storage_.emplace()), + std::forward(args)...); } else { - dst.storage_.emplace(std::make_unique(std::forward(val)).release()); + dst.storage_.emplace( + std::make_unique(std::forward(args)...).release()); } dst.vtable_ = vtable_t::get(); + return *storage_cast(dst.storage_); } static void swap(uvalue& l, uvalue& r) noexcept { @@ -112,6 +116,8 @@ namespace meta_hpp template < typename Tp > // NOLINTNEXTLINE(*-cognitive-complexity) static vtable_t* get() { + static_assert(std::same_as>); + static vtable_t table{ .type = resolve_type(), @@ -251,9 +257,10 @@ namespace meta_hpp template < detail::decay_non_value_kind T > requires std::is_copy_constructible_v> + && (!stdex::is_in_place_type_v>) // NOLINTNEXTLINE(*-forwarding-reference-overload) uvalue::uvalue(T&& val) { - vtable_t::construct(*this, std::forward(val)); + vtable_t::construct(*this, std::forward(val)); } template < detail::decay_non_value_kind T > @@ -263,6 +270,36 @@ namespace meta_hpp return *this; } + template < typename T, typename... Args > + requires std::is_copy_constructible_v> + && std::is_constructible_v, Args...> + uvalue::uvalue(std::in_place_type_t, Args&&... args) { + vtable_t::construct(*this, std::forward(args)...); + } + + template < typename T, typename U, typename... Args > + requires std::is_copy_constructible_v> + && std::is_constructible_v, std::initializer_list&, Args...> + uvalue::uvalue(std::in_place_type_t, std::initializer_list ilist, Args&&... args) { + vtable_t::construct(*this, ilist, std::forward(args)...); + } + + template < typename T, typename... Args > + requires std::is_copy_constructible_v> + && std::is_constructible_v, Args...> + std::decay_t& uvalue::emplace(Args&&... args) { + reset(); + return vtable_t::construct(*this, std::forward(args)...); + } + + template < typename T, typename U, typename... Args > + requires std::is_copy_constructible_v> + && std::is_constructible_v, std::initializer_list&, Args...> + std::decay_t& uvalue::emplace(std::initializer_list ilist, Args&&... args) { + reset(); + return vtable_t::construct(*this, ilist, std::forward(args)...); + } + inline bool uvalue::is_valid() const noexcept { return vtable_ != nullptr; } @@ -528,10 +565,12 @@ namespace meta_hpp namespace meta_hpp { inline std::istream& operator>>(std::istream& is, uvalue& v) { + assert(v && "bad operator call"); // NOLINT return v.vtable_->istream(is, v); } inline std::ostream& operator<<(std::ostream& os, const uvalue& v) { + assert(v && "bad operator call"); // NOLINT return v.vtable_->ostream(os, v); } } diff --git a/singles/headers/meta.hpp/meta_all.hpp b/singles/headers/meta.hpp/meta_all.hpp index 7d9a5b5..5b96df1 100644 --- a/singles/headers/meta.hpp/meta_all.hpp +++ b/singles/headers/meta.hpp/meta_all.hpp @@ -299,22 +299,22 @@ namespace meta_hpp::detail return *this; } - template < typename Functor > - requires (!std::same_as>) + template < typename F > + requires (!std::same_as>) // NOLINTNEXTLINE(*-forwarding-reference-overload) - fixed_function(Functor&& functor) { - vtable_t::construct(*this, std::forward(functor)); + fixed_function(F&& fun) { + vtable_t::construct(*this, std::forward(fun)); } - template < typename Functor > - requires (!std::same_as>) - fixed_function& operator=(Functor&& functor) { - fixed_function{std::forward(functor)}.swap(*this); + template < typename F > + requires (!std::same_as>) + fixed_function& operator=(F&& fun) { + fixed_function{std::forward(fun)}.swap(*this); return *this; } [[nodiscard]] bool is_valid() const noexcept { - return !!vtable_; + return vtable_ != nullptr; } [[nodiscard]] explicit operator bool() const noexcept { @@ -373,6 +373,8 @@ namespace meta_hpp::detail template < typename Fp > static vtable_t* get() { + static_assert(std::same_as>); + static vtable_t table{ .call = +[](const fixed_function& self, Args... args) -> R { assert(self); // NOLINT @@ -404,16 +406,18 @@ namespace meta_hpp::detail return &table; } - template < typename Functor > - static void construct(fixed_function& dst, Functor&& functor) { - using Fp = std::decay_t; + template < typename F, typename Fp = std::decay_t > + static void construct(fixed_function& dst, F&& fun) { + assert(!dst); // NOLINT static_assert(sizeof(Fp) <= sizeof(buffer_t)); static_assert(alignof(Fp) <= alignof(buffer_t)); static_assert(std::is_invocable_r_v); static_assert(std::is_nothrow_move_constructible_v); - std::construct_at(buffer_cast(dst.buffer_), std::forward(functor)); + std::construct_at( + buffer_cast(dst.buffer_), + std::forward(fun)); dst.vtable_ = vtable_t::get(); } @@ -471,9 +475,8 @@ namespace meta_hpp::detail 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; + template < typename F, typename S = impl::strip_signature_impl_t > + fixed_function(F) -> fixed_function; } namespace meta_hpp::detail @@ -550,6 +553,18 @@ namespace meta_hpp::detail } } +namespace meta_hpp::stdex +{ + template < typename T > + struct is_in_place_type : std::false_type {}; + + template < typename U > + struct is_in_place_type> : std::true_type {}; + + template < typename T > + inline constexpr bool is_in_place_type_v = is_in_place_type::value; +} + namespace meta_hpp::stdex { template < typename Enum > @@ -2213,6 +2228,7 @@ namespace meta_hpp template < detail::decay_non_value_kind T > requires std::is_copy_constructible_v> + && (!stdex::is_in_place_type_v>) // NOLINTNEXTLINE(*-forwarding-reference-overload) explicit uvalue(T&& val); @@ -2220,6 +2236,26 @@ namespace meta_hpp requires std::is_copy_constructible_v> uvalue& operator=(T&& val); + template < typename T, typename... Args > + requires std::is_copy_constructible_v> + && std::is_constructible_v, Args...> + explicit uvalue(std::in_place_type_t, Args&&... args); + + template < typename T, typename U, typename... Args > + requires std::is_copy_constructible_v> + && std::is_constructible_v, std::initializer_list&, Args...> + explicit uvalue(std::in_place_type_t, std::initializer_list ilist, Args&&... args); + + template < typename T, typename... Args > + requires std::is_copy_constructible_v> + && std::is_constructible_v, Args...> + std::decay_t& emplace(Args&&... args); + + template < typename T, typename U, typename... Args > + requires std::is_copy_constructible_v> + && std::is_constructible_v, std::initializer_list&, Args...> + std::decay_t& emplace(std::initializer_list ilist, Args&&... args); + [[nodiscard]] bool is_valid() const noexcept; [[nodiscard]] explicit operator bool() const noexcept; @@ -2270,6 +2306,16 @@ namespace meta_hpp inline void swap(uvalue& l, uvalue& r) noexcept { l.swap(r); } + + template < typename T, typename... Args > + uvalue make_uvalue(Args&&... args) { + return uvalue(std::in_place_type, std::forward(args)...); + } + + template < typename T, typename U, typename... Args > + uvalue make_uvalue(std::initializer_list ilist, Args&&... args) { + return uvalue(std::in_place_type, ilist, std::forward(args)...); + } } namespace meta_hpp::detail @@ -8022,9 +8068,9 @@ namespace meta_hpp }, storage); } - template < typename T > - static void construct(uvalue& dst, T&& val) { - using Tp = std::decay_t; + template < typename T, typename... Args, typename Tp = std::decay_t > + static Tp& construct(uvalue& dst, Args&&... args) { + assert(!dst); // NOLINT constexpr bool in_buffer = sizeof(Tp) <= sizeof(buffer_t) && @@ -8032,12 +8078,16 @@ namespace meta_hpp std::is_nothrow_move_constructible_v; if constexpr ( in_buffer ) { - std::construct_at(buffer_cast(dst.storage_.emplace()), std::forward(val)); + std::construct_at( + buffer_cast(dst.storage_.emplace()), + std::forward(args)...); } else { - dst.storage_.emplace(std::make_unique(std::forward(val)).release()); + dst.storage_.emplace( + std::make_unique(std::forward(args)...).release()); } dst.vtable_ = vtable_t::get(); + return *storage_cast(dst.storage_); } static void swap(uvalue& l, uvalue& r) noexcept { @@ -8069,6 +8119,8 @@ namespace meta_hpp template < typename Tp > // NOLINTNEXTLINE(*-cognitive-complexity) static vtable_t* get() { + static_assert(std::same_as>); + static vtable_t table{ .type = resolve_type(), @@ -8208,9 +8260,10 @@ namespace meta_hpp template < detail::decay_non_value_kind T > requires std::is_copy_constructible_v> + && (!stdex::is_in_place_type_v>) // NOLINTNEXTLINE(*-forwarding-reference-overload) uvalue::uvalue(T&& val) { - vtable_t::construct(*this, std::forward(val)); + vtable_t::construct(*this, std::forward(val)); } template < detail::decay_non_value_kind T > @@ -8220,6 +8273,36 @@ namespace meta_hpp return *this; } + template < typename T, typename... Args > + requires std::is_copy_constructible_v> + && std::is_constructible_v, Args...> + uvalue::uvalue(std::in_place_type_t, Args&&... args) { + vtable_t::construct(*this, std::forward(args)...); + } + + template < typename T, typename U, typename... Args > + requires std::is_copy_constructible_v> + && std::is_constructible_v, std::initializer_list&, Args...> + uvalue::uvalue(std::in_place_type_t, std::initializer_list ilist, Args&&... args) { + vtable_t::construct(*this, ilist, std::forward(args)...); + } + + template < typename T, typename... Args > + requires std::is_copy_constructible_v> + && std::is_constructible_v, Args...> + std::decay_t& uvalue::emplace(Args&&... args) { + reset(); + return vtable_t::construct(*this, std::forward(args)...); + } + + template < typename T, typename U, typename... Args > + requires std::is_copy_constructible_v> + && std::is_constructible_v, std::initializer_list&, Args...> + std::decay_t& uvalue::emplace(std::initializer_list ilist, Args&&... args) { + reset(); + return vtable_t::construct(*this, ilist, std::forward(args)...); + } + inline bool uvalue::is_valid() const noexcept { return vtable_ != nullptr; } @@ -8485,10 +8568,12 @@ namespace meta_hpp namespace meta_hpp { inline std::istream& operator>>(std::istream& is, uvalue& v) { + assert(v && "bad operator call"); // NOLINT return v.vtable_->istream(is, v); } inline std::ostream& operator<<(std::ostream& os, const uvalue& v) { + assert(v && "bad operator call"); // NOLINT return v.vtable_->ostream(os, v); } } diff --git a/untests/meta_utilities/value4_tests.cpp b/untests/meta_utilities/value4_tests.cpp index 2c7c14c..c6d1339 100644 --- a/untests/meta_utilities/value4_tests.cpp +++ b/untests/meta_utilities/value4_tests.cpp @@ -11,14 +11,41 @@ namespace struct clazz_throw_dtor { int i{}; - clazz_throw_dtor(int ni) : i{ni} {} - clazz_throw_dtor(const clazz_throw_dtor&) = default; + [[maybe_unused]] + clazz_throw_dtor() { + ++constructor_counter; + } - ~clazz_throw_dtor() noexcept(false) = default; + [[maybe_unused]] + clazz_throw_dtor(int ni) : i{ni} { + ++constructor_counter; + } + + [[maybe_unused]] + clazz_throw_dtor(clazz_throw_dtor&& other) + : i{other.i} { + other.i = 0; + ++move_constructor_counter; + } + + [[maybe_unused]] + clazz_throw_dtor(const clazz_throw_dtor& other) + : i{other.i} { + ++copy_constructor_counter; + } + + ~clazz_throw_dtor() noexcept(false) { + ++destructor_counter; + } static clazz_throw_dtor make(int ni) { return {ni}; } + + inline static int destructor_counter{}; + inline static int constructor_counter{}; + inline static int move_constructor_counter{}; + inline static int copy_constructor_counter{}; }; } @@ -72,3 +99,164 @@ TEST_CASE("meta/meta_utilities/value5/throw_dtor") { CHECK(v.get_as().i == 42); } } + +TEST_CASE("meta/meta_utilities/value5/inplace") { + namespace meta = meta_hpp; + + clazz_throw_dtor::destructor_counter = 0; + clazz_throw_dtor::constructor_counter = 0; + clazz_throw_dtor::move_constructor_counter = 0; + clazz_throw_dtor::copy_constructor_counter = 0; + + SUBCASE("def") { + meta::uvalue v = meta::make_uvalue(); + CHECK(v.get_type() == meta::resolve_type()); + CHECK(v.get_as().i == 0); + + CHECK(clazz_throw_dtor::destructor_counter == 0); + CHECK(clazz_throw_dtor::constructor_counter == 1); + CHECK(clazz_throw_dtor::move_constructor_counter == 0); + CHECK(clazz_throw_dtor::copy_constructor_counter == 0); + } + + SUBCASE("args") { + meta::uvalue v = meta::make_uvalue>(2, 42); + CHECK(v.get_type() == meta::resolve_type>()); + + CHECK(v[0].get_type() == meta::resolve_type()); + CHECK(v[0].get_as().i == 42); + + CHECK(v[1].get_type() == meta::resolve_type()); + CHECK(v[1].get_as().i == 42); + } + + SUBCASE("args/counters") { + { + meta::uvalue v = meta::make_uvalue>(2, 42); + CHECK(v.get_type() == meta::resolve_type>()); + } + CHECK(clazz_throw_dtor::destructor_counter == 3); + CHECK(clazz_throw_dtor::constructor_counter == 1); + CHECK(clazz_throw_dtor::move_constructor_counter == 0); + CHECK(clazz_throw_dtor::copy_constructor_counter == 2); + } + + SUBCASE("ilist/1") { + meta::uvalue v = meta::make_uvalue>({21, 42}); + CHECK(v.get_type() == meta::resolve_type>()); + + CHECK(v[0].get_type() == meta::resolve_type()); + CHECK(v[0].get_as() == 21); + + CHECK(v[1].get_type() == meta::resolve_type()); + CHECK(v[1].get_as() == 42); + } + + SUBCASE("ilist/2") { + meta::uvalue v = meta::make_uvalue>({21, 42}, std::allocator{}); + CHECK(v.get_type() == meta::resolve_type>>()); + + CHECK(v[0].get_type() == meta::resolve_type()); + CHECK(v[0].get_as() == 21); + + CHECK(v[1].get_type() == meta::resolve_type()); + CHECK(v[1].get_as() == 42); + } + + SUBCASE("ilist/counters") { + { + meta::uvalue v = meta::make_uvalue>({ + clazz_throw_dtor{21}, + clazz_throw_dtor{42}}); + CHECK(v.get_type() == meta::resolve_type>()); + } + CHECK(clazz_throw_dtor::destructor_counter == 4); + CHECK(clazz_throw_dtor::constructor_counter == 2); + CHECK(clazz_throw_dtor::move_constructor_counter == 0); + CHECK(clazz_throw_dtor::copy_constructor_counter == 2); + } +} + +TEST_CASE("meta/meta_utilities/value5/emplace") { + namespace meta = meta_hpp; + + clazz_throw_dtor::destructor_counter = 0; + clazz_throw_dtor::constructor_counter = 0; + clazz_throw_dtor::move_constructor_counter = 0; + clazz_throw_dtor::copy_constructor_counter = 0; + + SUBCASE("def") { + { + meta::uvalue v = meta::make_uvalue(21); + CHECK(v.emplace().i == 0); + CHECK(v.get_type() == meta::resolve_type()); + CHECK(v.get_as().i == 0); + } + CHECK(clazz_throw_dtor::destructor_counter == 2); + CHECK(clazz_throw_dtor::constructor_counter == 2); + CHECK(clazz_throw_dtor::move_constructor_counter == 0); + CHECK(clazz_throw_dtor::copy_constructor_counter == 0); + } + + SUBCASE("args") { + meta::uvalue v = meta::make_uvalue(21); + v.emplace>(2, 42); + CHECK(v.get_type() == meta::resolve_type>()); + + CHECK(v[0].get_type() == meta::resolve_type()); + CHECK(v[0].get_as().i == 42); + + CHECK(v[1].get_type() == meta::resolve_type()); + CHECK(v[1].get_as().i == 42); + } + + SUBCASE("args/counters") { + { + meta::uvalue v = meta::make_uvalue(21); + v.emplace>(2, 42); + CHECK(v.get_type() == meta::resolve_type>()); + } + CHECK(clazz_throw_dtor::destructor_counter == 4); + CHECK(clazz_throw_dtor::constructor_counter == 2); + CHECK(clazz_throw_dtor::move_constructor_counter == 0); + CHECK(clazz_throw_dtor::copy_constructor_counter == 2); + } + + SUBCASE("ilist/1") { + meta::uvalue v = meta::make_uvalue(84); + v.emplace>({21, 42}); + CHECK(v.get_type() == meta::resolve_type>()); + + CHECK(v[0].get_type() == meta::resolve_type()); + CHECK(v[0].get_as() == 21); + + CHECK(v[1].get_type() == meta::resolve_type()); + CHECK(v[1].get_as() == 42); + } + + SUBCASE("ilist/2") { + meta::uvalue v = meta::make_uvalue(84); + v.emplace>({21, 42}, std::allocator{}); + CHECK(v.get_type() == meta::resolve_type>>()); + + CHECK(v[0].get_type() == meta::resolve_type()); + CHECK(v[0].get_as() == 21); + + CHECK(v[1].get_type() == meta::resolve_type()); + CHECK(v[1].get_as() == 42); + } + + SUBCASE("ilist/counters") { + { + meta::uvalue v = meta::make_uvalue(84); + v.emplace>({ + clazz_throw_dtor{21}, + clazz_throw_dtor{42}}); + CHECK(v.get_type() == meta::resolve_type>()); + } + CHECK(clazz_throw_dtor::destructor_counter == 5); + CHECK(clazz_throw_dtor::constructor_counter == 3); + CHECK(clazz_throw_dtor::move_constructor_counter == 0); + CHECK(clazz_throw_dtor::copy_constructor_counter == 2); + } +}