diff --git a/CMakeLists.txt b/CMakeLists.txt index cd919c7..1efe609 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -6,7 +6,7 @@ cmake_minimum_required(VERSION 3.21) # Only set the cxx_standard if it is not set by someone else if (NOT DEFINED CMAKE_CXX_STANDARD) - set(CMAKE_CXX_STANDARD 20) + set(CMAKE_CXX_STANDARD 26) endif() # strongly encouraged to enable this globally to avoid conflicts between diff --git a/include/lefticus/tools/forwarding_ref.hpp b/include/lefticus/tools/forwarding_ref.hpp new file mode 100644 index 0000000..e88b6a2 --- /dev/null +++ b/include/lefticus/tools/forwarding_ref.hpp @@ -0,0 +1,63 @@ +/* +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to +*/ + + +#ifndef LEFTICUS_TOOLS_FORWARDING_REF_HPP +#define LEFTICUS_TOOLS_FORWARDING_REF_HPP + +#include + +#include "lifetimebound.hpp" + +namespace lefticus::tools { + +template struct [[nodiscard]] forwarding_ref +{ + using pointer_type = std::add_pointer_t>; + using reference_type = T; + + forwarding_ref(forwarding_ref &&) = delete ( + "you accidentally moved a ref (auto obj = std::move(ref)) instead of (Type obj = ref)"); + forwarding_ref(const forwarding_ref &) = delete ( + "you accidentally copied a ref (auto obj = ref) instead of (Type obj = ref)"); + forwarding_ref &operator=(forwarding_ref &&) = delete ( + "you accidentally move assigned a ref (ref = std::move(other_ref))"); + forwarding_ref &operator=(const forwarding_ref &) = delete ("you accidentally copy assigned a ref (ref = other_ref)"); + + [[nodiscard]] explicit constexpr forwarding_ref(reference_type ref_ LIFETIMEBOUND) noexcept : ref{ &ref_ } {} + [[nodiscard]] constexpr operator reference_type() noexcept LIFETIMEBOUND { return static_cast(*ref); } + +private: + pointer_type ref; +}; + +template forwarding_ref(T &&) -> forwarding_ref; + + +}// namespace lefticus::tools + +#endif diff --git a/include/lefticus/tools/has_move_ctor.hpp b/include/lefticus/tools/has_move_ctor.hpp new file mode 100644 index 0000000..dedc2a2 --- /dev/null +++ b/include/lefticus/tools/has_move_ctor.hpp @@ -0,0 +1,60 @@ +/* +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to +*/ + + +#ifndef LEFTICUS_TOOLS_HAS_MOVE_CTOR_HPP +#define LEFTICUS_TOOLS_HAS_MOVE_CTOR_HPP + +#include + +namespace lefticus::tools { + +template struct DetectMoveConstruction +{ + operator P const &(); + operator P &&(); +}; + +template +concept copyable_xor_moveable = requires(T t, DetectMoveConstruction m) +{ + // borrowed from + // https://stackoverflow.com/questions/51901837/how-to-get-if-a-type-is-truly-move-constructible/51912859#51912859 + // if this line below compiles then we know we only have either + // a move assignment or a copy assignment, otherwise + // it would be ambiguous + // we cannot detect the constructor because MSVC + // seems to have a bug, but we can detect assignment! + t = m; +}; + +template +concept has_move_ctor = std::move_constructible && !copyable_xor_moveable; + +}// namespace lefticus::tools + +#endif diff --git a/include/lefticus/tools/lifetimebound.hpp b/include/lefticus/tools/lifetimebound.hpp new file mode 100644 index 0000000..8a99ff5 --- /dev/null +++ b/include/lefticus/tools/lifetimebound.hpp @@ -0,0 +1,41 @@ +/* +This is free and unencumbered software released into the public domain. + +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. + +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. + +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. + +For more information, please refer to +*/ + + +#ifndef LEFTICUS_TOOLS_LIFETIMEBOUND_HPP +#define LEFTICUS_TOOLS_LIFETIMEBOUND_HPP + + +#if defined(__has_cpp_attribute) +#if __has_cpp_attribute(clang::lifetimebound) +#define LIFETIMEBOUND [[clang::lifetimebound]] +#else +#define LIFETIMEBOUND +#endif +#endif + +#endif diff --git a/include/lefticus/tools/moving_ref.hpp b/include/lefticus/tools/moving_ref.hpp index 3c5988f..a435dcf 100644 --- a/include/lefticus/tools/moving_ref.hpp +++ b/include/lefticus/tools/moving_ref.hpp @@ -1,53 +1,68 @@ -#ifndef LEFTICUS_TOOLS_MOVING_REF_HPP -#define LEFTICUS_TOOLS_MOVING_REF_HPP +/* +This is free and unencumbered software released into the public domain. -#include -#include +Anyone is free to copy, modify, publish, use, compile, sell, or +distribute this software, either in source code form or as a compiled +binary, for any purpose, commercial or non-commercial, and by any +means. -namespace lefticus { +In jurisdictions that recognize copyright laws, the author or authors +of this software dedicate any and all copyright interest in the +software to the public domain. We make this dedication for the benefit +of the public at large and to the detriment of our heirs and +successors. We intend this dedication to be an overt act of +relinquishment in perpetuity of all present and future rights to this +software under copyright law. +THE SOFTWARE IS PROVIDED "AS IS", WITHOUT WARRANTY OF ANY KIND, +EXPRESS OR IMPLIED, INCLUDING BUT NOT LIMITED TO THE WARRANTIES OF +MERCHANTABILITY, FITNESS FOR A PARTICULAR PURPOSE AND NONINFRINGEMENT. +IN NO EVENT SHALL THE AUTHORS BE LIABLE FOR ANY CLAIM, DAMAGES OR +OTHER LIABILITY, WHETHER IN AN ACTION OF CONTRACT, TORT OR OTHERWISE, +ARISING FROM, OUT OF OR IN CONNECTION WITH THE SOFTWARE OR THE USE OR +OTHER DEALINGS IN THE SOFTWARE. -#ifdef __cpp_concepts -template -struct DetectMoveConstruction -{ - constexpr operator P const&(); - constexpr operator P&&(); -}; +For more information, please refer to +*/ + + +#ifndef LEFTICUS_TOOLS_MOVING_REF_HPP +#define LEFTICUS_TOOLS_MOVING_REF_HPP + +#include "has_move_ctor.hpp" +#include "lifetimebound.hpp" + +namespace lefticus::tools { template -concept copyable_xor_moveable = requires (T t, DetectMoveConstruction m) { - // borrowed from https://stackoverflow.com/questions/51901837/how-to-get-if-a-type-is-truly-move-constructible/51912859#51912859 - // if this line below compiles then we know we only have either - // a move assignment or a copy assignment, otherwise - // it would be ambiguous - // we cannot detect the constructor because MSVC - // seems to have a bug, but we can detect assignment! - t = m; -}; +concept movable = std::is_move_constructible_v && std::is_move_assignable_v && not std::is_const_v< + T> && not std::is_pointer_v && not std::is_reference_v; template -concept has_move_ctor = - std::move_constructible && !copyable_xor_moveable; -#endif +requires movable && has_move_ctor +struct [[nodiscard]] moving_ref +{ + using value_type = T; + using pointer_type = std::add_pointer_t; + using reference_type = std::add_rvalue_reference_t; + moving_ref(moving_ref &&) = delete ( + "you accidentally moved a ref (auto obj = std::move(ref)) instead of (Type obj = ref)"); + moving_ref(const moving_ref &) = delete ( + "you accidentally copied a ref (auto obj = ref) instead of (Type obj = ref)"); + moving_ref &operator=(moving_ref &&) = delete ("you accidentally move assigned a ref (ref = std::move(other_ref))"); + moving_ref &operator=(const moving_ref &) = delete ("you accidentally copy assigned a ref (ref = other_ref)"); + [[nodiscard]] explicit constexpr moving_ref(reference_type ref_ LIFETIMEBOUND) noexcept : ref{ &ref_ } {} -#ifdef __cpp_concepts -template -#else -template -#endif + moving_ref(const T &) = delete ("this type is only to be used with rvalue reference parameters"); -struct moving_ref { - constexpr moving_ref(T &&val) : ref{std::move(val)} {} - constexpr operator T&&() { - return std::move(ref); - } - T &&ref; + [[nodiscard]] constexpr operator reference_type() noexcept LIFETIMEBOUND { return static_cast(*ref); } + +private: + pointer_type ref; }; -} +}// namespace lefticus::tools #endif - diff --git a/include/lefticus/tools/non_promoting_ints.hpp b/include/lefticus/tools/non_promoting_ints.hpp index 5b07f0d..6d761db 100644 --- a/include/lefticus/tools/non_promoting_ints.hpp +++ b/include/lefticus/tools/non_promoting_ints.hpp @@ -245,22 +245,22 @@ using int_np64_t = int_np; namespace literals { - consteval auto operator"" _npu8(unsigned long long val) { return uint_np8_t::from(val); } + consteval auto operator""_npu8(unsigned long long val) { return uint_np8_t::from(val); } - consteval auto operator"" _npu16(unsigned long long val) { return uint_np16_t::from(val); } + consteval auto operator""_npu16(unsigned long long val) { return uint_np16_t::from(val); } - consteval auto operator"" _npu32(unsigned long long val) { return uint_np32_t::from(val); } + consteval auto operator""_npu32(unsigned long long val) { return uint_np32_t::from(val); } - consteval auto operator"" _npu64(unsigned long long val) { return uint_np64_t::from(val); } + consteval auto operator""_npu64(unsigned long long val) { return uint_np64_t::from(val); } - consteval auto operator"" _np8(unsigned long long val) { return int_np8_t::from(val); } + consteval auto operator""_np8(unsigned long long val) { return int_np8_t::from(val); } - consteval auto operator"" _np16(unsigned long long val) { return int_np16_t::from(val); } + consteval auto operator""_np16(unsigned long long val) { return int_np16_t::from(val); } - consteval auto operator"" _np32(unsigned long long val) { return int_np32_t::from(val); } + consteval auto operator""_np32(unsigned long long val) { return int_np32_t::from(val); } - consteval auto operator"" _np64(unsigned long long val) { return int_np64_t::from(val); } + consteval auto operator""_np64(unsigned long long val) { return int_np64_t::from(val); } }// namespace literals }// namespace lefticus::tools diff --git a/include/lefticus/tools/polymorphic_variant.hpp b/include/lefticus/tools/polymorphic_variant.hpp new file mode 100644 index 0000000..ad12c67 --- /dev/null +++ b/include/lefticus/tools/polymorphic_variant.hpp @@ -0,0 +1,74 @@ +#ifndef LEFTICUS_TOOLS_POLYMORPHIC_VARIANT_HPP +#define LEFTICUS_TOOLS_POLYMORPHIC_VARIANT_HPP + +namespace lefticus::tools { +template const Base *get_base(const std::variant &input) +{ + const Base *retval = nullptr; + + ((retval = retval ? retval : std::get_if(&input)), ...); + + return retval; +} + +template Base *get_base(std::variant &input) +{ + Base *retval = nullptr; + + ((retval = retval ? retval : std::get_if(&input)), ...); + + return retval; +} + +// a variant-based type that allows polymorphic access to the underlying +// data +template +requires std::is_polymorphic_v &&(std::is_base_of_v &&...) + && (std::is_final_v && ...) class polymorphic_variant +{ +public: + polymorphic_variant() = delete; + + using concrete_Type_List = Type_List; + using contained_type = decltype(variant_type(concrete_Type_List{})); + + // allow any of the non-abstract supported Type_List in + template + constexpr explicit polymorphic_variant( + Param &&p) requires contains_type_v, concrete_Type_List> + : value{ std::forward(p) } + {} + + // anything that isn't an exact match for one of the supported Type_List + // is deleted. Why? We don't want to slice! + template constexpr explicit polymorphic_variant(Param &&) = delete; + + template + constexpr explicit polymorphic_variant(std::in_place_type_t ipt, Args &&...args) + : value(ipt, std::forward(args)...) + {} + + [[nodiscard]] constexpr Base *get() { return get_base(value); } + [[nodiscard]] constexpr const Base *get() const { return get_base(value); } + + [[nodiscard]] constexpr Base &operator*() { return *get(); } + [[nodiscard]] constexpr const Base &operator*() const { return *get(); } + + [[nodiscard]] constexpr Base *operator->() { return get(); } + [[nodiscard]] constexpr const Base *operator->() const { return get(); } + +private: + [[nodiscard]] constexpr static auto getter() noexcept + { + return [](auto &obj) -> Base * { return &obj; }; + } + + [[nodiscard]] constexpr static auto const_getter() noexcept + { + return [](const auto &obj) -> const Base * { return &obj; }; + } + + contained_type value; +}; +}// namespace lefticus::tools +#endif diff --git a/include/lefticus/tools/strong_types.hpp b/include/lefticus/tools/strong_types.hpp index 9613e6e..cdd826b 100644 --- a/include/lefticus/tools/strong_types.hpp +++ b/include/lefticus/tools/strong_types.hpp @@ -46,31 +46,53 @@ For more information, please refer to namespace lefticus::tools { template -concept addable = requires(LHS lhs, RHS rhs) { add(lhs, rhs); }; +concept addable = requires(LHS lhs, RHS rhs) +{ + add(lhs, rhs); +}; template -concept subtractable = requires(LHS lhs, RHS rhs) { subtract(lhs, rhs); }; +concept subtractable = requires(LHS lhs, RHS rhs) +{ + subtract(lhs, rhs); +}; template -concept multipliable = requires(LHS lhs, RHS rhs) { multiply(lhs, rhs); }; +concept multipliable = requires(LHS lhs, RHS rhs) +{ + multiply(lhs, rhs); +}; template -concept dividable = requires(LHS lhs, RHS rhs) { divide(lhs, rhs); }; +concept dividable = requires(LHS lhs, RHS rhs) +{ + divide(lhs, rhs); +}; template -concept negatable = requires(LHS lhs) { negate(lhs); }; +concept negatable = requires(LHS lhs) +{ + negate(lhs); +}; template -concept orderable = requires(LHS lhs, RHS rhs) { order(lhs, rhs); }; +concept orderable = requires(LHS lhs, RHS rhs) +{ + order(lhs, rhs); +}; template -concept equatable = requires(LHS lhs, RHS rhs) { equate(lhs, rhs); }; +concept equatable = requires(LHS lhs, RHS rhs) +{ + equate(lhs, rhs); +}; template -concept casts_to = requires(const From &from) { +concept casts_to = requires(const From &from) +{ { casts(from) - } -> std::same_as; + } -> std::same_as; }; @@ -79,24 +101,21 @@ template constexpr void null_validator(const Underlying &) template> struct strong_alias { template - constexpr explicit strong_alias(Param &&...param) - requires std::is_constructible_v + constexpr explicit strong_alias(Param &&...param) requires std::is_constructible_v : data(std::forward(param)...) { Validator(data); } - [[nodiscard]] constexpr const Underlying &get() const & noexcept { return data; } - [[nodiscard]] constexpr Underlying &&get() && noexcept { return std::move(data); } - [[nodiscard]] constexpr Underlying &get() & noexcept { return data; } + [[nodiscard]] constexpr const Underlying &get() const &noexcept { return data; } + [[nodiscard]] constexpr Underlying &&get() &&noexcept { return std::move(data); } + [[nodiscard]] constexpr Underlying &get() &noexcept { return data; } [[nodiscard]] auto operator<=>(const strong_alias &rhs) const noexcept - requires(orderable) - = default; + requires(orderable) = default; [[nodiscard]] bool operator==(const strong_alias &rhs) const noexcept - requires(equatable) - = default; + requires(equatable) = default; template [[nodiscard]] constexpr auto operator<=>(const strong_alias &rhs) const noexcept @@ -111,52 +130,46 @@ template -[[nodiscard]] constexpr auto operator-(LHS &&lhs) noexcept(noexcept(-lhs.get())) -> decltype(negate(lhs)) - requires(negatable) +[[nodiscard]] constexpr auto operator-(LHS &&lhs) noexcept(noexcept(-lhs.get())) + -> decltype(negate(lhs)) requires(negatable) { return decltype(negate(lhs)){ std::forward(lhs).get() }; } template [[nodiscard]] constexpr auto operator+(LHS &&lhs, RHS &&rhs) noexcept(noexcept(lhs.get() + rhs.get())) - -> decltype(add(lhs, rhs)) - requires(addable) + -> decltype(add(lhs, rhs)) requires(addable) { return decltype(add(lhs, rhs)){ std::forward(lhs).get() + std::forward(rhs).get() }; } template [[nodiscard]] constexpr auto operator-(LHS &&lhs, RHS &&rhs) noexcept(noexcept(lhs.get() - rhs.get())) - -> decltype(subtract(lhs, rhs)) - requires(subtractable) + -> decltype(subtract(lhs, rhs)) requires(subtractable) { return decltype(subtract(lhs, rhs)){ std::forward(lhs).get() - std::forward(rhs).get() }; } template [[nodiscard]] constexpr auto operator*(LHS &&lhs, RHS &&rhs) noexcept(noexcept(lhs.get() * rhs.get())) - -> decltype(multiply(lhs, rhs)) - requires(multipliable) + -> decltype(multiply(lhs, rhs)) requires(multipliable) { return decltype(multiply(lhs, rhs)){ std::forward(lhs).get() * std::forward(rhs).get() }; } template [[nodiscard]] constexpr auto operator/(LHS &&lhs, RHS &&rhs) noexcept(noexcept(lhs.get() / rhs.get())) - -> decltype(divide(lhs, rhs)) - requires(dividable) + -> decltype(divide(lhs, rhs)) requires(dividable) { return decltype(divide(lhs, rhs)){ std::forward(lhs).get() / std::forward(rhs).get() }; } template -[[nodiscard]] constexpr auto strong_cast(From &&from) -> To - requires(casts_to) +[[nodiscard]] constexpr auto strong_cast(From &&from) -> To requires(casts_to) { return To{ std::forward(from).get() }; } diff --git a/include/lefticus/tools/type_lists.hpp b/include/lefticus/tools/type_lists.hpp index 78aa89a..0853da8 100644 --- a/include/lefticus/tools/type_lists.hpp +++ b/include/lefticus/tools/type_lists.hpp @@ -57,18 +57,16 @@ template auto last_type(type_list) -> decltype((type_list auto first_type(type_list) -> First; template -auto nth_type_helper(type_list) -> First - requires(N == 0); +auto nth_type_helper(type_list) -> First requires(N == 0); // This is an optimization if we detect it's just the last one, so we don't // recurse further template -auto nth_type_helper(type_list list) -> decltype(last_type(list)) - requires(N > 0 && N == sizeof...(T) - 1); +auto nth_type_helper(type_list list) -> decltype(last_type(list)) requires(N > 0 && N == sizeof...(T) - 1); template -auto nth_type_helper(type_list list) -> decltype(nth_type_helper(type_list{})) - requires(N > 0 && N != sizeof...(T)); +auto nth_type_helper(type_list list) + -> decltype(nth_type_helper(type_list{})) requires(N > 0 && N != sizeof...(T)); template auto nth_type(type_list list) -> decltype(nth_type_helper(list)); @@ -76,12 +74,11 @@ template using nth_t = decltype(nth_type(T{})); template auto split_n_helper(type_list last, type_list) -> type_pair, type_list> - requires(N == 0); +requires(N == 0); template auto split_n_helper(type_list, type_list) - -> decltype(split_n_helper(type_list{}, type_list{})) - requires(N > 0); + -> decltype(split_n_helper(type_list{}, type_list{})) requires(N > 0); template auto split_n(type_list list) -> decltype(split_n_helper(type_list<>{}, list)); diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 40cf912..3e745ef 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -84,7 +84,7 @@ add_constexpr_test_executables( flat_map_tests.cpp type_lists_tests.cpp strong_types_tests.cpp - moving_ref.cpp) + moving_forwarding_ref.cpp) target_link_libraries( "constexpr_tests" PRIVATE lefticus::tools @@ -134,3 +134,5 @@ test_header_compiles(utility.hpp) test_header_compiles(strong_types.hpp) test_header_compiles(type_lists.hpp) test_header_compiles(moving_ref.hpp) +test_header_compiles(forwarding_ref.hpp) + diff --git a/test/moving_forwarding_ref.cpp b/test/moving_forwarding_ref.cpp new file mode 100644 index 0000000..e9f9024 --- /dev/null +++ b/test/moving_forwarding_ref.cpp @@ -0,0 +1,162 @@ +#include +#include +#include + +#ifdef CATCH_CONFIG_RUNTIME_STATIC_REQUIRE +#define CONSTEXPR +#else +// NOLINTNEXTLINE +#define CONSTEXPR constexpr +#endif + + +#include + + +struct CountsMovesCopies +{ + consteval CountsMovesCopies() = default; + constexpr CountsMovesCopies(CountsMovesCopies &&other) noexcept { ++other.move_constructed_from; } + constexpr CountsMovesCopies(const CountsMovesCopies &other) noexcept { ++other.copy_constructed_from; } + + constexpr CountsMovesCopies &operator=(CountsMovesCopies &&other) noexcept + { + ++other.move_assigned_from; + return *this; + } + + constexpr CountsMovesCopies &operator=(const CountsMovesCopies &other) noexcept + { + ++other.copy_assigned_from; + return *this; + } + + mutable int move_constructed_from = 0; + mutable int copy_constructed_from = 0; + mutable int move_assigned_from = 0; + mutable int copy_assigned_from = 0; +}; + +struct CopyOnlyType +{ + ~CopyOnlyType() = default; +}; + + +template constexpr auto can_construct_ref() +{ + return requires(T mct) { lefticus::tools::moving_ref{ std::move(mct) }; }; +} + +template constexpr auto can_construct_ref_const() +{ + return requires(const T mct) { lefticus::tools::moving_ref{ std::move(mct) }; }; +} + +template constexpr auto can_assign() +{ + return requires(T lhs, T mct) { lhs = lefticus::tools::moving_ref{ std::move(mct) }; }; +} + + +template constexpr auto can_assign_const() +{ + return requires(T lhs, const T mct) { lhs = lefticus::tools::moving_ref{ std::move(mct) }; }; +} + +template constexpr auto can_construct() +{ + return requires(T mct) { T{ lefticus::tools::moving_ref{ std::move(mct) } }; }; +} + + +template constexpr auto can_construct_const() +{ + return requires(const T mct) { T{ lefticus::tools::moving_ref{ std::move(mct) } }; }; +} + + +TEST_CASE("[forwarding_ref] behaves as expected") +{ + constexpr auto implicit_copy = []() { + CountsMovesCopies cmc; + lefticus::tools::forwarding_ref ref{ cmc }; + [[maybe_unused]] CountsMovesCopies cmc2 = ref; + + return cmc.copy_constructed_from; + }; + + STATIC_REQUIRE(implicit_copy() == 1); + + constexpr auto implicit_copy_as_const = []() { + CountsMovesCopies cmc; + lefticus::tools::forwarding_ref ref{ std::as_const(cmc) }; + [[maybe_unused]] CountsMovesCopies cmc2 = ref; + + return cmc.copy_constructed_from; + }; + + STATIC_REQUIRE(implicit_copy_as_const() == 1); + + constexpr auto implicit_move = []() { + CountsMovesCopies cmc; + lefticus::tools::forwarding_ref ref{ std::move(cmc) }; + [[maybe_unused]] CountsMovesCopies cmc2 = ref; + + return cmc.move_constructed_from; + }; + + STATIC_REQUIRE(implicit_move() == 1); + + constexpr auto implicit_copy_const_rvref = []() { + CountsMovesCopies cmc; + lefticus::tools::forwarding_ref ref{ std::move(std::as_const(cmc)) }; + [[maybe_unused]] CountsMovesCopies cmc2 = ref; + + return cmc.copy_constructed_from; + }; + + STATIC_REQUIRE(implicit_copy_const_rvref() == 1); +} + + +TEST_CASE("[moving_ref] behaves as expected") +{ + constexpr auto implicit_move = []() { + CountsMovesCopies cmc; + lefticus::tools::moving_ref ref{ std::move(cmc) }; + [[maybe_unused]] CountsMovesCopies cmc2 = ref; + + return cmc.move_constructed_from; + }; + + STATIC_REQUIRE(implicit_move() == 1); + + constexpr auto implicit_move_assign = []() { + CountsMovesCopies cmc; + lefticus::tools::moving_ref ref{ std::move(cmc) }; + CountsMovesCopies cmc2; + cmc2 = ref; + + return cmc.move_assigned_from; + }; + + STATIC_REQUIRE(implicit_move_assign() == 1); +} + +TEST_CASE("[moving_ref] traits") +{ + STATIC_REQUIRE(can_construct_ref()); + STATIC_REQUIRE(!can_construct_ref_const()); + STATIC_REQUIRE(can_assign()); + STATIC_REQUIRE(!can_assign_const()); + STATIC_REQUIRE(can_construct()); + STATIC_REQUIRE(!can_construct_const()); + + STATIC_REQUIRE(!can_construct_ref()); + STATIC_REQUIRE(!can_construct_ref_const()); + STATIC_REQUIRE(!can_assign()); + STATIC_REQUIRE(!can_assign_const()); + STATIC_REQUIRE(!can_construct()); + STATIC_REQUIRE(!can_construct_const()); +} diff --git a/test/moving_ref.cpp b/test/moving_ref.cpp deleted file mode 100644 index 718eeb0..0000000 --- a/test/moving_ref.cpp +++ /dev/null @@ -1,134 +0,0 @@ -#include -#include - -#ifdef CATCH_CONFIG_RUNTIME_STATIC_REQUIRE -#define CONSTEXPR -#else -// NOLINTNEXTLINE -#define CONSTEXPR constexpr -#endif - - - -#include - - -struct CountsMovesCopies -{ - constexpr CountsMovesCopies() = default; - constexpr CountsMovesCopies(CountsMovesCopies &&other) noexcept { - ++other.move_constructed_from; - } - constexpr CountsMovesCopies(const CountsMovesCopies &other) noexcept { - ++other.copy_constructed_from; - } - - constexpr CountsMovesCopies &operator=(CountsMovesCopies &&other) noexcept { - ++other.move_assigned_from; - return *this; - } - - constexpr CountsMovesCopies &operator=(const CountsMovesCopies &other) noexcept { - ++other.copy_assigned_from; - return *this; - } - - mutable int move_constructed_from = 0; - mutable int copy_constructed_from = 0; - mutable int move_assigned_from = 0; - mutable int copy_assigned_from = 0; - -}; - -struct CopyOnlyType -{ - ~CopyOnlyType() = default; -}; - - - -template -constexpr auto can_construct_ref() { - return requires(T mct) { - lefticus::moving_ref{std::move(mct)}; - }; -} - -template -constexpr auto can_construct_ref_const() { - return requires(const T mct) { - lefticus::moving_ref{std::move(mct)}; - }; -} - -template -constexpr auto can_assign() { - return requires(T lhs, T mct) { - lhs = lefticus::moving_ref{std::move(mct)}; - }; -} - - -template -constexpr auto can_assign_const() { - return requires(T lhs, const T mct) { - lhs = lefticus::moving_ref{std::move(mct)}; - }; -} - -template -constexpr auto can_construct() { - return requires(T mct) { - T{lefticus::moving_ref{std::move(mct)}}; - }; -} - - -template -constexpr auto can_construct_const() { - return requires(const T mct) { - T{lefticus::moving_ref{std::move(mct)}}; - }; -} - - - -TEST_CASE("[moving_ref] behaves as expected") -{ - constexpr auto implicit_move = [](){ - CountsMovesCopies cmc; - lefticus::moving_ref mr{std::move(cmc)}; - [[maybe_unused]] CountsMovesCopies cmc2 = mr; - - return cmc.move_constructed_from; - }; - - STATIC_REQUIRE(implicit_move() == 1); - - constexpr auto implicit_move_assign = [](){ - CountsMovesCopies cmc; - lefticus::moving_ref mr{std::move(cmc)}; - CountsMovesCopies cmc2; - cmc2 = mr; - - return cmc.move_assigned_from; - }; - - STATIC_REQUIRE(implicit_move_assign() == 1); - - STATIC_REQUIRE(can_construct_ref()); - STATIC_REQUIRE(!can_construct_ref_const()); - STATIC_REQUIRE(can_assign()); - STATIC_REQUIRE(!can_assign_const()); - STATIC_REQUIRE(can_construct()); - STATIC_REQUIRE(!can_construct_const()); - - STATIC_REQUIRE(!can_construct_ref()); - STATIC_REQUIRE(!can_construct_ref_const()); - STATIC_REQUIRE(!can_assign()); - STATIC_REQUIRE(!can_assign_const()); - STATIC_REQUIRE(!can_construct()); - STATIC_REQUIRE(!can_construct_const()); - - -} diff --git a/test/static_views_tests.cpp b/test/static_views_tests.cpp index d2896c3..e3bdbd0 100644 --- a/test/static_views_tests.cpp +++ b/test/static_views_tests.cpp @@ -138,7 +138,7 @@ template using map = lefticus::tools::simple_stack_flat_map;// NOLINT Magic Number using string = lefticus::tools::simple_stack_string<16>;// NOLINT Magic Number -TEST_CASE("[minimized_stackify] works")// NOLINT (cognitive complexity) +TEST_CASE("[minimized_stackify] works for maps")// NOLINT (cognitive complexity) { using namespace std::string_view_literals; const auto make_data = []() {