From 539fcaeedba90e12d7e81eff6cf1c89e459bab29 Mon Sep 17 00:00:00 2001 From: Tsche Date: Wed, 31 Dec 2025 01:04:14 +0000 Subject: [PATCH 01/14] verify enum, string_view, span, format, expect --- CMakeLists.txt | 6 ++-- conanfile.py | 2 +- include/rsl/_impl/format/fmt_parser.hpp | 6 ++-- include/rsl/meta_traits | 20 ++++++++--- include/rsl/serializer/repr.hpp | 2 +- include/rsl/string_view | 1 + test/CMakeLists.txt | 17 ++++----- test/span/span.cpp | 47 +++++++++++++++---------- 8 files changed, 62 insertions(+), 39 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index b73cd92..7428c73 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,9 +15,9 @@ add_library(rsl::util ALIAS rsl-util) target_compile_features(rsl-util INTERFACE cxx_std_26) if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - target_compile_options(rsl-util INTERFACE "-stdlib=libc++" "-freflection-latest") - target_link_options(rsl-util INTERFACE "-stdlib=libc++") - target_link_libraries(rsl-util INTERFACE "c++abi") + # target_compile_options(rsl-util INTERFACE "-stdlib=libc++" "-freflection-latest") + # target_link_options(rsl-util INTERFACE "-stdlib=libc++") + # target_link_libraries(rsl-util INTERFACE "c++abi") elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") target_compile_options(rsl-util INTERFACE "-freflection") endif() diff --git a/conanfile.py b/conanfile.py index 19024ff..f7ead05 100644 --- a/conanfile.py +++ b/conanfile.py @@ -25,7 +25,7 @@ class RslUtilRecipe(ConanFile): exports_sources = "CMakeLists.txt", "include/*", "test/*", "cmake/*" def requirements(self): if self.options.tests: - self.requires("gtest/1.14.0") + self.requires("gtest/1.17.0") def layout(self): cmake_layout(self) diff --git a/include/rsl/_impl/format/fmt_parser.hpp b/include/rsl/_impl/format/fmt_parser.hpp index 5e5b9de..1d2ff8b 100644 --- a/include/rsl/_impl/format/fmt_parser.hpp +++ b/include/rsl/_impl/format/fmt_parser.hpp @@ -100,14 +100,14 @@ struct Replacement final : _impl::Parser { using _impl::Parser::Parser; - constexpr void add_style(FormatString& out, bool enable) const { + consteval void add_style(FormatString& out, bool enable) const { if (!style_tags.empty()) { out.string += style_separator; out.style_tags.emplace_back(style_tags, enable); } } - constexpr void render(FormatString& out) const { + consteval void render(FormatString& out) const { if (kind == ReplacementType::invalid) { // todo emit error? return; @@ -223,7 +223,7 @@ struct FormatParser : _impl::Parser { FormatString result; - constexpr void push_text(int start, int end) { result.string += data.substr(start, end - start); } + consteval void push_text(int start, int end) { result.string += data.substr(start, end - start); } consteval static std::meta::info get_arg_accessor(std::size_t index) { return substitute(^^Accessor, {std::meta::reflect_constant(index)}); diff --git a/include/rsl/meta_traits b/include/rsl/meta_traits index 352e4eb..df7a16f 100644 --- a/include/rsl/meta_traits +++ b/include/rsl/meta_traits @@ -1,6 +1,7 @@ #pragma once #include #include +#include namespace rsl::meta { @@ -23,14 +24,25 @@ consteval bool is_nonstatic_member_function(std::meta::info R) { return is_member_function(R) && !is_static_member(R); } +namespace _compat { +consteval std::vector annotations_of_with_type(std::meta::info r, + std::meta::info t) { +#if $compiler_is(CLANG) + return std::meta::annotations_of(r, t); +#else + return std::meta::annotations_of_with_type(r, t); +#endif +} +} // namespace _compat + template consteval bool has_annotation(std::meta::info item) { - return !annotations_of(item, ^^T).empty(); + return !_compat::annotations_of_with_type(item, ^^T).empty(); } consteval bool has_annotation(std::meta::info item, std::meta::info type) { if (is_type(type)) { - return !annotations_of(item, type).empty(); + return !_compat::annotations_of_with_type(item, type).empty(); } else if (is_template(type)) { for (auto annotation : annotations_of(item)) { if (has_template_arguments(type_of(annotation)) && template_of(type_of(annotation)) == type) { @@ -46,8 +58,8 @@ consteval bool has_annotation(std::meta::info item, T const& value) { if (!has_annotation(item)) { return false; } - auto annotations = annotations_of(item, dealias(^^T)); - auto value_r = std::meta::reflect_value(value); + auto annotations = _compat::annotations_of_with_type(item, dealias(^^T)); + auto value_r = std::meta::reflect_constant(value); return std::ranges::any_of(annotations, [&](auto annotation) { return annotation == value_r; }); } diff --git a/include/rsl/serializer/repr.hpp b/include/rsl/serializer/repr.hpp index b96ecfc..c01daf3 100644 --- a/include/rsl/serializer/repr.hpp +++ b/include/rsl/serializer/repr.hpp @@ -380,7 +380,7 @@ consteval std::string get_type_name(NameMode mode) { } if constexpr (is_enumerable_type(^^T) && rsl::meta::has_annotation>(^^T)) { - return ret + [:constant_of(annotations_of(^^T, ^^preferred_name<_impl::Annotated>)[0]):].value; + return ret + [:constant_of(meta::_compat::annotations_of_with_type(^^T, ^^preferred_name<_impl::Annotated>)[0]):].value; } if constexpr (requires { { T::preferred_name } -> std::convertible_to; diff --git a/include/rsl/string_view b/include/rsl/string_view index f01c7b3..b35d465 100644 --- a/include/rsl/string_view +++ b/include/rsl/string_view @@ -359,6 +359,7 @@ inline namespace string_view_literals { // [string.view.literals], suffix for basic_string_view literals #pragma clang diagnostic push #pragma clang diagnostic ignored "-Wuser-defined-literals" + constexpr string_view operator""sv(const char* str, size_t len) noexcept { return {str, len}; } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ab5d7ef..e4db0ca 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,14 +1,15 @@ target_sources(rsl-util-test PRIVATE main.cpp) target_include_directories(rsl-util-test PRIVATE ${CMAKE_CURRENT_LIST_DIR}) -add_subdirectory(variant) -add_subdirectory(tagged_variant) -add_subdirectory(tuple) -add_subdirectory(expect) -add_subdirectory(serializer) +add_subdirectory(enum) add_subdirectory(string_view) add_subdirectory(span) add_subdirectory(format) -add_subdirectory(kwargs) -add_subdirectory(enum) -add_subdirectory(trie) \ No newline at end of file +add_subdirectory(expect) + +# add_subdirectory(kwargs) +# add_subdirectory(variant) +# add_subdirectory(tagged_variant) +# add_subdirectory(tuple) +# add_subdirectory(serializer) +# add_subdirectory(trie) \ No newline at end of file diff --git a/test/span/span.cpp b/test/span/span.cpp index 3ab93cb..96fcd71 100644 --- a/test/span/span.cpp +++ b/test/span/span.cpp @@ -3,6 +3,14 @@ #include +namespace { +struct Foo { + int a; + int b; + char c; +}; +} // namespace + TEST(span, construct) { constexpr int ca[]{0, 1, 2, 3, 4, 5, 6, 7, 8}; rsl::span span(ca); @@ -13,7 +21,7 @@ TEST(span, construct) { rsl::span spanSizedFromArray(arr.begin(), 3); constexpr auto objects = - define_static_array(members_of(^^::, std::meta::access_context::current())); + define_static_array(nonstatic_data_members_of(^^Foo, std::meta::access_context::current())); constexpr rsl::span spanOfInfo(objects); constexpr rsl::span spanOfInfoSized(objects.data(), 3); @@ -28,7 +36,7 @@ TEST(span, Empty) { ASSERT_TRUE(!span.empty()); constexpr auto spanOfInfo = - rsl::span{define_static_array(members_of(^^::, std::meta::access_context::current()))}; + rsl::span{define_static_array(nonstatic_data_members_of(^^Foo, std::meta::access_context::current()))}; static_assert(!spanOfInfo.empty()); } @@ -44,7 +52,7 @@ TEST(span, Size) { ASSERT_EQ(spanWithSize.size(), 3); constexpr auto spanOfInfo = - rsl::span{define_static_array(members_of(^^::, std::meta::access_context::current()))}; + rsl::span{define_static_array(nonstatic_data_members_of(^^Foo, std::meta::access_context::current()))}; static_assert(spanOfInfo.size() > 1); } @@ -94,10 +102,10 @@ TEST(span, At) { { constexpr auto spanOfInfo = - rsl::span{define_static_array(members_of(^^::, std::meta::access_context::current()))}; - static_assert(spanOfInfo.at(1) == members_of(^^::, std::meta::access_context::current())[1]); - static_assert(spanOfInfo.at(2) != members_of(^^::, std::meta::access_context::current())[1]); - static_assert(spanOfInfo.at(2) == members_of(^^::, std::meta::access_context::current())[2]); + rsl::span{define_static_array(nonstatic_data_members_of(^^Foo, std::meta::access_context::current()))}; + static_assert(spanOfInfo.at(1) == nonstatic_data_members_of(^^Foo, std::meta::access_context::current())[1]); + static_assert(spanOfInfo.at(2) != nonstatic_data_members_of(^^Foo, std::meta::access_context::current())[1]); + static_assert(spanOfInfo.at(2) == nonstatic_data_members_of(^^Foo, std::meta::access_context::current())[2]); } } @@ -118,7 +126,7 @@ TEST(span, back) { { constexpr auto members = - define_static_array(members_of(^^::, std::meta::access_context::current())); + define_static_array(nonstatic_data_members_of(^^Foo, std::meta::access_context::current())); constexpr auto spanOfInfo = rsl::span{members}; static_assert(spanOfInfo.back() == members[members.size() - 1]); } @@ -141,7 +149,7 @@ TEST(span, front) { { constexpr auto members = - define_static_array(members_of(^^::, std::meta::access_context::current())); + define_static_array(nonstatic_data_members_of(^^Foo, std::meta::access_context::current())); constexpr auto spanOfInfo = rsl::span{members}; static_assert(spanOfInfo.front() == members[0]); } @@ -164,7 +172,7 @@ TEST(span, size_bytes) { { constexpr auto members = - define_static_array(members_of(^^::, std::meta::access_context::current())); + define_static_array(nonstatic_data_members_of(^^Foo, std::meta::access_context::current())); constexpr auto spanOfInfo = rsl::span{members}; static_assert(spanOfInfo.size_bytes() == members.size() * sizeof(std::meta::info)); } @@ -193,7 +201,7 @@ TEST(span, first) { { constexpr auto members = - define_static_array(members_of(^^::, std::meta::access_context::current())); + define_static_array(nonstatic_data_members_of(^^Foo, std::meta::access_context::current())); constexpr auto spanOfInfo = rsl::span{members}; static_assert(spanOfInfo.first<3>().size() == 3); } @@ -222,7 +230,7 @@ TEST(span, last) { { constexpr auto members = - define_static_array(members_of(^^::, std::meta::access_context::current())); + define_static_array(nonstatic_data_members_of(^^Foo, std::meta::access_context::current())); constexpr auto spanOfInfo = rsl::span{members}; static_assert(spanOfInfo.last<3>().size() == 3); } @@ -266,7 +274,7 @@ TEST(span, subspan) { { constexpr auto members = - define_static_array(members_of(^^::, std::meta::access_context::current())); + define_static_array(nonstatic_data_members_of(^^Foo, std::meta::access_context::current())); constexpr auto spanOfInfo = rsl::span{members}; constexpr auto subspanWithSize = spanOfInfo.subspan<1, 2>(); @@ -279,9 +287,9 @@ TEST(span, subspan) { } } +namespace { template - requires std::same_as -void check() { +void check1() { static_assert(spanArgument.size() == 3); static_assert(spanArgument[0] == 1); static_assert(spanArgument[1] == 2); @@ -289,20 +297,21 @@ void check() { } template -void check() { +void check2() { static_assert(spanArgument.size() == 3); } +} // namespace TEST(span, asTemplateArgument) { { constexpr rsl::span arrSpan{std::define_static_array(std::array{1, 2, 3})}; - check(); + check1(); } { constexpr auto members = - define_static_array(members_of(^^::, std::meta::access_context::current())); + define_static_array(nonstatic_data_members_of(^^Foo, std::meta::access_context::current())); constexpr auto spanOfInfo = rsl::span{members}; - check()>(); + check2(); } } From 946f010b6e15a87b0f5aa8ae73e86c54047a3cba Mon Sep 17 00:00:00 2001 From: Tsche Date: Wed, 7 Jan 2026 05:57:21 +0100 Subject: [PATCH 02/14] help GCC with constant_wrapper Signed-off-by: Tsche --- include/rsl/constant_wrapper | 95 ++++++++++++++++++------------------ 1 file changed, 48 insertions(+), 47 deletions(-) diff --git a/include/rsl/constant_wrapper b/include/rsl/constant_wrapper index 89d1b9f..2562f65 100644 --- a/include/rsl/constant_wrapper +++ b/include/rsl/constant_wrapper @@ -1,6 +1,7 @@ #pragma once #include #include +#include namespace rsl { namespace _cw_impl { @@ -16,7 +17,7 @@ struct Constant { using type = T[Extent]; T _data[Extent]; - constexpr Constant(T (&_arr)[Extent]) noexcept : Constant(_arr, make_index_sequence()) {} + constexpr Constant(T (&_arr)[Extent]) noexcept : Constant(_arr, std::make_index_sequence()) {} private: template @@ -33,37 +34,37 @@ struct constant_wrapper; namespace _cw_impl { template -concept constexpr_param = requires { typename constant_wrapper; }; +concept constexpr_param = requires {typename constant_wrapper;}; struct cw_operators { // unary operators template - friend constexpr auto operator+(T) noexcept -> constant_wrapper<(+T::value)> { + friend constexpr auto operator+(T) noexcept -> constant_wrapper { return {}; } template - friend constexpr auto operator-(T) noexcept -> constant_wrapper<(-T::value)> { + friend constexpr auto operator-(T) noexcept -> constant_wrapper { return {}; } template - friend constexpr auto operator~(T) noexcept -> constant_wrapper<(~T::value)> { + friend constexpr auto operator~(T) noexcept -> constant_wrapper { return {}; } template - friend constexpr auto operator!(T) noexcept -> constant_wrapper<(!T::value)> { + friend constexpr auto operator!(T) noexcept -> constant_wrapper { return {}; } template - friend constexpr auto operator&(T) noexcept -> constant_wrapper<(&T::value)> { + friend constexpr auto operator&(T) noexcept -> constant_wrapper { return {}; } template - friend constexpr auto operator*(T) noexcept -> constant_wrapper<(*T::value)> { + friend constexpr auto operator*(T) noexcept -> constant_wrapper { return {}; } @@ -71,61 +72,61 @@ struct cw_operators { template friend constexpr auto operator+(Lhs, Rhs) noexcept - -> constant_wrapper<(Lhs::value + Rhs::value)> { + -> constant_wrapper { return {}; } template friend constexpr auto operator-(Lhs, Rhs) noexcept - -> constant_wrapper<(Lhs::value - Rhs::value)> { + -> constant_wrapper { return {}; } template friend constexpr auto operator*(Lhs, Rhs) noexcept - -> constant_wrapper<(Lhs::value * Rhs::value)> { + -> constant_wrapper { return {}; } template friend constexpr auto operator/(Lhs, Rhs) noexcept - -> constant_wrapper<(Lhs::value / Rhs::value)> { + -> constant_wrapper { return {}; } template friend constexpr auto operator%(Lhs, Rhs) noexcept - -> constant_wrapper<(Lhs::value % Rhs::value)> { + -> constant_wrapper { return {}; } template friend constexpr auto operator<<(Lhs, Rhs) noexcept - -> constant_wrapper<(Lhs::value << Rhs::value)> { + -> constant_wrapper { return {}; } template friend constexpr auto operator>>(Lhs, Rhs) noexcept - -> constant_wrapper<(Lhs::value >> Rhs::value)> { + -> constant_wrapper> Rhs::value)> { return {}; } template friend constexpr auto operator&(Lhs, Rhs) noexcept - -> constant_wrapper<(Lhs::value & Rhs::value)> { + -> constant_wrapper { return {}; } template friend constexpr auto operator|(Lhs, Rhs) noexcept - -> constant_wrapper<(Lhs::value | Rhs::value)> { + -> constant_wrapper { return {}; } template friend constexpr auto operator^(Lhs, Rhs) noexcept - -> constant_wrapper<(Lhs::value ^ Rhs::value)> { + -> constant_wrapper { return {}; } @@ -133,7 +134,7 @@ struct cw_operators { requires(!std::is_constructible_v || !std::is_constructible_v) friend constexpr auto operator&&(Lhs, Rhs) noexcept - -> constant_wrapper<(Lhs::value && Rhs::value)> { + -> constant_wrapper { return {}; } @@ -141,44 +142,44 @@ struct cw_operators { requires(!std::is_constructible_v || !std::is_constructible_v) friend constexpr auto operator||(Lhs, Rhs) noexcept - -> constant_wrapper<(Lhs::value || Rhs::value)> { + -> constant_wrapper { return {}; } // comparisons template friend constexpr auto operator<=>(Lhs, Rhs) noexcept - -> constant_wrapper<(Lhs::value <=> Rhs::value)> { + -> constant_wrapper Rhs::value)> { return {}; } template friend constexpr auto operator<(Lhs, Rhs) noexcept - -> constant_wrapper<(Lhs::value < Rhs::value)> { + -> constant_wrapper { return {}; } template friend constexpr auto operator<=(Lhs, Rhs) noexcept - -> constant_wrapper<(Lhs::value <= Rhs::value)> { + -> constant_wrapper { return {}; } template friend constexpr auto operator==(Lhs, Rhs) noexcept - -> constant_wrapper<(Lhs::value == Rhs::value)> { + -> constant_wrapper { return {}; } template friend constexpr auto operator!=(Lhs, Rhs) noexcept - -> constant_wrapper<(Lhs::value != Rhs::value)> { + -> constant_wrapper { return {}; } template friend constexpr auto operator>(Lhs, Rhs) noexcept - -> constant_wrapper<(Lhs::value > Rhs::value)> { + -> constant_wrapper Rhs::value)> { return {}; } template friend constexpr auto operator>=(Lhs, Rhs) noexcept - -> constant_wrapper<(Lhs::value >= Rhs::value)> { + -> constant_wrapper= Rhs::value)> { return {}; } @@ -186,84 +187,84 @@ struct cw_operators { friend constexpr auto operator,(Lhs, Rhs) noexcept = delete; template friend constexpr auto operator->*(Lhs, Rhs) noexcept - -> constant_wrapper*(Rhs::value)> { + -> constant_wrapper*(Rhs::value))> { return {}; } // call and index template constexpr auto operator()(this T, Args...) noexcept - requires requires { constant_wrapper(); } + requires requires { constant_wrapper(); } { - return constant_wrapper{}; + return constant_wrapper{}; } template constexpr auto operator[](this T, Args...) noexcept - -> constant_wrapper<(T::value[Args::value...])> { + -> constant_wrapper { return {}; } // pseudo-mutators template - constexpr auto operator++(this T) noexcept -> constant_wrapper<++T::value> { + constexpr auto operator++(this T) noexcept -> constant_wrapper { return {}; } template - constexpr auto operator++(this T, int) noexcept -> constant_wrapper { + constexpr auto operator++(this T, int) noexcept -> constant_wrapper { return {}; } template - constexpr auto operator--(this T) noexcept -> constant_wrapper<--T::value> { + constexpr auto operator--(this T) noexcept -> constant_wrapper { return {}; } template - constexpr auto operator--(this T, int) noexcept -> constant_wrapper { + constexpr auto operator--(this T, int) noexcept -> constant_wrapper { return {}; } template - constexpr auto operator+=(this T, Rhs) noexcept -> constant_wrapper<(T::value += Rhs::value)> { + constexpr auto operator+=(this T, Rhs) noexcept -> constant_wrapper { return {}; } template - constexpr auto operator-=(this T, Rhs) noexcept -> constant_wrapper<(T::value -= Rhs::value)> { + constexpr auto operator-=(this T, Rhs) noexcept -> constant_wrapper { return {}; } template - constexpr auto operator*=(this T, Rhs) noexcept -> constant_wrapper<(T::value *= Rhs::value)> { + constexpr auto operator*=(this T, Rhs) noexcept -> constant_wrapper { return {}; } template - constexpr auto operator/=(this T, Rhs) noexcept -> constant_wrapper<(T::value /= Rhs::value)> { + constexpr auto operator/=(this T, Rhs) noexcept -> constant_wrapper { return {}; } template - constexpr auto operator%=(this T, Rhs) noexcept -> constant_wrapper<(T::value %= Rhs::value)> { + constexpr auto operator%=(this T, Rhs) noexcept -> constant_wrapper { return {}; } template - constexpr auto operator&=(this T, Rhs) noexcept -> constant_wrapper<(T::value &= Rhs::value)> { + constexpr auto operator&=(this T, Rhs) noexcept -> constant_wrapper { return {}; } template - constexpr auto operator|=(this T, Rhs) noexcept -> constant_wrapper<(T::value |= Rhs::value)> { + constexpr auto operator|=(this T, Rhs) noexcept -> constant_wrapper { return {}; } template - constexpr auto operator^=(this T, Rhs) noexcept -> constant_wrapper<(T::value ^= Rhs::value)> { + constexpr auto operator^=(this T, Rhs) noexcept -> constant_wrapper { return {}; } template - constexpr auto operator<<=(this T, Rhs) noexcept -> constant_wrapper<(T::value <<= Rhs::value)> { + constexpr auto operator<<=(this T, Rhs) noexcept -> constant_wrapper { return {}; } template - constexpr auto operator>>=(this T, Rhs) noexcept -> constant_wrapper<(T::value >>= Rhs::value)> { + constexpr auto operator>>=(this T, Rhs) noexcept -> constant_wrapper>= Rhs::value)> { return {}; } }; @@ -271,12 +272,12 @@ struct cw_operators { template <_cw_impl::Constant Val, class> struct constant_wrapper : _cw_impl::cw_operators { - static constexpr const auto& value = Val.data; + static constexpr const auto& value = Val._data; using type = constant_wrapper; using value_type = typename decltype(Val)::type; template <_cw_impl::constexpr_param Rhs> - constexpr auto operator=(Rhs) const noexcept -> constant_wrapper<(Val = Rhs::value)> { + constexpr auto operator=(Rhs) const noexcept -> constant_wrapper { return {}; } From f4747fc508186946db23b9b5af3cc0380f5462e9 Mon Sep 17 00:00:00 2001 From: Tsche Date: Wed, 7 Jan 2026 04:59:51 +0000 Subject: [PATCH 03/14] reimplement kwargs --- include/rsl/_impl/macro/loop.hpp | 19 +++++ include/rsl/kwargs | 121 +++++++++++++++---------------- include/rsl/macro | 22 +++++- test/CMakeLists.txt | 2 +- test/kwargs/capture_list.cpp | 35 ++------- test/kwargs/simple.cpp | 16 ++-- 6 files changed, 115 insertions(+), 100 deletions(-) create mode 100644 include/rsl/_impl/macro/loop.hpp diff --git a/include/rsl/_impl/macro/loop.hpp b/include/rsl/_impl/macro/loop.hpp new file mode 100644 index 0000000..ce36cc9 --- /dev/null +++ b/include/rsl/_impl/macro/loop.hpp @@ -0,0 +1,19 @@ +#pragma once + +#define RSL_IMPL_PARENS () + +#define RSL_IMPL_EXPAND(...) RSL_IMPL_EXPAND4(RSL_IMPL_EXPAND4(RSL_IMPL_EXPAND4(RSL_IMPL_EXPAND4(__VA_ARGS__)))) +#define RSL_IMPL_EXPAND4(...) RSL_IMPL_EXPAND3(RSL_IMPL_EXPAND3(RSL_IMPL_EXPAND3(RSL_IMPL_EXPAND3(__VA_ARGS__)))) +#define RSL_IMPL_EXPAND3(...) RSL_IMPL_EXPAND2(RSL_IMPL_EXPAND2(RSL_IMPL_EXPAND2(RSL_IMPL_EXPAND2(__VA_ARGS__)))) +#define RSL_IMPL_EXPAND2(...) RSL_IMPL_EXPAND1(RSL_IMPL_EXPAND1(RSL_IMPL_EXPAND1(RSL_IMPL_EXPAND1(__VA_ARGS__)))) +#define RSL_IMPL_EXPAND1(...) __VA_ARGS__ + +#define RSL_IMPL_FOR_EACH_DELIM(macro, delim, ...) \ + __VA_OPT__(RSL_IMPL_EXPAND(RSL_IMPL_FOR_EACH_DELIM_HELPER(macro, delim, __VA_ARGS__))) +#define RSL_IMPL_FOR_EACH_DELIM_HELPER(macro, delim, a1, ...) \ + macro(a1) \ + __VA_OPT__(delim() RSL_IMPL_FOR_EACH_DELIM_AGAIN RSL_IMPL_PARENS (macro, delim, __VA_ARGS__)) +#define RSL_IMPL_FOR_EACH_DELIM_AGAIN() RSL_IMPL_FOR_EACH_DELIM_HELPER + +#define RSL_IMPL_DELIM_NONE() +#define RSL_IMPL_DELIM_COMMA() , \ No newline at end of file diff --git a/include/rsl/kwargs b/include/rsl/kwargs index 859f247..ceca010 100644 --- a/include/rsl/kwargs +++ b/include/rsl/kwargs @@ -26,9 +26,12 @@ template concept is_kwargs = has_template_arguments(^^T) && template_of(^^T) == ^^kwargs_t; namespace kwargs { +struct parse_error : std::logic_error { + using std::logic_error::logic_error; +}; + struct NameParser : _impl::Parser { using _impl::Parser::Parser; - std::vector names; constexpr bool parse() { @@ -36,35 +39,15 @@ struct NameParser : _impl::Parser { while (is_valid()) { skip_whitespace(); - - if (current() == '&') { - // might be captured by reference - ++cursor; - skip_whitespace(); - } - - if (current() == '.') { - // pack captured, reject - return false; - } - std::size_t start = cursor; - - // find '=', ',' or whitespace skip_to('=', ',', ' ', '\n', '\r', '\t'); if (cursor - start == 0) { - // default capture or invalid name - return false; - } - - auto name = data.substr(start, cursor - start); - if (name == "this" || name == "*this") { - // this captured, reject + // invalid name return false; } - names.emplace_back(name); + names.emplace_back(data.substr(start, cursor - start)); - // skip ahead to next capture + // skip ahead to next arg // if the current character is already ',', this will not move the cursor skip_to(','); ++cursor; @@ -75,41 +58,64 @@ struct NameParser : _impl::Parser { } }; -template -constexpr auto make(Ts&&... values) { - struct kwargs_impl; +struct Arg { + template + decltype(auto) operator=(T&& value) const { + return std::forward(value); + } +}; + +template +consteval std::meta::info inject_impl() { + struct injected_type; consteval { - std::vector types{^^Ts...}; - std::vector args; + define_aggregate(^^injected_type, {Args...}); + }; - auto parser = NameParser(Names); + // ensure injecting the class worked + static_assert(is_complete_type(^^injected_type)); + return ^^injected_type; +} - // with P3068 parser.parse() could throw to provide better diagnostics at this point - if (!parser.parse()) { - return; - } +consteval std::meta::info inject(std::meta::reflection_range auto&& fields) { + return extract(substitute( + ^^inject_impl, + fields | std::views::transform([](auto r) { return std::meta::reflect_constant(r); })))(); +} - // associate every argument with the corresponding name - // retrieved by parsing the capture list +consteval std::meta::info make_detector_type(std::string_view Names) { + auto parser = NameParser(Names); + if (not parser.parse()) { + throw parse_error("could not parse argument list"); + } + return inject(parser.names | std::views::transform([](auto name) { + return data_member_spec(^^Arg, {.name = name}); + })); +} - // std::views::zip_transform could also be used for this - for (auto [member, name] : std::views::zip(types, parser.names)) { - args.push_back(data_member_spec(member, {.name = name})); - } - define_aggregate(^^kwargs_impl, args); - }; +template +using detector = typename[:rsl::kwargs::make_detector_type(C):]; - // ensure injecting the class worked - static_assert(is_type(^^kwargs_impl), std::string{"Invalid keyword arguments `"} + Names + "`"); +template +consteval auto inject_wrapper() { + std::vector args; + auto members = + nonstatic_data_members_of(^^detector, std::meta::access_context::unprivileged()); + for (auto [member, type] : std::views::zip(members, std::vector{^^Ts...})) { + args.push_back(data_member_spec(remove_cvref(type), {.name = identifier_of(member)})); + } - return kwargs_t{{std::forward(values)...}}; + return inject(args); } -} // namespace kwargs -template -constexpr auto make_args(Ts&&... values) { - return kwargs::make(std::forward(values)...); +template +constexpr auto make(Ts&&... values) { + static_assert(((not std::same_as, Arg>) && ...)); + + using container = kwargs_t...>():]>; + return container{{std::forward(values)...}}; } +} // namespace kwargs template requires is_kwargs> @@ -170,15 +176,8 @@ struct std::tuple_element> { using type = [:rsl::kwargs_t>::_member_cache.types[Idx]:]; }; -#define RSL_KWARGS(...) \ - [__VA_ARGS__](this T _impl_this) { \ - constexpr static auto _impl_captures = \ - define_static_array(nonstatic_data_members_of(^^T, std::meta::access_context::current())); \ - return [&](std::index_sequence) { \ - return rsl::kwargs::make<#__VA_ARGS__>( \ - std::forward( \ - _impl_this.[:_impl_captures[Idx]:])...); \ - }(std::make_index_sequence<_impl_captures.size()>()); \ - }() - -#define $args(...) RSL_KWARGS(__VA_ARGS__) \ No newline at end of file +#define RSL_KWARGS_IDENTITY(x) x +#define RSL_KWARGS_EXPAND_ONE(d) rsl::kwargs::detector{}.RSL_KWARGS_IDENTITY + +#define $args(...) \ + rsl::kwargs::make<#__VA_ARGS__>($for_each(RSL_KWARGS_EXPAND_ONE(#__VA_ARGS__), __VA_ARGS__)) diff --git a/include/rsl/macro b/include/rsl/macro index e81b39d..2bcc520 100644 --- a/include/rsl/macro +++ b/include/rsl/macro @@ -3,6 +3,7 @@ #include #include #include +#include // compile environment checks @@ -40,4 +41,23 @@ # define $define_static_array(...) [:std::meta::reflect_constant_array(__VA_ARGS__):] #else # define $define_static_array(...) std::define_static_array(__VA_ARGS__) -#endif \ No newline at end of file +#endif + +/** Applies `macro` to every element and inserts delimiters produced by `delim` between elements + * @param macro function-like macro applied to every element + * @param delim function-like macro producing the delimiter + * @param ... elements to loop over +*/ +#define $for_each_delim(macro, delim, ...) RSL_IMPL_FOR_EACH_DELIM(macro, delim, __VA_ARGS__) + +/** Applies `macro` to every element and inserts commas between elements + * @param macro function-like macro applied to every element + * @param ... elements to loop over +*/ +#define $for_each_list(macro, ...) RSL_IMPL_FOR_EACH_DELIM(macro, RSL_IMPL_DELIM_COMMA, __VA_ARGS__) + +/** Applies `macro` to every element + * @param macro function-like macro applied to every element + * @param ... elements to loop over +*/ +#define $for_each(macro, ...) RSL_IMPL_FOR_EACH_DELIM(macro, RSL_IMPL_DELIM_NONE, __VA_ARGS__) diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index e4db0ca..0b5fc78 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -7,7 +7,7 @@ add_subdirectory(span) add_subdirectory(format) add_subdirectory(expect) -# add_subdirectory(kwargs) +add_subdirectory(kwargs) # add_subdirectory(variant) # add_subdirectory(tagged_variant) # add_subdirectory(tuple) diff --git a/test/kwargs/capture_list.cpp b/test/kwargs/capture_list.cpp index 9367903..94ba233 100644 --- a/test/kwargs/capture_list.cpp +++ b/test/kwargs/capture_list.cpp @@ -19,21 +19,22 @@ void check(std::string_view str, } } -void check_reject(std::string_view str, std::source_location const& loc = std::source_location::current()) { - auto parser = rsl::kwargs::NameParser{str}; +void check_reject(std::string_view str, + std::source_location const& loc = std::source_location::current()) { + auto parser = rsl::kwargs::NameParser{str}; auto source_location = std::format("{}:{}", loc.file_name(), loc.line()); EXPECT_FALSE(parser.parse()) << source_location; } -} +} // namespace TEST(CaptureList, Named) { - std::vector one_arg = {"x=1", "x = 1", "x= 1", "x =1", "&x = 1", "& x = 1"}; + std::vector one_arg = {"x=1", "x = 1", "x= 1", "x =1"}; for (auto capture_list : one_arg) { check(capture_list, {"x"}); } - std::vector two_args = {"x = 1, y = 1", "x=1,y=1", "x=1 ,y=1", "x=1,&y=1", "x=1 , &y=1"}; + std::vector two_args = {"x = 1, y = 1", "x=1,y=1", "x=1 ,y=1", "x=1 , y=1"}; for (auto capture_list : two_args) { check(capture_list, {"x", "y"}); @@ -60,27 +61,3 @@ TEST(CaptureList, Unnamed) { check(capture_list, {"x", "y", "z"}); } } - -TEST(CaptureList, CaptureDefault) { - std::vector one_arg = {"&, x=2", "&, &x=2", "=, x=2", "=, &x=2"}; - // reject - for (auto capture_list : one_arg) { - check_reject(capture_list); - } -} - -TEST(CaptureList, This) { - std::vector one_arg = {"this, x = 2", "x=2, this", "*this, x=2", "x = 2, *this"}; - // reject - for (auto capture_list : one_arg) { - check_reject(capture_list); - } -} - -TEST(CaptureList, Packs) { - std::vector one_arg = {"...args = foo, x = 2", "... args = foo, x = 2"}; - // reject - for (auto capture_list : one_arg) { - check_reject(capture_list); - } -} diff --git a/test/kwargs/simple.cpp b/test/kwargs/simple.cpp index ad73089..ed10424 100644 --- a/test/kwargs/simple.cpp +++ b/test/kwargs/simple.cpp @@ -32,19 +32,19 @@ int get_by_idx_default(rsl::kwargs_t const& kwargs) { } TEST(KwArgs, Empty) { - auto args = RSL_KWARGS(); + auto args = $args(); EXPECT_EQ(std::tuple_size_v, 0); } TEST(KwArgs, Simple) { - EXPECT_EQ(member_access(RSL_KWARGS(x=10)), 10); - EXPECT_EQ(get_by_idx(RSL_KWARGS(x=10)), 10); - EXPECT_EQ(get_by_name(RSL_KWARGS(x=10)), 10); + EXPECT_EQ(member_access($args(x=10)), 10); + EXPECT_EQ(get_by_idx($args(x=10)), 10); + EXPECT_EQ(get_by_name($args(x=10)), 10); } TEST(KwArgs, Default) { - EXPECT_EQ(get_by_name_default(RSL_KWARGS(x=10)), 10); - EXPECT_EQ(get_by_idx_default(RSL_KWARGS(x=10)), 10); - EXPECT_EQ(get_by_name_default(RSL_KWARGS()), 42); - EXPECT_EQ(get_by_idx_default(RSL_KWARGS()), 42); + EXPECT_EQ(get_by_name_default($args(x=10)), 10); + EXPECT_EQ(get_by_idx_default($args(x=10)), 10); + EXPECT_EQ(get_by_name_default($args()), 42); + EXPECT_EQ(get_by_idx_default($args()), 42); } \ No newline at end of file From a73b424b1fa796f62f7375108e4fc776515be367 Mon Sep 17 00:00:00 2001 From: Tsche Date: Wed, 7 Jan 2026 05:16:21 +0000 Subject: [PATCH 04/14] re-enable reflection and libc++ for clang --- CMakeLists.txt | 6 +++--- 1 file changed, 3 insertions(+), 3 deletions(-) diff --git a/CMakeLists.txt b/CMakeLists.txt index 7428c73..b73cd92 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -15,9 +15,9 @@ add_library(rsl::util ALIAS rsl-util) target_compile_features(rsl-util INTERFACE cxx_std_26) if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang") - # target_compile_options(rsl-util INTERFACE "-stdlib=libc++" "-freflection-latest") - # target_link_options(rsl-util INTERFACE "-stdlib=libc++") - # target_link_libraries(rsl-util INTERFACE "c++abi") + target_compile_options(rsl-util INTERFACE "-stdlib=libc++" "-freflection-latest") + target_link_options(rsl-util INTERFACE "-stdlib=libc++") + target_link_libraries(rsl-util INTERFACE "c++abi") elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU") target_compile_options(rsl-util INTERFACE "-freflection") endif() From 2cbbfe419f6c40f085f29974f6cc6e66a5c528bc Mon Sep 17 00:00:00 2001 From: Tsche Date: Fri, 9 Jan 2026 23:11:13 +0000 Subject: [PATCH 05/14] ensure to_string compiles --- include/rsl/repr | 26 +++++++++++++++-------- include/rsl/serialize | 3 ++- test/CMakeLists.txt | 6 +++--- test/serializer/CMakeLists.txt | 8 +++---- test/serializer/to_string/fundamental.cpp | 8 +++---- 5 files changed, 30 insertions(+), 21 deletions(-) diff --git a/include/rsl/repr b/include/rsl/repr index 444ba0b..b2a0316 100644 --- a/include/rsl/repr +++ b/include/rsl/repr @@ -19,17 +19,25 @@ constexpr std::string repr(T&& value, repr_options opts = {}) { } template -constexpr std::string repr(rsl::constant_wrapper, repr_options opts = {}) { +struct constant_t { + constexpr static auto value = V; +}; + +template +constexpr constant_t constant{}; + +template +constexpr std::string repr(constant_t, repr_options opts = {}) { return rsl::repr(V, opts); } template -constexpr std::string repr(T&& value, rsl::constant_wrapper = {}) { +constexpr std::string repr(T&& value, constant_t = {}) { return rsl::repr(std::forward(value), Opts); } template -constexpr std::string repr(rsl::constant_wrapper, rsl::constant_wrapper = {}) { +constexpr std::string repr(constant_t, constant_t = {}) { return rsl::repr(V, Opts); } @@ -39,21 +47,21 @@ std::ostream& operator<<(std::ostream& stream, std::string (*)(T&&, repr_options } template -std::ostream& operator<<(std::ostream& stream, std::string (*)(T&&, rsl::constant_wrapper)) { +std::ostream& operator<<(std::ostream& stream, std::string (*)(T&&, constant_t)) { return stream << std::string_view(define_static_string(display_string_of(remove_cvref(^^T)))); } template std::ostream& operator<<(std::ostream& stream, - std::string (*fnc)(rsl::constant_wrapper, repr_options)) { - return stream << fnc(rsl::constant_wrapper{}, {}); + std::string (*fnc)(constant_t, repr_options)) { + return stream << fnc(constant, {}); } template std::ostream& operator<<(std::ostream& stream, - std::string (*fnc)(rsl::constant_wrapper, - rsl::constant_wrapper)) { - return stream << fnc(rsl::constant_wrapper{}, {}); + std::string (*fnc)(constant_t, + constant_t)) { + return stream << fnc(constant, {}); } } // namespace rsl diff --git a/include/rsl/serialize b/include/rsl/serialize index 27a1e90..9f76844 100644 --- a/include/rsl/serialize +++ b/include/rsl/serialize @@ -3,6 +3,7 @@ #include #include +#include #include // for to_string(enum-type) #include #include @@ -109,7 +110,7 @@ constexpr std::string to_string(T value) { return ret; } else { // otherwise we can do better - try generating a switch instead - template for (constexpr auto E : define_static_array(enumerators_of(^^T))) { + template for (constexpr auto E : $define_static_array(enumerators_of(^^T))) { if (extract(constant_of(E)) == value) { // at least one enumerator was an exact match, print the first return std::string{identifier_of(E)}; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 0b5fc78..ea000f9 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -6,10 +6,10 @@ add_subdirectory(string_view) add_subdirectory(span) add_subdirectory(format) add_subdirectory(expect) - add_subdirectory(kwargs) +add_subdirectory(tagged_variant) + +add_subdirectory(serializer) # add_subdirectory(variant) -# add_subdirectory(tagged_variant) # add_subdirectory(tuple) -# add_subdirectory(serializer) # add_subdirectory(trie) \ No newline at end of file diff --git a/test/serializer/CMakeLists.txt b/test/serializer/CMakeLists.txt index 5fc8aab..fb19038 100644 --- a/test/serializer/CMakeLists.txt +++ b/test/serializer/CMakeLists.txt @@ -1,6 +1,6 @@ -target_sources(rsl-util-test PRIVATE - canonical_name.cpp -) +# target_sources(rsl-util-test PRIVATE +# canonical_name.cpp +# ) -add_subdirectory(repr) +# add_subdirectory(repr) add_subdirectory(to_string) \ No newline at end of file diff --git a/test/serializer/to_string/fundamental.cpp b/test/serializer/to_string/fundamental.cpp index 3c9d455..86e6985 100644 --- a/test/serializer/to_string/fundamental.cpp +++ b/test/serializer/to_string/fundamental.cpp @@ -1,3 +1,4 @@ +#include #include #include @@ -66,8 +67,7 @@ TEST(ToString, Integral) { } TEST(ToString, floating) { - // flaky? - ASSERT_EQ(rsl::to_string(1.23F), "1.230000"); - ASSERT_EQ(rsl::to_string(1.23), "1.230000"); - ASSERT_EQ(rsl::to_string(1.23L), "1.230000"); + ASSERT_TRUE(rsl::to_string(1.23F).starts_with("1.23")); + ASSERT_TRUE(rsl::to_string(1.23).starts_with("1.23")); + ASSERT_TRUE(rsl::to_string(1.23L).starts_with("1.23")); } \ No newline at end of file From 5f8111a7d0d633e932ae373279f0da72564852b5 Mon Sep 17 00:00:00 2001 From: Tsche Date: Sat, 10 Jan 2026 03:31:25 +0100 Subject: [PATCH 06/14] fix variant Signed-off-by: Tsche --- include/rsl/_impl/member_cache.hpp | 2 +- include/rsl/variant | 353 +++++++++--------- test/CMakeLists.txt | 4 +- test/variant/swap.cpp | 3 +- test/variant/template.cpp | 2 +- test/variant/variant.get/get.cpp | 2 +- .../variant.helper/variant_alternative.cpp | 26 +- 7 files changed, 211 insertions(+), 181 deletions(-) diff --git a/include/rsl/_impl/member_cache.hpp b/include/rsl/_impl/member_cache.hpp index b23cb5c..a755d54 100644 --- a/include/rsl/_impl/member_cache.hpp +++ b/include/rsl/_impl/member_cache.hpp @@ -60,7 +60,7 @@ struct MemberAccessor { static consteval bool has_member(std::string_view name) { return get_index_of(name) != -1UZ; } }; -template +template constexpr inline MemberAccessor member_cache{}; consteval auto cache_members(auto&& member_range) { diff --git a/include/rsl/variant b/include/rsl/variant index b54ecc4..2a2337b 100644 --- a/include/rsl/variant +++ b/include/rsl/variant @@ -40,7 +40,6 @@ public: }; [[noreturn]] inline void throw_bad_variant_access(bool valueless) { - __builtin_debugtrap(); #if __cpp_exceptions if (valueless) { throw bad_variant_access("variant is valueless"); @@ -196,7 +195,7 @@ template constexpr R visit_at_enumerated(std::size_t idx, F&& visitor, V&& variant) { // This only makes sense for single-variant visitation constexpr static std::size_t max_index = rsl::variant_size>::value; - template for (constexpr size_t Idx : std::views::iota(0ZU, max_index)) { + template for (constexpr size_t Idx : $define_static_array(std::views::iota(0ZU, max_index))) { if (idx == Idx) { return std::invoke(std::forward(visitor), std::in_place_index, @@ -209,7 +208,7 @@ constexpr R visit_at_enumerated(std::size_t idx, F&& visitor, V&& variant) { template constexpr R visit_at(std::size_t idx, F&& visitor, Vs&&... variants) { constexpr static std::size_t max_index = VisitImpl::max_index; - template for (constexpr size_t Idx : std::views::iota(0ZU, max_index)) { + template for (constexpr size_t Idx : $define_static_array(std::views::iota(0ZU, max_index))) { if (idx == Idx) { return VisitImpl::template visit(std::forward(visitor), std::forward(variants)...); @@ -259,7 +258,8 @@ protected: return true; } - template for (constexpr auto Idx : std::views::iota(0ZU, alternatives.count)) { + template for (constexpr auto Idx : + $define_static_array(std::views::iota(0ZU, alternatives.count))) { if constexpr (std::is_nothrow_move_constructible_v) { return true; } @@ -285,11 +285,13 @@ protected: } public: - constexpr static auto alternatives = [:_impl::cache_members( - nonstatic_data_members_of( - dealias(^^Storage), - std::meta::access_context::unchecked()) | - std::views::drop(1)):]; + constexpr static auto alternatives = + typename[:substitute(^^_impl::MemberAccessor, + nonstatic_data_members_of(dealias(^^Storage), + std::meta::access_context::unchecked()) | + std::views::drop(1) | std::views::transform([](std::meta::info r) { + return reflect_constant(r); + })):](); using index_type = std::conditional_t<(alternatives.count >= 255), unsigned short, unsigned char>; static constexpr auto npos = index_type(-1ULL); @@ -415,8 +417,8 @@ public: template requires(!std::same_as, variant_base> && !_variant_impl::is_in_place> && - std::assignable_from]:]&, T> && - selected_index != variant_npos) + selected_index != variant_npos && + std::assignable_from]:]&, T>) constexpr variant_base& operator=(T&& obj) noexcept( is_nothrow_constructible_type(alternatives.types[selected_index], {remove_cvref(^^T)})) { emplace>(std::forward(obj)); @@ -753,8 +755,7 @@ template constexpr auto get_if(_variant_impl::variant_base* variant_) noexcept -> variant_alternative_t>* { constexpr static auto alt_count = _variant_impl::variant_base::alternatives.count; - static_assert(Idx < alt_count, - std::string("index must be in [0, ") + to_string(alt_count) + "]"); + static_assert(Idx < alt_count, std::string("index must be in [0, ") + to_string(alt_count) + "]"); static_assert(!std::is_void_v>>, "alternative type must not be void"); @@ -782,29 +783,39 @@ inline constexpr std::size_t variant_npos = _variant_impl::variant_npos; using bad_variant_access = _variant_impl::bad_variant_access; namespace _impl { +// template +// consteval std::meta::info make_storage() { +// union Storage; +// consteval { +// std::size_t idx = 0; +// define_aggregate(^^Storage, +// {data_member_spec(^^char, {.name = "_rsl_dummy"}), +// data_member_spec(^^Ts, {.name = "_rsl_alt" + to_string(idx++)})...}); +// }; +// return ^^Storage; +// } + +// template +// using Storage = [:make_storage...>():]; + template -consteval std::meta::info make_storage() { - union Storage; +struct Storage { + union type; consteval { std::size_t idx = 0; - define_aggregate( - ^^Storage, - {data_member_spec(^^char, {.name = "_rsl_dummy"}), - data_member_spec(remove_cvref(^^Ts), {.name = "_rsl_alt" + to_string(idx++)})...}); + define_aggregate(^^type, + {data_member_spec(^^char, {.name = "_rsl_dummy"}), + data_member_spec(^^std::remove_cvref_t, {.name = "_rsl_alt" + to_string(idx++)})...}); }; - return ^^Storage; -} - -template -using Storage = [:make_storage():]; +}; } // namespace _impl template -class variant : public _variant_impl::variant_base<_impl::Storage> { +class variant : public _variant_impl::variant_base::type> { static_assert(sizeof...(Ts) > 0, "variant must contain at least one alternative"); static_assert((!std::is_reference_v && ...), "variant must not have reference alternatives"); static_assert((!std::is_void_v && ...), "variant must not have void alternatives"); - using storage_type = _impl::Storage; + using storage_type = _impl::Storage::type; using base = _variant_impl::variant_base; public: @@ -844,7 +855,7 @@ struct variant_alternative> { template struct variant_alternative const> { static_assert(Idx < sizeof...(Ts), "variant_alternative index out of range"); - using type = Ts...[Idx] const; + using type = std::add_const_t; }; namespace _impl { @@ -863,151 +874,153 @@ using unwrap_type_tag = typename T::type; template constexpr inline _impl::TypeTag type{}; -namespace _tagged_variant_impl { -template <_impl::is_enum T> -consteval std::span make_transitions() { - std::vector transitions; - for (auto enumerator : enumerators_of(^^T)) { - transitions.push_back(extract(enumerator)); - } - return define_static_array(transitions); -} - -template <_impl::is_enum T> -consteval std::size_t find_inverse_transition(T value) { - std::size_t index = 0; - for (auto enumerator : enumerators_of(^^T)) { - if (value == extract(enumerator)) { - return index; - } - ++index; - } - return -1ULL; -} - -template -constexpr inline auto inverse_transition = find_inverse_transition(V); - -template <_impl::is_enum E> -constexpr static auto transitions = _tagged_variant_impl::make_transitions(); - -template <_impl::is_enum E> -consteval std::meta::info make_storage() { - union Storage; - consteval { - std::size_t idx = 0; - auto members = std::vector{data_member_spec(^^char)}; - - for (auto enumerator : enumerators_of(^^E)) { - auto annotations = annotations_of(enumerator); - auto annotation_type = type_of(annotations[0]); - auto alt_type = dealias(substitute(^^_impl::unwrap_type_tag, {annotation_type})); - members.push_back(data_member_spec(alt_type, {.name = identifier_of(enumerator)})); - } - define_aggregate(^^Storage, members); - }; - return ^^Storage; -} - -template <_impl::is_enum E> -using Storage = [:make_storage():]; -} // namespace _tagged_variant_impl - -template -struct tagged_variant : public _variant_impl::variant_base<_tagged_variant_impl::Storage> { - using storage_type = _tagged_variant_impl::Storage; - using base = _variant_impl::variant_base; - -public: - using _variant_impl::variant_base::variant_base; - constexpr tagged_variant(tagged_variant const&) = default; - constexpr tagged_variant(tagged_variant&&) = default; - constexpr tagged_variant& operator=(tagged_variant const&) = default; - constexpr tagged_variant& operator=(tagged_variant&&) = default; - constexpr ~tagged_variant() = default; - - using tags = E; - - storage_type* operator->() { return &this->_impl_storage; } - storage_type const* operator->() const { return &this->_impl_storage; } - storage_type* operator*() { return &this->_impl_storage; } - storage_type const* operator*() const { return &this->_impl_storage; } - - explicit(false) operator E() const { return _tagged_variant_impl::transitions[this->index()]; } - - using base::emplace; - using base::get; - using base::get_alt; // TODO hide - using base::index; - using base::swap; - using base::valueless_by_exception; - - template - constexpr decltype(auto) visit(this Self&& self, V&& visitor) { - return rsl::visit(std::forward(visitor), std::forward(self)); - } -}; - -template <_impl::is_enum E> -constexpr E get_tag(tagged_variant const& variant_) { - return _tagged_variant_impl::transitions[variant_.index()]; -} - -template - requires(_impl::is_enum) -constexpr bool holds_alternative(tagged_variant const& variant_) noexcept { - return _tagged_variant_impl::transitions[variant_.index()] == E; -} - -template - requires(_impl::is_enum && - std::same_as, std::remove_cvref_t>) -constexpr decltype(auto) get(V&& variant_) { - // TODO underconstrained - return rsl::get<_tagged_variant_impl::inverse_transition>(std::forward(variant_)); -} - -template - requires(_impl::is_enum) -constexpr auto* get_if(tagged_variant* variant_) noexcept { - return rsl::get_if<_tagged_variant_impl::inverse_transition>(variant_); -} - -namespace _impl { - -template -concept all_alts_hashable = std::ranges::all_of( - rsl::tagged_variant::alternatives, - std::ranges::transform([](auto R) { return extract(substitute(^^is_hashable, {R})); })); - -template -concept all_alts_noexcept_hashable = - std::ranges::all_of(rsl::tagged_variant::alternatives, std::ranges::transform([](auto R) { - return extract( - substitute(^^std::is_nothrow_invocable_v, - {substitute(^^std::hash, remove_const(R))})); - })); -} // namespace _impl +// namespace _tagged_variant_impl { +// template <_impl::is_enum T> +// consteval std::span make_transitions() { +// std::vector transitions; +// for (auto enumerator : enumerators_of(^^T)) { +// transitions.push_back(extract(enumerator)); +// } +// return define_static_array(transitions); +// } + +// template <_impl::is_enum T> +// consteval std::size_t find_inverse_transition(T value) { +// std::size_t index = 0; +// for (auto enumerator : enumerators_of(^^T)) { +// if (value == extract(enumerator)) { +// return index; +// } +// ++index; +// } +// return -1ULL; +// } + +// template +// constexpr inline auto inverse_transition = find_inverse_transition(V); + +// template <_impl::is_enum E> +// constexpr static auto transitions = _tagged_variant_impl::make_transitions(); + +// template <_impl::is_enum E> +// consteval std::meta::info make_storage() { +// union Storage; +// consteval { +// std::size_t idx = 0; +// auto members = std::vector{data_member_spec(^^char, {.name = "_rsl_dummy"})}; + +// for (auto enumerator : enumerators_of(^^E)) { +// auto annotations = annotations_of(enumerator); +// auto annotation_type = type_of(annotations[0]); +// auto alt_type = dealias(substitute(^^_impl::unwrap_type_tag, {annotation_type})); +// members.push_back(data_member_spec(alt_type, {.name = identifier_of(enumerator)})); +// } +// define_aggregate(^^Storage, members); +// }; +// return ^^Storage; +// } + +// template <_impl::is_enum E> +// using Storage = [:make_storage():]; +// } // namespace _tagged_variant_impl + +// template +// struct tagged_variant : public _variant_impl::variant_base<_tagged_variant_impl::Storage> { +// using storage_type = _tagged_variant_impl::Storage; +// using base = _variant_impl::variant_base; + +// public: +// using _variant_impl::variant_base::variant_base; +// constexpr tagged_variant(tagged_variant const&) = default; +// constexpr tagged_variant(tagged_variant&&) = default; +// constexpr tagged_variant& operator=(tagged_variant const&) = default; +// constexpr tagged_variant& operator=(tagged_variant&&) = default; +// constexpr ~tagged_variant() = default; + +// using tags = E; + +// storage_type* operator->() { return &this->_impl_storage; } +// storage_type const* operator->() const { return &this->_impl_storage; } +// storage_type* operator*() { return &this->_impl_storage; } +// storage_type const* operator*() const { return &this->_impl_storage; } + +// explicit(false) operator E() const { return +// _tagged_variant_impl::transitions[this->index()]; } + +// using base::emplace; +// using base::get; +// using base::get_alt; // TODO hide +// using base::index; +// using base::swap; +// using base::valueless_by_exception; + +// template +// constexpr decltype(auto) visit(this Self&& self, V&& visitor) { +// return rsl::visit(std::forward(visitor), std::forward(self)); +// } +// }; + +// template <_impl::is_enum E> +// constexpr E get_tag(tagged_variant const& variant_) { +// return _tagged_variant_impl::transitions[variant_.index()]; +// } + +// template +// requires(_impl::is_enum) +// constexpr bool holds_alternative(tagged_variant const& variant_) noexcept { +// return _tagged_variant_impl::transitions[variant_.index()] == E; +// } + +// template +// requires(_impl::is_enum && +// std::same_as, std::remove_cvref_t>) +// constexpr decltype(auto) get(V&& variant_) { +// // TODO underconstrained +// return rsl::get<_tagged_variant_impl::inverse_transition>(std::forward(variant_)); +// } + +// template +// requires(_impl::is_enum) +// constexpr auto* get_if(tagged_variant* variant_) noexcept { +// return rsl::get_if<_tagged_variant_impl::inverse_transition>(variant_); +// } + +// namespace _impl { + +// template +// concept all_alts_hashable = std::ranges::all_of( +// rsl::tagged_variant::alternatives, +// std::ranges::transform([](auto R) { return extract(substitute(^^is_hashable, {R})); +// })); + +// template +// concept all_alts_noexcept_hashable = +// std::ranges::all_of(rsl::tagged_variant::alternatives, std::ranges::transform([](auto R) { +// return extract( +// substitute(^^std::is_nothrow_invocable_v, +// {substitute(^^std::hash, remove_const(R))})); +// })); +// } // namespace _impl } // namespace rsl -template - requires(rsl::_impl::all_alts_hashable) -struct std::hash> { - using result_type = std::size_t; - - std::size_t operator()(rsl::tagged_variant const& obj) const - noexcept(rsl::_impl::all_alts_noexcept_hashable) { - if (obj.valueless_by_exception()) { - constexpr static std::size_t valueless_hash = 0x22c08c8cbcae8fc4; - return valueless_hash; - } - return obj.index() + rsl::visit( - [](T const& value) { - return std::hash>{}(value); - }, - obj); - } -}; +// template +// requires(rsl::_impl::all_alts_hashable) +// struct std::hash> { +// using result_type = std::size_t; + +// std::size_t operator()(rsl::tagged_variant const& obj) const +// noexcept(rsl::_impl::all_alts_noexcept_hashable) { +// if (obj.valueless_by_exception()) { +// constexpr static std::size_t valueless_hash = 0x22c08c8cbcae8fc4; +// return valueless_hash; +// } +// return obj.index() + rsl::visit( +// [](T const& value) { +// return std::hash>{}(value); +// }, +// obj); +// } +// }; template <> struct std::hash { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ea000f9..77ba179 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -7,9 +7,9 @@ add_subdirectory(span) add_subdirectory(format) add_subdirectory(expect) add_subdirectory(kwargs) -add_subdirectory(tagged_variant) +# add_subdirectory(tagged_variant) add_subdirectory(serializer) -# add_subdirectory(variant) +add_subdirectory(variant) # add_subdirectory(tuple) # add_subdirectory(trie) \ No newline at end of file diff --git a/test/variant/swap.cpp b/test/variant/swap.cpp index 7e89616..11aa7cc 100644 --- a/test/variant/swap.cpp +++ b/test/variant/swap.cpp @@ -9,7 +9,8 @@ enum class Foo { bar [[=rsl::type]] }; -using variant_swap_p = ::testing::Types, rsl::tagged_variant>; +// TODO test tagged_variant as well +using variant_swap_p = ::testing::Types>; //, rsl::tagged_variant>; template struct VariantSwap : public testing::Test { diff --git a/test/variant/template.cpp b/test/variant/template.cpp index e19719d..ff10a0d 100644 --- a/test/variant/template.cpp +++ b/test/variant/template.cpp @@ -31,7 +31,7 @@ struct WhoKnows { static_assert(hasOne); using rsl::get; template for (constexpr auto index : - construct_array(std::integer_sequence())) { + $define_static_array(construct_array(std::integer_sequence()))) { if constexpr (rsl::holds_alternative(Args...[index])) { return get(Args...[index]); } diff --git a/test/variant/variant.get/get.cpp b/test/variant/variant.get/get.cpp index e5fcc0a..51e4bc3 100644 --- a/test/variant/variant.get/get.cpp +++ b/test/variant/variant.get/get.cpp @@ -149,7 +149,7 @@ TEST(Get, TypeLvalue) { ASSERT_TRUE((std::same_as::type, int>)); ASSERT_TRUE((std::same_as::type, long>)); - using variant_base = rsl::_variant_impl::variant_base>; + using variant_base = rsl::_variant_impl::variant_base::type>; ASSERT_EQ((variant_base::alternatives.get_index_of(^^int)), 0); ASSERT_EQ((variant_base::alternatives.get_index_of(^^long)), 1); { diff --git a/test/variant/variant.helper/variant_alternative.cpp b/test/variant/variant.helper/variant_alternative.cpp index ce59150..5f82706 100644 --- a/test/variant/variant.helper/variant_alternative.cpp +++ b/test/variant/variant.helper/variant_alternative.cpp @@ -22,12 +22,28 @@ union AlternativeUnion { long double member_3; }; -TEST(Helpers, VariantAlternative) { +constexpr void test_alternative() { using variant_t = rsl::variant; - check_alternatives(); - check_alternatives(); - check_alternatives(); - check_alternatives(); + static_assert(std::same_as::type, int>); + static_assert(std::same_as::type, void*>); + static_assert(std::same_as::type, const void*>); + static_assert(std::same_as::type, long double>); + + static_assert(std::same_as::type, int const>); + static_assert(std::same_as::type, void* const>); + static_assert(std::same_as::type, const void* const>); + static_assert(std::same_as::type, long double const>); + + + static_assert(std::same_as, int>); + static_assert(std::same_as, void*>); + static_assert(std::same_as, const void*>); + static_assert(std::same_as, long double>); + + static_assert(std::same_as, int const>); + static_assert(std::same_as, void* const>); + static_assert(std::same_as, const void* const>); + static_assert(std::same_as, long double const>); } // todo ensure out of bounds access causes a static assertion failure \ No newline at end of file From 06cf0c78ba3301fdc5ba8dc5949c4ae514af6805 Mon Sep 17 00:00:00 2001 From: Tsche Date: Sat, 10 Jan 2026 06:24:15 +0100 Subject: [PATCH 07/14] cleanup, macro Signed-off-by: Tsche --- include/rsl/_impl/macro/compiler.hpp | 8 +- include/rsl/_impl/macro/diagnostic.hpp | 46 ++++ include/rsl/macro | 3 + include/rsl/string_view | 9 +- include/rsl/variant | 321 ++++++++++++------------- test/CMakeLists.txt | 2 +- test/variant/swap.cpp | 2 +- 7 files changed, 214 insertions(+), 177 deletions(-) create mode 100644 include/rsl/_impl/macro/diagnostic.hpp diff --git a/include/rsl/_impl/macro/compiler.hpp b/include/rsl/_impl/macro/compiler.hpp index 5f94d02..fc01eb9 100644 --- a/include/rsl/_impl/macro/compiler.hpp +++ b/include/rsl/_impl/macro/compiler.hpp @@ -9,14 +9,17 @@ #define RSL_VERSION_ENCODE(major, minor, patch) ((major) * 1000000 + (minor) * 1000 + (patch)) #if defined(__clang__) -# define RSL_COMPILER RSL_COMPILER_CLANG +# define RSL_COMPILER RSL_COMPILER_CLANG +# define RSL_COMPILER_ID clang # define RSL_COMPILER_VERSION \ RSL_VERSION_ENCODE(__clang_major__, __clang_minor__, __clang_patchlevel__) #elif defined(__GNUC__) # define RSL_COMPILER RSL_COMPILER_GCC +# define RSL_COMPILER_ID GCC # define RSL_COMPILER_VERSION RSL_VERSION_ENCODE(__GNUC__, __GNUC_MINOR__, __GNUC_PATCHLEVEL__) #elif defined(_MSC_VER) -# define RSL_COMPILER RSL_COMPILER_MSVC +# define RSL_COMPILER RSL_COMPILER_MSVC +# define RSL_COMPILER_ID MSVC # if defined(_MSC_FULL_VER) # define RSL_COMPILER_VERSION _MSC_FULL_VER # else @@ -25,6 +28,7 @@ #else # define RSL_COMPILER RSL_COMPILER_UNKNOWN +# define RSL_COMPILER_ID UNKNOWN # define RSL_COMPILER_VERSION 0 #endif diff --git a/include/rsl/_impl/macro/diagnostic.hpp b/include/rsl/_impl/macro/diagnostic.hpp new file mode 100644 index 0000000..7dc5fb8 --- /dev/null +++ b/include/rsl/_impl/macro/diagnostic.hpp @@ -0,0 +1,46 @@ +#pragma once + +#include "compiler.hpp" + +#define RSL_STRINGIZE_IMPL(x) #x +#define RSL_STRINGIZE(x) RSL_STRINGIZE_IMPL(x) +#if RSL_COMPILER == RSL_COMPILER_MSVC +# define RSL_PRAGMA(x) __pragma(x) +#else +# define RSL_PRAGMA(x) _Pragma(RSL_STRINGIZE(x)) +#endif + + +#if RSL_COMPILER == RSL_COMPILER_MSVC +# define RSL_DIAG_PUSH RSL_PRAGMA(warning(push)) +# define RSL_DIAG_POP RSL_PRAGMA(warning(pop)) +# define RSL_DIAG_DISABLE(id) RSL_PRAGMA(warning(disable : RSL_DIAGS_##id)) +# define RSL_DIAG_ERROR(id) RSL_PRAGMA(warning(error : RSL_DIAGS_##id)) +#elif RSL_COMPILER == RSL_COMPILER_GCC || RSL_COMPILER == RSL_COMPILER_CLANG +# define RSL_DIAG_PUSH RSL_PRAGMA(RSL_COMPILER_ID diagnostic push) +# define RSL_DIAG_POP RSL_PRAGMA(RSL_COMPILER_ID diagnostic pop) +# define RSL_DIAG_DISABLE(w) RSL_PRAGMA(RSL_COMPILER_ID diagnostic ignored RSL_DIAGS_##w) +# define RSL_DIAG_ERROR(w) RSL_PRAGMA(RSL_COMPILER_ID diagnostic error RSL_DIAGS_##w) +#else +# warning "Unsupported compiler" +#endif + +#define RSL_IMPL_CONSUME(...) +#define RSL_DIAG_push RSL_DIAG_PUSH RSL_IMPL_CONSUME +#define RSL_DIAG_pop RSL_DIAG_POP RSL_IMPL_CONSUME +#define RSL_DIAG_disable RSL_DIAG_DISABLE +#define RSL_DIAG_error RSL_DIAG_ERROR + +// Diagnostic map +#if RSL_COMPILER == RSL_COMPILER_MSVC +# define RSL_DIAGS_literal_suffix 4455 +# define RSL_DIAGS_narrowing 4838 +#elif RSL_COMPILER == RSL_COMPILER_GCC +# define RSL_DIAGS_literal_suffix "-Wliteral-suffix" +# define RSL_DIAGS_narrowing "-Wnarrowing" +#elif RSL_COMPILER == RSL_COMPILER_CLANG +# define RSL_DIAGS_literal_suffix "-Wliteral-suffix" +# define RSL_DIAGS_narrowing "-Wc++11-narrowing" +#else +# warning "Unsupported compiler" +#endif \ No newline at end of file diff --git a/include/rsl/macro b/include/rsl/macro index 2bcc520..3b8ece6 100644 --- a/include/rsl/macro +++ b/include/rsl/macro @@ -4,6 +4,7 @@ #include #include #include +#include // compile environment checks @@ -61,3 +62,5 @@ * @param ... elements to loop over */ #define $for_each(macro, ...) RSL_IMPL_FOR_EACH_DELIM(macro, RSL_IMPL_DELIM_NONE, __VA_ARGS__) + +#define $diagnostics(op, ...) RSL_DIAG_##op (__VA_ARGS__) \ No newline at end of file diff --git a/include/rsl/string_view b/include/rsl/string_view index b35d465..a33c6df 100644 --- a/include/rsl/string_view +++ b/include/rsl/string_view @@ -12,6 +12,7 @@ //? compat #include +#include #include #include @@ -357,8 +358,8 @@ using wstring_view = basic_string_view; inline namespace literals { inline namespace string_view_literals { // [string.view.literals], suffix for basic_string_view literals -#pragma clang diagnostic push -#pragma clang diagnostic ignored "-Wuser-defined-literals" +$diagnostics(push) +$diagnostics(disable, literal_suffix) constexpr string_view operator""sv(const char* str, size_t len) noexcept { return {str, len}; @@ -375,7 +376,9 @@ constexpr u32string_view operator""sv(const char32_t* str, size_t len) noexcept constexpr wstring_view operator""sv(const wchar_t* str, size_t len) noexcept { return {str, len}; } -#pragma clang diagnostic pop + +$diagnostics(pop) + } // namespace string_view_literals } // namespace literals } // namespace rsl diff --git a/include/rsl/variant b/include/rsl/variant index 2a2337b..7b9fb46 100644 --- a/include/rsl/variant +++ b/include/rsl/variant @@ -285,13 +285,11 @@ protected: } public: - constexpr static auto alternatives = - typename[:substitute(^^_impl::MemberAccessor, - nonstatic_data_members_of(dealias(^^Storage), - std::meta::access_context::unchecked()) | - std::views::drop(1) | std::views::transform([](std::meta::info r) { - return reflect_constant(r); - })):](); + constexpr static auto alternatives = [:_impl::cache_members( + nonstatic_data_members_of( + dealias(^^Storage), + std::meta::access_context::unchecked()) | + std::views::drop(1)):]; using index_type = std::conditional_t<(alternatives.count >= 255), unsigned short, unsigned char>; static constexpr auto npos = index_type(-1ULL); @@ -783,29 +781,15 @@ inline constexpr std::size_t variant_npos = _variant_impl::variant_npos; using bad_variant_access = _variant_impl::bad_variant_access; namespace _impl { -// template -// consteval std::meta::info make_storage() { -// union Storage; -// consteval { -// std::size_t idx = 0; -// define_aggregate(^^Storage, -// {data_member_spec(^^char, {.name = "_rsl_dummy"}), -// data_member_spec(^^Ts, {.name = "_rsl_alt" + to_string(idx++)})...}); -// }; -// return ^^Storage; -// } - -// template -// using Storage = [:make_storage...>():]; - template struct Storage { union type; consteval { std::size_t idx = 0; - define_aggregate(^^type, - {data_member_spec(^^char, {.name = "_rsl_dummy"}), - data_member_spec(^^std::remove_cvref_t, {.name = "_rsl_alt" + to_string(idx++)})...}); + define_aggregate( + ^^type, + {data_member_spec(^^char, {.name = "_rsl_dummy"}), + data_member_spec(^^std::remove_cvref_t, {.name = "_rsl_alt" + to_string(idx++)})...}); }; }; } // namespace _impl @@ -874,153 +858,150 @@ using unwrap_type_tag = typename T::type; template constexpr inline _impl::TypeTag type{}; -// namespace _tagged_variant_impl { -// template <_impl::is_enum T> -// consteval std::span make_transitions() { -// std::vector transitions; -// for (auto enumerator : enumerators_of(^^T)) { -// transitions.push_back(extract(enumerator)); -// } -// return define_static_array(transitions); -// } - -// template <_impl::is_enum T> -// consteval std::size_t find_inverse_transition(T value) { -// std::size_t index = 0; -// for (auto enumerator : enumerators_of(^^T)) { -// if (value == extract(enumerator)) { -// return index; -// } -// ++index; -// } -// return -1ULL; -// } - -// template -// constexpr inline auto inverse_transition = find_inverse_transition(V); - -// template <_impl::is_enum E> -// constexpr static auto transitions = _tagged_variant_impl::make_transitions(); - -// template <_impl::is_enum E> -// consteval std::meta::info make_storage() { -// union Storage; -// consteval { -// std::size_t idx = 0; -// auto members = std::vector{data_member_spec(^^char, {.name = "_rsl_dummy"})}; - -// for (auto enumerator : enumerators_of(^^E)) { -// auto annotations = annotations_of(enumerator); -// auto annotation_type = type_of(annotations[0]); -// auto alt_type = dealias(substitute(^^_impl::unwrap_type_tag, {annotation_type})); -// members.push_back(data_member_spec(alt_type, {.name = identifier_of(enumerator)})); -// } -// define_aggregate(^^Storage, members); -// }; -// return ^^Storage; -// } - -// template <_impl::is_enum E> -// using Storage = [:make_storage():]; -// } // namespace _tagged_variant_impl - -// template -// struct tagged_variant : public _variant_impl::variant_base<_tagged_variant_impl::Storage> { -// using storage_type = _tagged_variant_impl::Storage; -// using base = _variant_impl::variant_base; - -// public: -// using _variant_impl::variant_base::variant_base; -// constexpr tagged_variant(tagged_variant const&) = default; -// constexpr tagged_variant(tagged_variant&&) = default; -// constexpr tagged_variant& operator=(tagged_variant const&) = default; -// constexpr tagged_variant& operator=(tagged_variant&&) = default; -// constexpr ~tagged_variant() = default; - -// using tags = E; - -// storage_type* operator->() { return &this->_impl_storage; } -// storage_type const* operator->() const { return &this->_impl_storage; } -// storage_type* operator*() { return &this->_impl_storage; } -// storage_type const* operator*() const { return &this->_impl_storage; } - -// explicit(false) operator E() const { return -// _tagged_variant_impl::transitions[this->index()]; } - -// using base::emplace; -// using base::get; -// using base::get_alt; // TODO hide -// using base::index; -// using base::swap; -// using base::valueless_by_exception; - -// template -// constexpr decltype(auto) visit(this Self&& self, V&& visitor) { -// return rsl::visit(std::forward(visitor), std::forward(self)); -// } -// }; - -// template <_impl::is_enum E> -// constexpr E get_tag(tagged_variant const& variant_) { -// return _tagged_variant_impl::transitions[variant_.index()]; -// } - -// template -// requires(_impl::is_enum) -// constexpr bool holds_alternative(tagged_variant const& variant_) noexcept { -// return _tagged_variant_impl::transitions[variant_.index()] == E; -// } - -// template -// requires(_impl::is_enum && -// std::same_as, std::remove_cvref_t>) -// constexpr decltype(auto) get(V&& variant_) { -// // TODO underconstrained -// return rsl::get<_tagged_variant_impl::inverse_transition>(std::forward(variant_)); -// } - -// template -// requires(_impl::is_enum) -// constexpr auto* get_if(tagged_variant* variant_) noexcept { -// return rsl::get_if<_tagged_variant_impl::inverse_transition>(variant_); -// } - -// namespace _impl { - -// template -// concept all_alts_hashable = std::ranges::all_of( -// rsl::tagged_variant::alternatives, -// std::ranges::transform([](auto R) { return extract(substitute(^^is_hashable, {R})); -// })); - -// template -// concept all_alts_noexcept_hashable = -// std::ranges::all_of(rsl::tagged_variant::alternatives, std::ranges::transform([](auto R) { -// return extract( -// substitute(^^std::is_nothrow_invocable_v, -// {substitute(^^std::hash, remove_const(R))})); -// })); -// } // namespace _impl +namespace _tagged_variant_impl { +template <_impl::is_enum T> +consteval std::span make_transitions() { + std::vector transitions; + for (auto enumerator : enumerators_of(^^T)) { + transitions.push_back(extract(enumerator)); + } + return define_static_array(transitions); +} + +template <_impl::is_enum T> +consteval std::size_t find_inverse_transition(T value) { + std::size_t index = 0; + for (auto enumerator : enumerators_of(^^T)) { + if (value == extract(enumerator)) { + return index; + } + ++index; + } + return -1ULL; +} + +template +constexpr inline auto inverse_transition = find_inverse_transition(V); + +template <_impl::is_enum E> +constexpr static auto transitions = _tagged_variant_impl::make_transitions(); + +template <_impl::is_enum E> +struct Storage { + union type; + consteval { + std::size_t idx = 0; + auto members = std::vector{data_member_spec(^^char, {.name = "_rsl_dummy"})}; + + for (auto enumerator : enumerators_of(^^E)) { + auto annotations = annotations_of(enumerator); + auto annotation_type = type_of(annotations[0]); + auto alt_type = dealias(substitute(^^_impl::unwrap_type_tag, {annotation_type})); + members.push_back(data_member_spec(alt_type, {.name = identifier_of(enumerator)})); + } + define_aggregate(^^type, members); + }; +}; + +} // namespace _tagged_variant_impl + +template +struct tagged_variant : public _variant_impl::variant_base::type> { + using storage_type = _tagged_variant_impl::Storage::type; + using base = _variant_impl::variant_base; + +public: + using _variant_impl::variant_base::variant_base; + constexpr tagged_variant(tagged_variant const&) = default; + constexpr tagged_variant(tagged_variant&&) = default; + constexpr tagged_variant& operator=(tagged_variant const&) = default; + constexpr tagged_variant& operator=(tagged_variant&&) = default; + constexpr ~tagged_variant() = default; + + using tags = E; + + storage_type* operator->() { return &this->_impl_storage; } + storage_type const* operator->() const { return &this->_impl_storage; } + storage_type* operator*() { return &this->_impl_storage; } + storage_type const* operator*() const { return &this->_impl_storage; } + + explicit(false) operator E() const { return + _tagged_variant_impl::transitions[this->index()]; } + + using base::emplace; + using base::get; + using base::get_alt; // TODO hide + using base::index; + using base::swap; + using base::valueless_by_exception; + + template + constexpr decltype(auto) visit(this Self&& self, V&& visitor) { + return rsl::visit(std::forward(visitor), std::forward(self)); + } +}; + +template <_impl::is_enum E> +constexpr E get_tag(tagged_variant const& variant_) { + return _tagged_variant_impl::transitions[variant_.index()]; +} + +template + requires(_impl::is_enum) +constexpr bool holds_alternative(tagged_variant const& variant_) noexcept { + return _tagged_variant_impl::transitions[variant_.index()] == E; +} + +template + requires(_impl::is_enum && + std::same_as, std::remove_cvref_t>) +constexpr decltype(auto) get(V&& variant_) { + // TODO underconstrained + return rsl::get<_tagged_variant_impl::inverse_transition>(std::forward(variant_)); +} + +template + requires(_impl::is_enum) +constexpr auto* get_if(tagged_variant* variant_) noexcept { + return rsl::get_if<_tagged_variant_impl::inverse_transition>(variant_); +} + +namespace _impl { + +template +concept all_alts_hashable = std::ranges::all_of( + rsl::tagged_variant::alternatives, + std::ranges::transform([](auto R) { return extract(substitute(^^is_hashable, {R})); + })); + +template +concept all_alts_noexcept_hashable = + std::ranges::all_of(rsl::tagged_variant::alternatives, std::ranges::transform([](auto R) { + return extract( + substitute(^^std::is_nothrow_invocable_v, + {substitute(^^std::hash, remove_const(R))})); + })); +} // namespace _impl } // namespace rsl -// template -// requires(rsl::_impl::all_alts_hashable) -// struct std::hash> { -// using result_type = std::size_t; - -// std::size_t operator()(rsl::tagged_variant const& obj) const -// noexcept(rsl::_impl::all_alts_noexcept_hashable) { -// if (obj.valueless_by_exception()) { -// constexpr static std::size_t valueless_hash = 0x22c08c8cbcae8fc4; -// return valueless_hash; -// } -// return obj.index() + rsl::visit( -// [](T const& value) { -// return std::hash>{}(value); -// }, -// obj); -// } -// }; +template + requires(rsl::_impl::all_alts_hashable) +struct std::hash> { + using result_type = std::size_t; + + std::size_t operator()(rsl::tagged_variant const& obj) const + noexcept(rsl::_impl::all_alts_noexcept_hashable) { + if (obj.valueless_by_exception()) { + constexpr static std::size_t valueless_hash = 0x22c08c8cbcae8fc4; + return valueless_hash; + } + return obj.index() + rsl::visit( + [](T const& value) { + return std::hash>{}(value); + }, + obj); + } +}; template <> struct std::hash { diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 77ba179..ad9d6ab 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -7,7 +7,7 @@ add_subdirectory(span) add_subdirectory(format) add_subdirectory(expect) add_subdirectory(kwargs) -# add_subdirectory(tagged_variant) +add_subdirectory(tagged_variant) add_subdirectory(serializer) add_subdirectory(variant) diff --git a/test/variant/swap.cpp b/test/variant/swap.cpp index 11aa7cc..e68e739 100644 --- a/test/variant/swap.cpp +++ b/test/variant/swap.cpp @@ -10,7 +10,7 @@ enum class Foo { }; // TODO test tagged_variant as well -using variant_swap_p = ::testing::Types>; //, rsl::tagged_variant>; +using variant_swap_p = ::testing::Types, rsl::tagged_variant>; template struct VariantSwap : public testing::Test { From 4b469bc576cab7c21cfe8d5b102d3b06dc62eb69 Mon Sep 17 00:00:00 2001 From: Tsche Date: Sun, 11 Jan 2026 01:29:00 +0100 Subject: [PATCH 08/14] split and Signed-off-by: Tsche --- include/rsl/annotations | 2 +- include/rsl/enum | 2 +- include/rsl/expect | 2 +- include/rsl/meta | 119 ++++++++++++++++++++++++++++++++++++++++ include/rsl/meta_traits | 82 +-------------------------- include/rsl/serialize | 2 +- 6 files changed, 124 insertions(+), 85 deletions(-) create mode 100644 include/rsl/meta diff --git a/include/rsl/annotations b/include/rsl/annotations index 2efd0ae..3a3e6e0 100644 --- a/include/rsl/annotations +++ b/include/rsl/annotations @@ -1,5 +1,5 @@ #pragma once -#include +#include #include // for preferred_name annotation #include diff --git a/include/rsl/enum b/include/rsl/enum index 39f0ce2..fb08b7d 100644 --- a/include/rsl/enum +++ b/include/rsl/enum @@ -3,7 +3,7 @@ #include #include #include -#include +#include #include diff --git a/include/rsl/expect b/include/rsl/expect index e49a3eb..3407aaf 100644 --- a/include/rsl/expect +++ b/include/rsl/expect @@ -7,7 +7,7 @@ #include #include -#include +#include #include #include diff --git a/include/rsl/meta b/include/rsl/meta new file mode 100644 index 0000000..3fd54a9 --- /dev/null +++ b/include/rsl/meta @@ -0,0 +1,119 @@ +#pragma once +#include +#include +#include + +#include + +namespace rsl::meta { + +consteval bool is_specialization(std::meta::info type, std::meta::info templ) { + if (not is_type(type)) { + throw "not a type"; + } + return has_template_arguments(type) && + template_of(type) == (is_template(templ) ? templ : template_of(templ)); +} + +using std::meta::is_function; + +consteval bool is_member_function(std::meta::info r) { + return is_function(r) && is_class_member(r); +} + +consteval bool is_static_member_function(std::meta::info r) { + return is_member_function(r) && is_static_member(r); +} + +consteval bool is_nonstatic_member_function(std::meta::info r) { + return is_member_function(r) && !is_static_member(r); +} + +namespace _compat { +consteval std::vector annotations_of_with_type(std::meta::info r, + std::meta::info t) { +#if $compiler_is(CLANG) + return std::meta::annotations_of(r, t); +#else + return std::meta::annotations_of_with_type(r, t); +#endif +} +} // namespace _compat + +template +consteval bool has_annotation(std::meta::info item) { + return !_compat::annotations_of_with_type(item, ^^T).empty(); +} + +consteval bool has_annotation(std::meta::info item, std::meta::info type) { + if (is_type(type)) { + return !_compat::annotations_of_with_type(item, type).empty(); + } else if (is_template(type)) { + for (auto annotation : annotations_of(item)) { + if (has_template_arguments(type_of(annotation)) && template_of(type_of(annotation)) == type) { + return true; + } + } + } + return false; +} + +template +consteval bool has_annotation(std::meta::info item, T const& value) { + if (!has_annotation(item)) { + return false; + } + auto annotations = _compat::annotations_of_with_type(item, dealias(^^T)); + auto value_r = std::meta::reflect_constant(value); + return std::ranges::any_of(annotations, [&](auto annotation) { return annotation == value_r; }); +} + +consteval std::vector get_annotations(std::meta::info item, std::meta::info type) { + std::vector annotations; + for (auto annotation : annotations_of(item)) { + if ((is_type(type) && type == type_of(annotation)) || + (is_template(type) && has_template_arguments(type_of(annotation)) && + template_of(type_of(annotation)) == type)) { + annotations.push_back(annotation); + } + } + return annotations; +} + +consteval std::meta::info get_annotation(std::meta::info item, std::meta::info type) { + return get_annotations(item, type)[0]; +} + +consteval bool has_parent(std::meta::info R) { + // HACK remove this once `std::meta::has_parent` is supported in libc++ + return R != ^^::; +} + + +consteval std::meta::info get_member_by_name(std::meta::info r, std::string_view name) { + for (auto member : members_of(r, std::meta::access_context::unprivileged())) { + if (has_identifier(member) && identifier_of(member) == name) { + return member; + } + } + return {}; +} + +namespace _impl { +template +struct injected { + struct type; + consteval { define_aggregate(^^type, {Args...}); }; + + static_assert(is_complete_type(^^type)); +}; +} // namespace _impl + +consteval std::meta::info inject_aggregate(std::meta::reflection_range auto&& fields) { + auto wrapper = substitute(^^_impl::injected, fields | std::views::transform([](auto r) { + return std::meta::reflect_constant(r); + })); + auto type = get_member_by_name(wrapper, "type"); + return is_type(type) ? type : throw "not a type"; +} +} // namespace rsl::meta \ No newline at end of file diff --git a/include/rsl/meta_traits b/include/rsl/meta_traits index df7a16f..d8c0781 100644 --- a/include/rsl/meta_traits +++ b/include/rsl/meta_traits @@ -1,89 +1,9 @@ #pragma once #include -#include -#include +#include namespace rsl::meta { -consteval bool is_specialization(std::meta::info R, std::meta::info Template) { - return has_template_arguments(R) && - template_of(R) == (is_template(Template) ? Template : template_of(Template)); -} - -using std::meta::is_function; - -consteval bool is_member_function(std::meta::info R) { - return is_function(R) && is_class_member(R); -} - -consteval bool is_static_member_function(std::meta::info R) { - return is_member_function(R) && is_static_member(R); -} - -consteval bool is_nonstatic_member_function(std::meta::info R) { - return is_member_function(R) && !is_static_member(R); -} - -namespace _compat { -consteval std::vector annotations_of_with_type(std::meta::info r, - std::meta::info t) { -#if $compiler_is(CLANG) - return std::meta::annotations_of(r, t); -#else - return std::meta::annotations_of_with_type(r, t); -#endif -} -} // namespace _compat - -template -consteval bool has_annotation(std::meta::info item) { - return !_compat::annotations_of_with_type(item, ^^T).empty(); -} - -consteval bool has_annotation(std::meta::info item, std::meta::info type) { - if (is_type(type)) { - return !_compat::annotations_of_with_type(item, type).empty(); - } else if (is_template(type)) { - for (auto annotation : annotations_of(item)) { - if (has_template_arguments(type_of(annotation)) && template_of(type_of(annotation)) == type) { - return true; - } - } - } - return false; -} - -template -consteval bool has_annotation(std::meta::info item, T const& value) { - if (!has_annotation(item)) { - return false; - } - auto annotations = _compat::annotations_of_with_type(item, dealias(^^T)); - auto value_r = std::meta::reflect_constant(value); - return std::ranges::any_of(annotations, [&](auto annotation) { return annotation == value_r; }); -} - -consteval std::vector get_annotations(std::meta::info item, std::meta::info type) { - std::vector annotations; - for (auto annotation : annotations_of(item)) { - if ((is_type(type) && type == type_of(annotation)) || - (is_template(type) && has_template_arguments(type_of(annotation)) && - template_of(type_of(annotation)) == type)) { - annotations.push_back(annotation); - } - } - return annotations; -} - -consteval std::meta::info get_annotation(std::meta::info item, std::meta::info type) { - return get_annotations(item, type)[0]; -} - -consteval bool has_parent(std::meta::info R) { - // HACK remove this once `std::meta::has_parent` is supported in libc++ - return R != ^^::; -} - template concept specializes = is_specialization(^^T, Template); diff --git a/include/rsl/serialize b/include/rsl/serialize index 9f76844..d1f01ea 100644 --- a/include/rsl/serialize +++ b/include/rsl/serialize @@ -1,7 +1,7 @@ #pragma once #include #include -#include +#include #include #include // for to_string(enum-type) From 9788768335fa15de019d6afd8997e089a3f6f0dd Mon Sep 17 00:00:00 2001 From: Tsche Date: Sun, 11 Jan 2026 01:46:53 +0100 Subject: [PATCH 09/14] clean up meta_traits Signed-off-by: Tsche --- include/rsl/expect | 20 +++++++++----------- include/rsl/meta_traits | 21 ++++++++++++++++----- include/rsl/serialize | 4 ++-- include/rsl/serializer/repr.hpp | 2 +- 4 files changed, 28 insertions(+), 19 deletions(-) diff --git a/include/rsl/expect b/include/rsl/expect index 3407aaf..8a38cbb 100644 --- a/include/rsl/expect +++ b/include/rsl/expect @@ -32,7 +32,8 @@ struct Operator { std::meta::operators op; Operator() = delete; - constexpr Operator(std::convertible_to auto str) : op(_serialize_impl::to_operator(str)) {} + constexpr Operator(std::convertible_to auto str) + : op(_serialize_impl::to_operator(str)) {} constexpr Operator(std::meta::operators op) : op(op) {} constexpr ~Operator() {} @@ -58,7 +59,7 @@ constexpr auto make_expr(R rhs_, L lhs_) { } template -concept is_binary_expr = meta::specializes; +concept is_binary_expr = meta::specializes<^^T, ^^BinaryExpr>; template concept is_binary_comparison = is_binary_expr && is_comparison(T::op); @@ -70,7 +71,7 @@ struct BinaryExpr { R rhs; constexpr auto get_left_most() const { - if constexpr (meta::specializes) { + if constexpr (meta::specializes<^^L, ^^BinaryExpr>) { return lhs.get_left_most(); } else { return lhs; @@ -78,7 +79,7 @@ struct BinaryExpr { } constexpr auto get_right_most() const { - if constexpr (meta::specializes) { + if constexpr (meta::specializes<^^R, ^^BinaryExpr>) { return rhs.get_right_most(); } else { return rhs; @@ -134,10 +135,7 @@ struct BinaryExpr { }; if constexpr (rhs_printable && lhs_printable) { - return std::format("({} {} {})", - rhs.to_string(args), - to_string(OP), - lhs.to_string(args)); + return std::format("({} {} {})", rhs.to_string(args), to_string(OP), lhs.to_string(args)); } else if constexpr (rhs_printable) { return std::format("({} {} {})", rhs.to_string(args), to_string(OP), lhs); } else if constexpr (lhs_printable) { @@ -406,8 +404,8 @@ struct Lazy { }; template -concept is_expr_tree = meta::specializes || meta::specializes || - meta::specializes || meta::specializes; +concept is_expr_tree = meta::specializes<^^T, ^^Wrapped> || meta::specializes<^^T, ^^BinaryExpr> || + meta::specializes<^^T, ^^Placeholder> || meta::specializes<^^T, ^^Lazy>; #define $make_op(op) \ template \ @@ -468,7 +466,7 @@ auto lazy(auto... args) { template struct Expect : T { constexpr auto transform() { - if constexpr (meta::specializes) { + if constexpr (meta::specializes<^^T, ^^BinaryExpr>) { return rsl::_expect_impl::Expect{T::transform()}; } else { return *this; diff --git a/include/rsl/meta_traits b/include/rsl/meta_traits index d8c0781..acedb20 100644 --- a/include/rsl/meta_traits +++ b/include/rsl/meta_traits @@ -2,10 +2,11 @@ #include #include -namespace rsl::meta { +namespace rsl { -template -concept specializes = is_specialization(^^T, Template); +namespace meta { +template +concept specializes = is_specialization(T, Template); template concept function = is_function(R); @@ -22,9 +23,19 @@ concept nonstatic_member_function = is_nonstatic_member_function(R); template concept annotated_with = has_annotation(R, T); +template +concept complete_type = is_complete_type(T); + +template +concept incomplete_type = !complete_type; +} // namespace meta + +template +concept specializes = meta::specializes<^^T, Template>; + template -concept complete_type = is_complete_type(^^T); +concept complete_type = meta::complete_type<^^T>; template concept incomplete_type = !complete_type; -} // namespace rsl::meta \ No newline at end of file +} // namespace rsl \ No newline at end of file diff --git a/include/rsl/serialize b/include/rsl/serialize index d1f01ea..e1d656c 100644 --- a/include/rsl/serialize +++ b/include/rsl/serialize @@ -32,10 +32,10 @@ constexpr void serialize(T&& serializer, V&& data) { std::invoke(serializer, serializer::Meta>{}, std::forward(data)); } -template +template constexpr V deserialize(T&& deserializer, R&& data) {} -template +template constexpr void serialize_type(T&& serializer) { return serializer.template operator()<^^V>(); } diff --git a/include/rsl/serializer/repr.hpp b/include/rsl/serializer/repr.hpp index c01daf3..6d42408 100644 --- a/include/rsl/serializer/repr.hpp +++ b/include/rsl/serializer/repr.hpp @@ -375,7 +375,7 @@ consteval std::string get_type_name(NameMode mode) { ret += _impl::namespace_prefix>(); } - if constexpr (rsl::meta::complete_type>) { + if constexpr (complete_type>) { return ret + preferred_name::value; } if constexpr (is_enumerable_type(^^T) && From 9bf098341d9afe0fd1fd2f790e6d4e67daec07ae Mon Sep 17 00:00:00 2001 From: Tsche Date: Sun, 11 Jan 2026 06:18:52 +0100 Subject: [PATCH 10/14] fix tuple Signed-off-by: Tsche --- include/rsl/tuple | 166 +++++++++++++++++++++----------------------- test/CMakeLists.txt | 2 +- 2 files changed, 80 insertions(+), 88 deletions(-) diff --git a/include/rsl/tuple b/include/rsl/tuple index 15d55b7..f1bc866 100644 --- a/include/rsl/tuple +++ b/include/rsl/tuple @@ -53,7 +53,7 @@ struct tuple_element; template struct tuple_element const> { - using type = Types...[I] const; + using type = std::add_const_t; }; template @@ -72,11 +72,11 @@ concept tuple_like = has_template_arguments(remove_cvref(^^T)) && (template_of(remove_cvref(^^T)) == ^^std::array || template_of(remove_cvref(^^T)) == ^^std::complex || template_of(remove_cvref(^^T)) == ^^std::pair || - template_of(remove_cvref(^^T)) == ^^tuple); // exposition only + template_of(remove_cvref(^^T)) == ^^rsl::tuple); // exposition only template concept is_rsl_tuple = - has_template_arguments(remove_cvref(^^T)) && template_of(remove_cvref(^^T)) == ^^tuple; + has_template_arguments(remove_cvref(^^T)) && template_of(remove_cvref(^^T)) == ^^rsl::tuple; template void test_implicit_default_constructibility(T) { @@ -123,6 +123,9 @@ struct Subscript> : SubscriptElement... { } }; #endif + +template +struct type_list {}; } // namespace _tuple_impl template @@ -136,25 +139,32 @@ private: constexpr static bool enable_utypes_ctor = !std::same_as && (std::is_constructible_v && ...); - template <> - constexpr bool enable_utypes_ctor<> = false; - template constexpr static bool enable_utypes_tuple_ctor = true; // TODO - template <> - constexpr bool enable_utypes_tuple_ctor<> = false; - - // FIXME, variadic template for StorageHolder is not needed, but required to workaround a clang - // implementation - template - struct StorageHolder; + struct storage_type; consteval { std::size_t idx = 0; - define_aggregate(^^StorageHolder, + define_aggregate(^^storage_type, {data_member_spec(^^Types, {.name = "_impl_" + to_string(idx++)})...}); } - using storage_type = StorageHolder; + + template + constexpr void _impl_copy_assign(this Self& self, T const& other) { + template for (constexpr auto Idx : + $define_static_array(std::views::iota(0ZU, sizeof...(Types)))) { + self._impl_storage.[:self._impl_accessor.members[Idx]:] = other.template get(); + } + } + + template + constexpr void _impl_move_assign(this Self& self, tuple&& other) { + template for (constexpr auto Idx : + $define_static_array(std::views::iota(0ZU, sizeof...(Types)))) { + self._impl_storage.[:self._impl_accessor.members[Idx]:] = std::forward( + other.template get()); + } + } public: storage_type _impl_storage; @@ -285,106 +295,83 @@ public: //! non-standard: get member function template constexpr decltype(auto) get(this Self&& self) { - return _impl_accessor.template get(std::forward(self)._impl_storage); + return std::forward_like(_impl_accessor.template get(self._impl_storage)); } template constexpr decltype(auto) get(this Self&& self) { - return _impl_accessor.template get<_impl::index_of>>( - std::forward(self)._impl_storage); + return std::forward_like( + _impl_accessor.template get<_impl::index_of>>( + self._impl_storage)); } // [tuple.assign], tuple assignment - constexpr tuple& operator=(const tuple& other) - requires(std::ranges::all_of(_impl_accessor.types, std::meta::is_copy_assignable_type)) + constexpr tuple& operator=(tuple const& other) noexcept( + (std::is_nothrow_copy_assignable_v && ...)) + requires((std::is_copy_assignable_v && ...)) // [tuple.assign]/4 { if (this == &other) { return *this; } - /// TODO: replace with - /// constexpr static auto [...Idx] = std::make_index_sequence<_impl_accessor.count>(); - /// ((get(*this) = get(other)), ...); - /// - template for (constexpr auto el : _impl_accessor.members) { - this->_impl_storage.[:el:] = other._impl_storage.[:el:]; - } + _impl_copy_assign(other); return *this; } + constexpr tuple& operator=(tuple const& other) = delete; - constexpr const tuple& operator=(const tuple& other) const - noexcept(std::ranges::all_of(_impl_accessor.types, - std::meta::is_nothrow_copy_constructible_type)) - requires(std::ranges::all_of(std::ranges::transform(_impl_accessor.types, std::meta::add_const), - std::meta::is_copy_assignable_type)) + constexpr tuple const& operator=(tuple const& other) const + noexcept((std::is_nothrow_copy_assignable_v> && ...)) + requires((std::is_copy_assignable_v> && ...)) // [tuple.assign]/5 { - template for (constexpr auto el : _impl_accessor.members) { - this->_impl_storage.[:el:] = other._impl_storage.[:el:]; - } + _impl_copy_assign(other); return *this; } - constexpr tuple& operator=(tuple&& other) noexcept( - std::ranges::all_of(_impl_accessor.types, std::meta::is_nothrow_move_constructible_type)) - requires(std::ranges::all_of(_impl_accessor.types, - std::meta::is_move_assignable_type)) // tuple.assign-8 + constexpr tuple& operator=(tuple&& other) // + noexcept((std::is_nothrow_move_constructible_v && ...)) // [tuple.assign]/11 + requires((std::is_move_assignable_v && ...)) // [tuple.assign]/8 { - this->_impl_storage = std::move(other._impl_storage); + _impl_move_assign(std::move(other)); return *this; } - constexpr const tuple& operator=(tuple&& other) const - requires(std::ranges::all_of(std::ranges::transform(_impl_accessor.types, std::meta::add_const), - std::meta::is_assignable_type)) // tuple.assign-12 + constexpr tuple const& operator=(tuple&& other) const + requires((std::is_assignable_v && ...)) // [tuple.assign]/12 { - template for (constexpr auto el : define_static_array( - nonstatic_data_members_of(^^storage_type, - std::meta::access_context::current()))) { - this->_impl_storage.[:el:] = std::move(other._impl_storage.[:el:]); - } + _impl_move_assign(std::move(other)); return *this; } - template - requires(sizeof...(UTypes) == sizeof...(Types) && - (std::is_assignable_v && ...)) // tuple-assign-15 - constexpr tuple& operator=(const tuple& other) { - template for (constexpr auto Idx : rsl::make_index_sequence()) { - this->get() = other.template get(); - } + template + requires(sizeof...(UTypes) == sizeof...(Types) && // [tuple.assign]/15.1 + (std::is_assignable_v && ...)) // [tuple.assign]/15.2 + constexpr tuple& operator=(tuple const& other) { + _impl_copy_assign(other); return *this; } - template - requires(sizeof...(UTypes) == sizeof...(Types) && - (std::is_assignable_v && ...)) // tuple-assign-18 - constexpr const tuple& operator=(const tuple& other) const { - template for (constexpr auto Idx : rsl::make_index_sequence()) { - constexpr auto el = _impl_accessor.members[Idx]; - this->_impl_storage.[:el:] = other.template get(); - } + template + requires(sizeof...(UTypes) == sizeof...(Types) && // [tuple.assign]/18.1 + (std::is_assignable_v && ...)) // [tuple.assign]/18.2 + constexpr tuple const& operator=(tuple const& other) const { + _impl_copy_assign(other); return *this; } - template - requires(sizeof...(UTypes) == sizeof...(Types) && - (std::is_assignable_v && ...)) // tuple-assign-21 + template + requires(sizeof...(UTypes) == sizeof...(Types) && // [tuple.assign]/21.1 + (std::is_assignable_v && ...)) // [tuple.assign]/21.2 constexpr tuple& operator=(tuple&& other) { - template for (constexpr auto Idx : rsl::make_index_sequence()) { - this->get() = std::move(other).template get(); - } + _impl_move_assign(std::move(other)); return *this; } - template - requires(sizeof...(UTypes) == sizeof...(Types) && - (std::is_assignable_v && ...)) // tuple-assign-24 + template + requires(sizeof...(UTypes) == sizeof...(Types) && // [tuple.assign]/24.1 + (std::is_assignable_v && ...)) // [tuple.assign]/24.2 constexpr const tuple& operator=(tuple&& other) const { - template for (constexpr auto Idx : rsl::make_index_sequence()) { - constexpr auto el = _impl_accessor.members[Idx]; - this->_impl_storage.[:el:] = std::move(other).template get(); - } + _impl_move_assign(std::move(other)); return *this; } @@ -466,7 +453,7 @@ consteval bool is_nothrow_apply(F&& f, Tuple&& t) { template constexpr decltype(auto) apply(F&& f, Tuple&& t) noexcept( - _tuple_impl::is_nothrow_apply(std::forward(f), std::forward(t))) { + _tuple_impl::is_nothrow_apply(std::declval(), std::declval())) { auto&& [... args] = std::forward(t); return std::invoke(std::forward(f), std::forward_like(args)...); } @@ -480,12 +467,12 @@ constexpr T make_from_tuple(Tuple&& t) { // [tuple.elem], element access template constexpr decltype(auto) get(T&& obj) noexcept { - return std::forward(obj).template get(); + return std::forward_like(obj.template get()); } template constexpr decltype(auto) get(U&& obj) noexcept { - return std::forward(obj).template get(); + return std::forward_like(obj.template get()); } // [tuple.rel], relational operators @@ -537,13 +524,18 @@ constexpr bool operator==(tuple const& rhs, tuple const& l // template // struct std::uses_allocator, Alloc>; -#if $uses_opt(RSL_STD_COMPAT) -template - requires requires { typename rsl::tuple_size; } -struct std::tuple_size : rsl::tuple_size {}; +template +struct std::tuple_size> : std::integral_constant {}; +template +struct std::tuple_size const> + : std::integral_constant {}; -template - requires requires { typename rsl::tuple_element; } -struct std::tuple_element : rsl::tuple_element {}; +template +struct std::tuple_element const> { + using type = std::add_const_t; +}; -#endif +template +struct std::tuple_element> { + using type = Ts...[I]; +}; diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index ad9d6ab..c741e4f 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -11,5 +11,5 @@ add_subdirectory(tagged_variant) add_subdirectory(serializer) add_subdirectory(variant) -# add_subdirectory(tuple) +add_subdirectory(tuple) # add_subdirectory(trie) \ No newline at end of file From 4a71dddfdfb8fcf394257b55b179fa7ee1a25381 Mon Sep 17 00:00:00 2001 From: Tsche Date: Sun, 11 Jan 2026 06:19:04 +0100 Subject: [PATCH 11/14] cleanup Signed-off-by: Tsche --- include/rsl/kwargs | 28 ++++++---------------------- 1 file changed, 6 insertions(+), 22 deletions(-) diff --git a/include/rsl/kwargs b/include/rsl/kwargs index ceca010..3b5621e 100644 --- a/include/rsl/kwargs +++ b/include/rsl/kwargs @@ -9,6 +9,8 @@ #include #include +#include + #include #include @@ -65,32 +67,14 @@ struct Arg { } }; -template -consteval std::meta::info inject_impl() { - struct injected_type; - consteval { - define_aggregate(^^injected_type, {Args...}); - }; - - // ensure injecting the class worked - static_assert(is_complete_type(^^injected_type)); - return ^^injected_type; -} - -consteval std::meta::info inject(std::meta::reflection_range auto&& fields) { - return extract(substitute( - ^^inject_impl, - fields | std::views::transform([](auto r) { return std::meta::reflect_constant(r); })))(); -} - consteval std::meta::info make_detector_type(std::string_view Names) { auto parser = NameParser(Names); if (not parser.parse()) { throw parse_error("could not parse argument list"); } - return inject(parser.names | std::views::transform([](auto name) { - return data_member_spec(^^Arg, {.name = name}); - })); + return rsl::meta::inject_aggregate(parser.names | std::views::transform([](auto name) { + return data_member_spec(^^Arg, {.name = name}); + })); } template @@ -105,7 +89,7 @@ consteval auto inject_wrapper() { args.push_back(data_member_spec(remove_cvref(type), {.name = identifier_of(member)})); } - return inject(args); + return rsl::meta::inject_aggregate(args); } template From 2a305c3f9021582185593a495071dea8a39a9947 Mon Sep 17 00:00:00 2001 From: Tsche Date: Sun, 11 Jan 2026 07:24:46 +0100 Subject: [PATCH 12/14] fix repr Signed-off-by: Tsche --- include/rsl/serializer/machinery.hpp | 5 ++++- include/rsl/serializer/repr.hpp | 33 +++++++++++++++++----------- test/CMakeLists.txt | 18 +++++++-------- test/serializer/CMakeLists.txt | 8 +++---- test/serializer/repr/repr.cpp | 2 +- 5 files changed, 38 insertions(+), 28 deletions(-) diff --git a/include/rsl/serializer/machinery.hpp b/include/rsl/serializer/machinery.hpp index a6b91c7..e6198f4 100644 --- a/include/rsl/serializer/machinery.hpp +++ b/include/rsl/serializer/machinery.hpp @@ -1,7 +1,10 @@ #pragma once #include #include + +#include #include + #include "annotations.hpp" namespace rsl::serializer { @@ -62,7 +65,7 @@ struct Meta { template requires(std::same_as, T>) constexpr void descend(F&& visitor, U&& value) { - template for (constexpr auto Idx : std::views::iota(0ZU, members.size())) { + template for (constexpr auto Idx : $define_static_array(std::views::iota(0ZU, members.size()))) { constexpr auto M = members[Idx]; if constexpr (!meta::has_annotation(M, ^^annotations::Skip)) { std::invoke(visitor, Member{}, value.[:M:]); diff --git a/include/rsl/serializer/repr.hpp b/include/rsl/serializer/repr.hpp index 6d42408..8b537ff 100644 --- a/include/rsl/serializer/repr.hpp +++ b/include/rsl/serializer/repr.hpp @@ -131,7 +131,7 @@ consteval std::string_view name_of(std::meta::info R, NameMode mode = NameMode:: // return define_static_string(extract( // substitute(^^_impl::get_canonical_recurse, {R, std::meta::reflect_constant(mode)}))()); return define_static_string( - extract(substitute(^^type_name, {R, std::meta::reflect_constant(mode)}))); + extract(substitute(^^type_name, {R, std::meta::reflect_constant(mode)}))); } else { // TODO handle qualified/fully_qualified // TODO collapse t.operator() to t()? @@ -195,9 +195,9 @@ class ReprVisitor { constexpr void print_type() { using CleanT = std::remove_cvref_t; switch (opts.names) { - case NameMode::qualified: out += qualified_name; break; - case NameMode::fully_qualified: out += fully_qualified_name; break; - case NameMode::unqualified: out += unqualified_name; break; + case NameMode::qualified: out += qualified_name_of(remove_cvref(^^T)); break; + case NameMode::fully_qualified: out += fully_qualified_name_of(remove_cvref(^^T)); break; + case NameMode::unqualified: out += unqualified_name_of(remove_cvref(^^T)); break; } } @@ -218,7 +218,7 @@ class ReprVisitor { template constexpr void operator()(R meta, T&& value) { print_separator(); - print_type>(); + print_type(); increase_nesting(); meta.descend(*this, std::forward(value)); decrease_nesting(); @@ -227,14 +227,13 @@ class ReprVisitor { template constexpr void operator()(R meta, T&& value) { print_separator(); - print_type>(); + print_type(); increase_nesting(); meta.descend(*this, std::forward(value)); decrease_nesting(); } - template - constexpr void operator()(R, std::convertible_to auto const& value) { + constexpr void operator()(auto, std::convertible_to auto const& value) { print_separator(); out += '"'; out += std::string(value); // allowed in C++26 constexpr @@ -261,9 +260,9 @@ class ReprVisitor { decrease_nesting(); } - template + template requires std::is_floating_point_v> - constexpr void operator()(R, T value) { + constexpr void operator()(auto, T value) { print_separator(); std::array buffer{}; @@ -286,9 +285,9 @@ class ReprVisitor { out += result; } - template + template requires std::is_integral_v> - constexpr void operator()(R, T value) { + constexpr void operator()(auto, T value) { print_separator(); std::array buffer{}; // Enough for any 64-bit integer including sign auto [ptr, ec] = std::to_chars(buffer.data(), buffer.data() + buffer.size(), value); @@ -337,6 +336,9 @@ consteval std::string_view stringify_template_args(std::meta::info R, if (!can_substitute(template_of(R), args | std::views::take(required))) { break; } + if (substitute(template_of(R), args | std::views::take(required)) != R) { + break; + } } } ++required; @@ -349,10 +351,13 @@ consteval std::string_view stringify_template_args(std::meta::info R, ret += ", "; } if (is_type(arg)) { - ret += extract( + ret += extract( substitute(^^type_name, {arg, std::meta::reflect_constant(mode)})); } else if (is_value(arg) || is_object(arg)) { ret += _impl::stringify_constant(arg, mode); + } else if (has_identifier(arg)) { + // TODO allow customization for templates? + ret += identifier_of(arg); } else { ret += display_string_of(arg); } @@ -412,6 +417,8 @@ consteval std::string get_type_name(NameMode mode) { } ret += stringify_template_args(^^T, mode); return ret; + } else if constexpr (has_identifier(^^T)) { + return ret + identifier_of(^^T); } else { return ret + display_string_of(^^T); } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index c741e4f..744748c 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,15 +1,15 @@ target_sources(rsl-util-test PRIVATE main.cpp) target_include_directories(rsl-util-test PRIVATE ${CMAKE_CURRENT_LIST_DIR}) -add_subdirectory(enum) -add_subdirectory(string_view) -add_subdirectory(span) -add_subdirectory(format) -add_subdirectory(expect) -add_subdirectory(kwargs) -add_subdirectory(tagged_variant) +# add_subdirectory(enum) +# add_subdirectory(string_view) +# add_subdirectory(span) +# add_subdirectory(format) +# add_subdirectory(expect) +# add_subdirectory(kwargs) +# add_subdirectory(tagged_variant) +# add_subdirectory(variant) +# add_subdirectory(tuple) add_subdirectory(serializer) -add_subdirectory(variant) -add_subdirectory(tuple) # add_subdirectory(trie) \ No newline at end of file diff --git a/test/serializer/CMakeLists.txt b/test/serializer/CMakeLists.txt index fb19038..5fc8aab 100644 --- a/test/serializer/CMakeLists.txt +++ b/test/serializer/CMakeLists.txt @@ -1,6 +1,6 @@ -# target_sources(rsl-util-test PRIVATE -# canonical_name.cpp -# ) +target_sources(rsl-util-test PRIVATE + canonical_name.cpp +) -# add_subdirectory(repr) +add_subdirectory(repr) add_subdirectory(to_string) \ No newline at end of file diff --git a/test/serializer/repr/repr.cpp b/test/serializer/repr/repr.cpp index eb0f403..89b9db4 100644 --- a/test/serializer/repr/repr.cpp +++ b/test/serializer/repr/repr.cpp @@ -64,5 +64,5 @@ TEST(Repr, Iterables) { } TEST(Repr, Unsupported) { - ASSERT_EQ(rsl::repr(TestNS::Unsupported(3)), "Unsupported{/*...*/}"); + ASSERT_EQ(rsl::repr(TestNS::Unsupported(3), {.names=rsl::NameMode::unqualified}), "Unsupported{/*...*/}"); } \ No newline at end of file From 239456cef7b0923f93fe48f08f186769d5ea2cb4 Mon Sep 17 00:00:00 2001 From: Tsche Date: Sun, 11 Jan 2026 07:51:54 +0100 Subject: [PATCH 13/14] work around GCC ice --- include/rsl/serializer/repr.hpp | 17 +++++++++++------ 1 file changed, 11 insertions(+), 6 deletions(-) diff --git a/include/rsl/serializer/repr.hpp b/include/rsl/serializer/repr.hpp index 8b537ff..97ae2df 100644 --- a/include/rsl/serializer/repr.hpp +++ b/include/rsl/serializer/repr.hpp @@ -203,6 +203,7 @@ class ReprVisitor { public: constexpr explicit ReprVisitor(Options opts = {}) : opts(opts) {} + constexpr ~ReprVisitor() = default; constexpr std::string finalize() const { return out; } @@ -311,17 +312,19 @@ class ReprVisitor { }; namespace _impl { -template +template consteval std::string stringify_constant_impl() { auto visitor = ReprVisitor{{.names = mode}}; - rsl::serialize(visitor, [:R:]); + rsl::serialize(visitor, Value); return visitor.finalize(); } -consteval std::string stringify_constant(std::meta::info R, NameMode mode = NameMode::unqualified) { - return extract( - substitute(^^stringify_constant_impl, - {reflect_constant(R), std::meta::reflect_constant(mode)}))(); +template +constexpr std::string_view stringified_constant = define_static_string(stringify_constant_impl()); + +consteval std::string_view stringify_constant(std::meta::info R, NameMode mode = NameMode::unqualified) { + auto meta = substitute(^^stringified_constant, {R, std::meta::reflect_constant(mode)}); + return extract(meta); } } // namespace _impl @@ -354,7 +357,9 @@ consteval std::string_view stringify_template_args(std::meta::info R, ret += extract( substitute(^^type_name, {arg, std::meta::reflect_constant(mode)})); } else if (is_value(arg) || is_object(arg)) { + // TODO ret += _impl::stringify_constant(arg, mode); + // ret += display_string_of(arg); } else if (has_identifier(arg)) { // TODO allow customization for templates? ret += identifier_of(arg); From 6c469a720683fee1f5248869157b90a9ae147e50 Mon Sep 17 00:00:00 2001 From: Tsche Date: Mon, 12 Jan 2026 01:09:14 +0100 Subject: [PATCH 14/14] cleanup --- include/rsl/_impl/macro/diagnostic.hpp | 2 +- include/rsl/meta | 1 - include/rsl/tuple | 15 ++++++++++----- test/CMakeLists.txt | 18 +++++++++--------- test/tagged_variant/access.cpp | 6 +++--- test/tagged_variant/switch.cpp | 10 +++++----- 6 files changed, 28 insertions(+), 24 deletions(-) diff --git a/include/rsl/_impl/macro/diagnostic.hpp b/include/rsl/_impl/macro/diagnostic.hpp index 7dc5fb8..a8aa116 100644 --- a/include/rsl/_impl/macro/diagnostic.hpp +++ b/include/rsl/_impl/macro/diagnostic.hpp @@ -39,7 +39,7 @@ # define RSL_DIAGS_literal_suffix "-Wliteral-suffix" # define RSL_DIAGS_narrowing "-Wnarrowing" #elif RSL_COMPILER == RSL_COMPILER_CLANG -# define RSL_DIAGS_literal_suffix "-Wliteral-suffix" +# define RSL_DIAGS_literal_suffix "-Wuser-defined-literals" # define RSL_DIAGS_narrowing "-Wc++11-narrowing" #else # warning "Unsupported compiler" diff --git a/include/rsl/meta b/include/rsl/meta index 3fd54a9..e2825c5 100644 --- a/include/rsl/meta +++ b/include/rsl/meta @@ -89,7 +89,6 @@ consteval bool has_parent(std::meta::info R) { return R != ^^::; } - consteval std::meta::info get_member_by_name(std::meta::info r, std::string_view name) { for (auto member : members_of(r, std::meta::access_context::unprivileged())) { if (has_identifier(member) && identifier_of(member) == name) { diff --git a/include/rsl/tuple b/include/rsl/tuple index f1bc866..af2bd12 100644 --- a/include/rsl/tuple +++ b/include/rsl/tuple @@ -123,9 +123,6 @@ struct Subscript> : SubscriptElement... { } }; #endif - -template -struct type_list {}; } // namespace _tuple_impl template @@ -137,12 +134,20 @@ class tuple private: template constexpr static bool enable_utypes_ctor = - !std::same_as && (std::is_constructible_v && ...); + not(sizeof...(Us) == 1 && (std::same_as && ...)) && + (std::is_constructible_v && ...); template constexpr static bool enable_utypes_tuple_ctor = true; // TODO +#if $compiler_is(CLANG) + // workaround for use as template argument + template + struct Storage; + using storage_type = Storage; +#else struct storage_type; +#endif consteval { std::size_t idx = 0; define_aggregate(^^storage_type, @@ -153,7 +158,7 @@ private: constexpr void _impl_copy_assign(this Self& self, T const& other) { template for (constexpr auto Idx : $define_static_array(std::views::iota(0ZU, sizeof...(Types)))) { - self._impl_storage.[:self._impl_accessor.members[Idx]:] = other.template get(); + self._impl_storage.[:self._impl_accessor.members[Idx]:] = other.template get(); } } diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 744748c..38d08df 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -1,15 +1,15 @@ target_sources(rsl-util-test PRIVATE main.cpp) target_include_directories(rsl-util-test PRIVATE ${CMAKE_CURRENT_LIST_DIR}) -# add_subdirectory(enum) -# add_subdirectory(string_view) -# add_subdirectory(span) -# add_subdirectory(format) -# add_subdirectory(expect) -# add_subdirectory(kwargs) -# add_subdirectory(tagged_variant) -# add_subdirectory(variant) -# add_subdirectory(tuple) +add_subdirectory(enum) +add_subdirectory(string_view) +add_subdirectory(span) +add_subdirectory(format) +add_subdirectory(expect) +add_subdirectory(kwargs) +add_subdirectory(tagged_variant) +add_subdirectory(variant) +add_subdirectory(tuple) add_subdirectory(serializer) # add_subdirectory(trie) \ No newline at end of file diff --git a/test/tagged_variant/access.cpp b/test/tagged_variant/access.cpp index a811f7f..02550d7 100644 --- a/test/tagged_variant/access.cpp +++ b/test/tagged_variant/access.cpp @@ -18,11 +18,11 @@ enum UnscopedEnum { // }; TEST(TaggedVariant, GetTag) { - using type = rsl::tagged_variant; - type variant{42}; + using variant_type = rsl::tagged_variant; + variant_type variant{42}; ASSERT_EQ(get_tag(variant), ScopedEnum::INT); - variant = type{std::in_place_index<2>, false}; + variant = variant_type{std::in_place_index<2>, false}; ASSERT_EQ(get_tag(variant), ScopedEnum::BOOL); } diff --git a/test/tagged_variant/switch.cpp b/test/tagged_variant/switch.cpp index b57caad..a097b2c 100644 --- a/test/tagged_variant/switch.cpp +++ b/test/tagged_variant/switch.cpp @@ -21,18 +21,18 @@ enum UnscopedEnum { // } TEST(TaggedVariant, Switch) { - using type = rsl::tagged_variant; - type variant{42}; + using variant_type = rsl::tagged_variant; + variant_type variant{42}; switch (variant) { - using enum type::tags; + using enum variant_type::tags; case INT: ASSERT_EQ(get(variant), 42); break; case CHAR: FAIL(); case BOOL: FAIL(); } - variant = type{std::in_place_index<2>, false}; + variant = variant_type{std::in_place_index<2>, false}; switch (variant) { - using enum type::tags; + using enum variant_type::tags; case INT: FAIL(); case CHAR: FAIL(); case BOOL: ASSERT_EQ(variant->BOOL, false); break;