From 6fe27c8ccde3783d7c6e138d8105a2b9eb8d17d6 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Wed, 18 May 2022 07:51:43 -0600 Subject: [PATCH 1/7] Start adding more things --- .../lefticus/tools/polymorphic_variant.hpp | 74 +++++++++++++++++++ include/lefticus/tools/type_list.hpp | 47 ++++++++++++ include/lefticus/tools/unique_ptr.hpp | 61 +++++++++++++++ test/static_views_tests.cpp | 2 +- 4 files changed, 183 insertions(+), 1 deletion(-) create mode 100644 include/lefticus/tools/polymorphic_variant.hpp create mode 100644 include/lefticus/tools/type_list.hpp create mode 100644 include/lefticus/tools/unique_ptr.hpp 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/type_list.hpp b/include/lefticus/tools/type_list.hpp new file mode 100644 index 0000000..0dc9d93 --- /dev/null +++ b/include/lefticus/tools/type_list.hpp @@ -0,0 +1,47 @@ +#ifndef LEFTICUS_TOOLS_TYPE_LIST_HPP +#define LEFTICUS_TOOLS_TYPE_LIST_HPP + +namespace lefticus::tools { + +template +struct Type_List {}; + +template +constexpr auto filter_impl(Filter filter, Type_List, + Type_List result) { + if constexpr (filter.template operator()()) { + if constexpr (sizeof...(Rest) > 0) { + return filter_impl(filter, Type_List{}, Type_List{}); + } else { + return Type_List{}; + } + } else { + if constexpr (sizeof...(Rest) > 0) { + return filter_impl(filter, Type_List{}, result); + } else { + return result; + } + } +} + +template +constexpr auto filter(Filter filter, Type_List input) { + return filter_impl(filter, input, Type_List<>{}); +} + +template +constexpr std::variant variant_type(Type_List); + + +template +constexpr inline bool contains_type_v = (std::is_same_v || ...); + +template +constexpr inline bool contains_type_v> = + (std::is_same_v || ...); + + +} + + +#endif diff --git a/include/lefticus/tools/unique_ptr.hpp b/include/lefticus/tools/unique_ptr.hpp new file mode 100644 index 0000000..c713ec2 --- /dev/null +++ b/include/lefticus/tools/unique_ptr.hpp @@ -0,0 +1,61 @@ +#ifndef LEFTICUS_TOOLS_UNIQUE_PTR_HPP +#define LEFTICUS_TOOLS_UNIQUE_PTR_HPP + +namespace lefticus::tools { +#include +#include + +template class unique_ptr +{ + using pointer = Contained *; + using deleter_type = unique_ptr; + using element_type = Contained; + + constexpr unique_ptr() = default; + constexpr unique_ptr(std::nullptr_t) {} + constexpr explicit unique_ptr(pointer p) : data(p) {} + + unique_ptr(const unique_ptr &) = delete; + constexpr unique_ptr(unique_ptr &&other) : data{ std::exchange(other.data, nullptr) } {} + + unique_ptr &operator=(const unique_ptr &) = delete; + constexpr unique_ptr &operator=(unique_ptr &&other) { reset(std::exchange(other.data, nullptr)); } + + constexpr void reset(pointer p = pointer()) noexcept { delete std::exchange(data, p); } + + constexpr void reset(std::nullptr_t) noexcept { reset(); } + + constexpr deleter_type &get_deleter() noexcept { return *this; } + + constexpr const deleter_type &get_deleter() const noexcept { return *this; } + + constexpr pointer release() { return std::exchange(data, nullptr); } + + constexpr ~unique_ptr() { reset(); } + + constexpr void swap(unique_ptr &other) noexcept { std::swap(this->data, other.data); } + + constexpr pointer get() const noexcept { return data; } + + constexpr explicit operator bool() const noexcept { return data != nullptr; } + + constexpr pointer operator->() const noexcept { return data; } + + constexpr std::add_lvalue_reference_t operator*() const noexcept(noexcept(*std::declval())) + { + return *data; + } + +private: + Contained *data = nullptr; +}; + +template unique_ptr make_unique(Param &&...param) +{ + return unique_ptr(new Type(std::forward(param)...)); +} + +}// namespace lefticus::tools + + +#endif diff --git a/test/static_views_tests.cpp b/test/static_views_tests.cpp index fff142c..bc89127 100644 --- a/test/static_views_tests.cpp +++ b/test/static_views_tests.cpp @@ -135,7 +135,7 @@ template using vector = lefticus::tools::simple_stack_vector; template using map = lefticus::tools::simple_stack_flat_map; using string = lefticus::tools::simple_stack_string<16>; -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 = []() { From f15553d3d3efa814d5ad8dc09227fa5f06485570 Mon Sep 17 00:00:00 2001 From: Clang Robot Date: Mon, 18 Mar 2024 15:02:00 +0000 Subject: [PATCH 2/7] :art: Committing clang-format changes --- include/lefticus/tools/strong_types.hpp | 77 +++++++++++++++---------- include/lefticus/tools/type_lists.hpp | 15 ++--- 2 files changed, 51 insertions(+), 41 deletions(-) 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)); From fce80a6919eceeb09bdd95472ee40af0cfa20e50 Mon Sep 17 00:00:00 2001 From: Clang Robot Date: Mon, 18 Mar 2024 15:02:07 +0000 Subject: [PATCH 3/7] :art: Committing clang-format changes --- include/lefticus/tools/strong_types.hpp | 77 +++++++++++++++---------- include/lefticus/tools/type_lists.hpp | 15 ++--- 2 files changed, 51 insertions(+), 41 deletions(-) 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)); From 4ca9d43db9bf0710ffbdad4a487037ddea2cf196 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Sat, 7 Mar 2026 09:13:59 -0700 Subject: [PATCH 4/7] Get compiling in C++26 mode, add forwarding_ref and more tests --- CMakeLists.txt | 2 +- include/lefticus/tools/forwarding_ref.hpp | 65 ++++++++++++++ include/lefticus/tools/has_move_ctor.hpp | 59 ++++++++++++ include/lefticus/tools/lifetimebound.hpp | 42 +++++++++ include/lefticus/tools/moving_ref.hpp | 89 +++++++++++-------- include/lefticus/tools/non_promoting_ints.hpp | 16 ++-- test/CMakeLists.txt | 4 +- ...ving_ref.cpp => moving_forwarding_ref.cpp} | 71 ++++++++++++--- 8 files changed, 289 insertions(+), 59 deletions(-) create mode 100644 include/lefticus/tools/forwarding_ref.hpp create mode 100644 include/lefticus/tools/has_move_ctor.hpp create mode 100644 include/lefticus/tools/lifetimebound.hpp rename test/{moving_ref.cpp => moving_forwarding_ref.cpp} (59%) 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..6a95228 --- /dev/null +++ b/include/lefticus/tools/forwarding_ref.hpp @@ -0,0 +1,65 @@ +/* +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 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)"); + + constexpr forwarding_ref(reference_type ref_ LIFETIMEBOUND) noexcept + : ref{&ref_} {} + 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..abb701a --- /dev/null +++ b/include/lefticus/tools/has_move_ctor.hpp @@ -0,0 +1,59 @@ +/* +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; + +} + +#endif diff --git a/include/lefticus/tools/lifetimebound.hpp b/include/lefticus/tools/lifetimebound.hpp new file mode 100644 index 0000000..d38625d --- /dev/null +++ b/include/lefticus/tools/lifetimebound.hpp @@ -0,0 +1,42 @@ +/* +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..3cba6c3 100644 --- a/include/lefticus/tools/moving_ref.hpp +++ b/include/lefticus/tools/moving_ref.hpp @@ -1,53 +1,70 @@ -#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 +*/ -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; -#endif +#ifndef LEFTICUS_TOOLS_MOVING_REF_HPP +#define LEFTICUS_TOOLS_MOVING_REF_HPP +#include "lifetimebound.hpp" +#include "has_move_ctor.hpp" +namespace lefticus::tools { -#ifdef __cpp_concepts -template -#else template -#endif + concept movable = std::is_move_constructible_v && + std::is_move_assignable_v && + not std::is_const_v && not std::is_pointer_v && not std::is_reference_v; +template requires movable && has_move_ctor struct moving_ref { - constexpr moving_ref(T &&val) : ref{std::move(val)} {} - constexpr operator T&&() { - return std::move(ref); - } - T &&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)"); + + constexpr moving_ref(reference_type ref_ LIFETIMEBOUND) noexcept + : ref{&ref_} {} + + moving_ref(const T&) = + delete ("this type is only to be used with rvalue reference parameters"); + + 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/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_ref.cpp b/test/moving_forwarding_ref.cpp similarity index 59% rename from test/moving_ref.cpp rename to test/moving_forwarding_ref.cpp index 718eeb0..f1844c3 100644 --- a/test/moving_ref.cpp +++ b/test/moving_forwarding_ref.cpp @@ -1,5 +1,6 @@ #include #include +#include #ifdef CATCH_CONFIG_RUNTIME_STATIC_REQUIRE #define CONSTEXPR @@ -15,7 +16,7 @@ struct CountsMovesCopies { - constexpr CountsMovesCopies() = default; + consteval CountsMovesCopies() = default; constexpr CountsMovesCopies(CountsMovesCopies &&other) noexcept { ++other.move_constructed_from; } @@ -50,21 +51,21 @@ struct CopyOnlyType template constexpr auto can_construct_ref() { return requires(T mct) { - lefticus::moving_ref{std::move(mct)}; + lefticus::tools::moving_ref{std::move(mct)}; }; } template constexpr auto can_construct_ref_const() { return requires(const T mct) { - lefticus::moving_ref{std::move(mct)}; + lefticus::tools::moving_ref{std::move(mct)}; }; } template constexpr auto can_assign() { return requires(T lhs, T mct) { - lhs = lefticus::moving_ref{std::move(mct)}; + lhs = lefticus::tools::moving_ref{std::move(mct)}; }; } @@ -72,14 +73,14 @@ constexpr auto can_assign() { template constexpr auto can_assign_const() { return requires(T lhs, const T mct) { - lhs = lefticus::moving_ref{std::move(mct)}; + lhs = lefticus::tools::moving_ref{std::move(mct)}; }; } template constexpr auto can_construct() { return requires(T mct) { - T{lefticus::moving_ref{std::move(mct)}}; + T{lefticus::tools::moving_ref{std::move(mct)}}; }; } @@ -87,18 +88,61 @@ constexpr auto can_construct() { template constexpr auto can_construct_const() { return requires(const T mct) { - T{lefticus::moving_ref{std::move(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::moving_ref mr{std::move(cmc)}; - [[maybe_unused]] CountsMovesCopies cmc2 = mr; + lefticus::tools::moving_ref ref{std::move(cmc)}; + [[maybe_unused]] CountsMovesCopies cmc2 = ref; return cmc.move_constructed_from; }; @@ -107,15 +151,18 @@ TEST_CASE("[moving_ref] behaves as expected") constexpr auto implicit_move_assign = [](){ CountsMovesCopies cmc; - lefticus::moving_ref mr{std::move(cmc)}; + lefticus::tools::moving_ref ref{std::move(cmc)}; CountsMovesCopies cmc2; - cmc2 = mr; + 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()); @@ -129,6 +176,4 @@ TEST_CASE("[moving_ref] behaves as expected") STATIC_REQUIRE(!can_assign_const()); STATIC_REQUIRE(!can_construct()); STATIC_REQUIRE(!can_construct_const()); - - } From 656dea0f90235af036e5f6253deca6a869fe5849 Mon Sep 17 00:00:00 2001 From: Clang Robot Date: Sun, 8 Mar 2026 00:27:33 +0000 Subject: [PATCH 5/7] :art: Committing clang-format changes --- include/lefticus/tools/forwarding_ref.hpp | 28 +++--- include/lefticus/tools/has_move_ctor.hpp | 17 ++-- include/lefticus/tools/lifetimebound.hpp | 1 - include/lefticus/tools/moving_ref.hpp | 36 ++++---- include/lefticus/tools/strong_types.hpp | 77 +++++++++------- include/lefticus/tools/type_lists.hpp | 15 ++-- test/moving_forwarding_ref.cpp | 103 +++++++++------------- 7 files changed, 133 insertions(+), 144 deletions(-) diff --git a/include/lefticus/tools/forwarding_ref.hpp b/include/lefticus/tools/forwarding_ref.hpp index 6a95228..daf598b 100644 --- a/include/lefticus/tools/forwarding_ref.hpp +++ b/include/lefticus/tools/forwarding_ref.hpp @@ -35,31 +35,29 @@ For more information, please refer to namespace lefticus::tools { -template -struct forwarding_ref { +template struct 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)"); + 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)"); - constexpr forwarding_ref(reference_type ref_ LIFETIMEBOUND) noexcept - : ref{&ref_} {} - constexpr operator reference_type() noexcept LIFETIMEBOUND { - return static_cast(*ref); - } + constexpr forwarding_ref(reference_type ref_ LIFETIMEBOUND) noexcept : ref{ &ref_ } {} + constexpr operator reference_type() noexcept LIFETIMEBOUND { return static_cast(*ref); } - private: +private: pointer_type ref; }; -template -forwarding_ref(T&&) -> forwarding_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 index abb701a..dedc2a2 100644 --- a/include/lefticus/tools/has_move_ctor.hpp +++ b/include/lefticus/tools/has_move_ctor.hpp @@ -33,14 +33,15 @@ For more information, please refer to namespace lefticus::tools { -template -struct DetectMoveConstruction { - operator P const&(); - operator P&&(); +template struct DetectMoveConstruction +{ + operator P const &(); + operator P &&(); }; -template -concept copyable_xor_moveable = requires(T t, DetectMoveConstruction m) { +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 @@ -51,9 +52,9 @@ concept copyable_xor_moveable = requires(T t, DetectMoveConstruction m) { t = m; }; -template +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 index d38625d..8a99ff5 100644 --- a/include/lefticus/tools/lifetimebound.hpp +++ b/include/lefticus/tools/lifetimebound.hpp @@ -30,7 +30,6 @@ For more information, please refer to #define LEFTICUS_TOOLS_LIFETIMEBOUND_HPP - #if defined(__has_cpp_attribute) #if __has_cpp_attribute(clang::lifetimebound) #define LIFETIMEBOUND [[clang::lifetimebound]] diff --git a/include/lefticus/tools/moving_ref.hpp b/include/lefticus/tools/moving_ref.hpp index 3cba6c3..00b7077 100644 --- a/include/lefticus/tools/moving_ref.hpp +++ b/include/lefticus/tools/moving_ref.hpp @@ -29,42 +29,40 @@ For more information, please refer to #ifndef LEFTICUS_TOOLS_MOVING_REF_HPP #define LEFTICUS_TOOLS_MOVING_REF_HPP -#include "lifetimebound.hpp" #include "has_move_ctor.hpp" +#include "lifetimebound.hpp" namespace lefticus::tools { template - concept movable = std::is_move_constructible_v && - std::is_move_assignable_v && - not std::is_const_v && not std::is_pointer_v && not std::is_reference_v; +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 requires movable && has_move_ctor -struct moving_ref { +template +requires movable && has_move_ctor +struct 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)"); + 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)"); - constexpr moving_ref(reference_type ref_ LIFETIMEBOUND) noexcept - : ref{&ref_} {} + constexpr moving_ref(reference_type ref_ LIFETIMEBOUND) noexcept : ref{ &ref_ } {} - moving_ref(const T&) = - delete ("this type is only to be used with rvalue reference parameters"); + moving_ref(const T &) = delete ("this type is only to be used with rvalue reference parameters"); - constexpr operator reference_type() noexcept LIFETIMEBOUND { - return static_cast(*ref); - } + constexpr operator reference_type() noexcept LIFETIMEBOUND { return static_cast(*ref); } - private: +private: pointer_type ref; }; }// 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/moving_forwarding_ref.cpp b/test/moving_forwarding_ref.cpp index f1844c3..e9f9024 100644 --- a/test/moving_forwarding_ref.cpp +++ b/test/moving_forwarding_ref.cpp @@ -1,6 +1,6 @@ #include -#include #include +#include #ifdef CATCH_CONFIG_RUNTIME_STATIC_REQUIRE #define CONSTEXPR @@ -10,26 +10,23 @@ #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(CountsMovesCopies &&other) noexcept { ++other.move_constructed_from; } + constexpr CountsMovesCopies(const CountsMovesCopies &other) noexcept { ++other.copy_constructed_from; } - constexpr CountsMovesCopies &operator=(CountsMovesCopies &&other) noexcept { + constexpr CountsMovesCopies &operator=(CountsMovesCopies &&other) noexcept + { ++other.move_assigned_from; return *this; } - constexpr CountsMovesCopies &operator=(const CountsMovesCopies &other) noexcept { + constexpr CountsMovesCopies &operator=(const CountsMovesCopies &other) noexcept + { ++other.copy_assigned_from; return *this; } @@ -38,7 +35,6 @@ struct CountsMovesCopies mutable int copy_constructed_from = 0; mutable int move_assigned_from = 0; mutable int copy_assigned_from = 0; - }; struct CopyOnlyType @@ -47,88 +43,75 @@ struct CopyOnlyType }; - -template -constexpr auto can_construct_ref() { - return requires(T mct) { - lefticus::tools::moving_ref{std::move(mct)}; - }; +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_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() +{ + 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_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() +{ + 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)}}; - }; +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 = [](){ + constexpr auto implicit_copy = []() { CountsMovesCopies cmc; - lefticus::tools::forwarding_ref ref{cmc}; - [[maybe_unused]] CountsMovesCopies cmc2 = ref; + 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 = [](){ + constexpr auto implicit_copy_as_const = []() { CountsMovesCopies cmc; - lefticus::tools::forwarding_ref ref{std::as_const(cmc)}; - [[maybe_unused]] CountsMovesCopies cmc2 = ref; + 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 = [](){ + constexpr auto implicit_move = []() { CountsMovesCopies cmc; - lefticus::tools::forwarding_ref ref{std::move(cmc)}; - [[maybe_unused]] CountsMovesCopies cmc2 = ref; + 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 = [](){ + constexpr auto implicit_copy_const_rvref = []() { CountsMovesCopies cmc; - lefticus::tools::forwarding_ref ref{std::move(std::as_const(cmc))}; - [[maybe_unused]] CountsMovesCopies cmc2 = ref; + lefticus::tools::forwarding_ref ref{ std::move(std::as_const(cmc)) }; + [[maybe_unused]] CountsMovesCopies cmc2 = ref; return cmc.copy_constructed_from; }; @@ -139,21 +122,21 @@ TEST_CASE("[forwarding_ref] behaves as expected") TEST_CASE("[moving_ref] behaves as expected") { - constexpr auto implicit_move = [](){ + constexpr auto implicit_move = []() { CountsMovesCopies cmc; - lefticus::tools::moving_ref ref{std::move(cmc)}; - [[maybe_unused]] CountsMovesCopies cmc2 = ref; + 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 = [](){ + constexpr auto implicit_move_assign = []() { CountsMovesCopies cmc; - lefticus::tools::moving_ref ref{std::move(cmc)}; + lefticus::tools::moving_ref ref{ std::move(cmc) }; CountsMovesCopies cmc2; - cmc2 = ref; + cmc2 = ref; return cmc.move_assigned_from; }; From 8fbd818f5168811952158e4a7a5bb285915fb600 Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Sat, 7 Mar 2026 17:38:58 -0700 Subject: [PATCH 6/7] make constructors explicit/nodiscard --- include/lefticus/tools/forwarding_ref.hpp | 6 +++--- include/lefticus/tools/moving_ref.hpp | 6 +++--- 2 files changed, 6 insertions(+), 6 deletions(-) diff --git a/include/lefticus/tools/forwarding_ref.hpp b/include/lefticus/tools/forwarding_ref.hpp index daf598b..e88b6a2 100644 --- a/include/lefticus/tools/forwarding_ref.hpp +++ b/include/lefticus/tools/forwarding_ref.hpp @@ -35,7 +35,7 @@ For more information, please refer to namespace lefticus::tools { -template struct forwarding_ref +template struct [[nodiscard]] forwarding_ref { using pointer_type = std::add_pointer_t>; using reference_type = T; @@ -48,8 +48,8 @@ template struct forwarding_ref "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)"); - constexpr forwarding_ref(reference_type ref_ LIFETIMEBOUND) noexcept : ref{ &ref_ } {} - constexpr operator reference_type() noexcept LIFETIMEBOUND { return static_cast(*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; diff --git a/include/lefticus/tools/moving_ref.hpp b/include/lefticus/tools/moving_ref.hpp index 00b7077..a435dcf 100644 --- a/include/lefticus/tools/moving_ref.hpp +++ b/include/lefticus/tools/moving_ref.hpp @@ -40,7 +40,7 @@ concept movable = std::is_move_constructible_v && std::is_move_assignable_v requires movable && has_move_ctor -struct moving_ref +struct [[nodiscard]] moving_ref { using value_type = T; using pointer_type = std::add_pointer_t; @@ -53,11 +53,11 @@ struct moving_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)"); - constexpr moving_ref(reference_type ref_ LIFETIMEBOUND) noexcept : ref{ &ref_ } {} + [[nodiscard]] explicit constexpr moving_ref(reference_type ref_ LIFETIMEBOUND) noexcept : ref{ &ref_ } {} moving_ref(const T &) = delete ("this type is only to be used with rvalue reference parameters"); - constexpr operator reference_type() noexcept LIFETIMEBOUND { return static_cast(*ref); } + [[nodiscard]] constexpr operator reference_type() noexcept LIFETIMEBOUND { return static_cast(*ref); } private: pointer_type ref; From 68d606e5900dba82e4ecfdd3d9e12cd900d1870d Mon Sep 17 00:00:00 2001 From: Jason Turner Date: Sat, 7 Mar 2026 17:49:56 -0700 Subject: [PATCH 7/7] Remove redundant code --- include/lefticus/tools/type_list.hpp | 47 --------------------- include/lefticus/tools/unique_ptr.hpp | 61 --------------------------- 2 files changed, 108 deletions(-) delete mode 100644 include/lefticus/tools/type_list.hpp delete mode 100644 include/lefticus/tools/unique_ptr.hpp diff --git a/include/lefticus/tools/type_list.hpp b/include/lefticus/tools/type_list.hpp deleted file mode 100644 index 0dc9d93..0000000 --- a/include/lefticus/tools/type_list.hpp +++ /dev/null @@ -1,47 +0,0 @@ -#ifndef LEFTICUS_TOOLS_TYPE_LIST_HPP -#define LEFTICUS_TOOLS_TYPE_LIST_HPP - -namespace lefticus::tools { - -template -struct Type_List {}; - -template -constexpr auto filter_impl(Filter filter, Type_List, - Type_List result) { - if constexpr (filter.template operator()()) { - if constexpr (sizeof...(Rest) > 0) { - return filter_impl(filter, Type_List{}, Type_List{}); - } else { - return Type_List{}; - } - } else { - if constexpr (sizeof...(Rest) > 0) { - return filter_impl(filter, Type_List{}, result); - } else { - return result; - } - } -} - -template -constexpr auto filter(Filter filter, Type_List input) { - return filter_impl(filter, input, Type_List<>{}); -} - -template -constexpr std::variant variant_type(Type_List); - - -template -constexpr inline bool contains_type_v = (std::is_same_v || ...); - -template -constexpr inline bool contains_type_v> = - (std::is_same_v || ...); - - -} - - -#endif diff --git a/include/lefticus/tools/unique_ptr.hpp b/include/lefticus/tools/unique_ptr.hpp deleted file mode 100644 index c713ec2..0000000 --- a/include/lefticus/tools/unique_ptr.hpp +++ /dev/null @@ -1,61 +0,0 @@ -#ifndef LEFTICUS_TOOLS_UNIQUE_PTR_HPP -#define LEFTICUS_TOOLS_UNIQUE_PTR_HPP - -namespace lefticus::tools { -#include -#include - -template class unique_ptr -{ - using pointer = Contained *; - using deleter_type = unique_ptr; - using element_type = Contained; - - constexpr unique_ptr() = default; - constexpr unique_ptr(std::nullptr_t) {} - constexpr explicit unique_ptr(pointer p) : data(p) {} - - unique_ptr(const unique_ptr &) = delete; - constexpr unique_ptr(unique_ptr &&other) : data{ std::exchange(other.data, nullptr) } {} - - unique_ptr &operator=(const unique_ptr &) = delete; - constexpr unique_ptr &operator=(unique_ptr &&other) { reset(std::exchange(other.data, nullptr)); } - - constexpr void reset(pointer p = pointer()) noexcept { delete std::exchange(data, p); } - - constexpr void reset(std::nullptr_t) noexcept { reset(); } - - constexpr deleter_type &get_deleter() noexcept { return *this; } - - constexpr const deleter_type &get_deleter() const noexcept { return *this; } - - constexpr pointer release() { return std::exchange(data, nullptr); } - - constexpr ~unique_ptr() { reset(); } - - constexpr void swap(unique_ptr &other) noexcept { std::swap(this->data, other.data); } - - constexpr pointer get() const noexcept { return data; } - - constexpr explicit operator bool() const noexcept { return data != nullptr; } - - constexpr pointer operator->() const noexcept { return data; } - - constexpr std::add_lvalue_reference_t operator*() const noexcept(noexcept(*std::declval())) - { - return *data; - } - -private: - Contained *data = nullptr; -}; - -template unique_ptr make_unique(Param &&...param) -{ - return unique_ptr(new Type(std::forward(param)...)); -} - -}// namespace lefticus::tools - - -#endif