From bfae4a49394f27b49c6570ba71dbe94c967ac391 Mon Sep 17 00:00:00 2001 From: Tsche Date: Tue, 10 Mar 2026 20:45:52 +0100 Subject: [PATCH 01/23] init docs --- docs/antora.yml | 11 + docs/api/meta | 514 ++++++++ docs/api/rsl/_impl/traits.hpp | 52 + docs/api/rsl/annotations | 23 + docs/api/rsl/assert | 165 +++ docs/api/rsl/constant_sequence | 76 ++ docs/api/rsl/constant_wrapper | 290 +++++ docs/api/rsl/enum.h | 212 ++++ docs/api/rsl/expect | 571 +++++++++ docs/api/rsl/format.h | 36 + docs/api/rsl/kwargs | 167 +++ docs/api/rsl/macro.h | 59 + docs/api/rsl/meta.h | 34 + docs/api/rsl/meta_traits.h | 41 + docs/api/rsl/print.h | 12 + docs/api/rsl/repr | 70 ++ docs/api/rsl/serialize | 124 ++ docs/api/rsl/source_location.h | 65 ++ docs/api/rsl/span | 408 +++++++ docs/api/rsl/string_constant | 29 + docs/api/rsl/string_util | 41 + docs/api/rsl/string_view | 423 +++++++ docs/api/rsl/trie | 187 +++ docs/api/rsl/tuple | 588 ++++++++++ docs/api/rsl/variant | 1035 +++++++++++++++++ docs/modules/ROOT/nav.adoc | 0 docs/modules/ROOT/pages/index.adoc | 2 + docs/modules/annotation/nav.adoc | 1 + docs/modules/annotation/pages/index.adoc | 1 + docs/modules/api/README.md | 1 + docs/modules/collections/nav.adoc | 5 + docs/modules/collections/pages/index.adoc | 1 + docs/modules/platform/nav.adoc | 1 + docs/modules/platform/pages/index.adoc | 1 + docs/modules/serialization/nav.adoc | 4 + .../serialization/pages/customization.adoc | 1 + docs/modules/serialization/pages/design.adoc | 1 + docs/modules/serialization/pages/index.adoc | 1 + docs/modules/utilities/nav.adoc | 5 + docs/modules/utilities/pages/index.adoc | 1 + docs/mrdocs.yml | 12 + docs/postprocess.py | 361 ++++++ include/rsl/format | 4 + 43 files changed, 5636 insertions(+) create mode 100644 docs/antora.yml create mode 100644 docs/api/meta create mode 100644 docs/api/rsl/_impl/traits.hpp create mode 100644 docs/api/rsl/annotations create mode 100644 docs/api/rsl/assert create mode 100644 docs/api/rsl/constant_sequence create mode 100644 docs/api/rsl/constant_wrapper create mode 100644 docs/api/rsl/enum.h create mode 100644 docs/api/rsl/expect create mode 100644 docs/api/rsl/format.h create mode 100644 docs/api/rsl/kwargs create mode 100644 docs/api/rsl/macro.h create mode 100644 docs/api/rsl/meta.h create mode 100644 docs/api/rsl/meta_traits.h create mode 100644 docs/api/rsl/print.h create mode 100644 docs/api/rsl/repr create mode 100644 docs/api/rsl/serialize create mode 100644 docs/api/rsl/source_location.h create mode 100644 docs/api/rsl/span create mode 100644 docs/api/rsl/string_constant create mode 100644 docs/api/rsl/string_util create mode 100644 docs/api/rsl/string_view create mode 100644 docs/api/rsl/trie create mode 100644 docs/api/rsl/tuple create mode 100644 docs/api/rsl/variant create mode 100644 docs/modules/ROOT/nav.adoc create mode 100644 docs/modules/ROOT/pages/index.adoc create mode 100644 docs/modules/annotation/nav.adoc create mode 100644 docs/modules/annotation/pages/index.adoc create mode 100644 docs/modules/api/README.md create mode 100644 docs/modules/collections/nav.adoc create mode 100644 docs/modules/collections/pages/index.adoc create mode 100644 docs/modules/platform/nav.adoc create mode 100644 docs/modules/platform/pages/index.adoc create mode 100644 docs/modules/serialization/nav.adoc create mode 100644 docs/modules/serialization/pages/customization.adoc create mode 100644 docs/modules/serialization/pages/design.adoc create mode 100644 docs/modules/serialization/pages/index.adoc create mode 100644 docs/modules/utilities/nav.adoc create mode 100644 docs/modules/utilities/pages/index.adoc create mode 100644 docs/mrdocs.yml create mode 100644 docs/postprocess.py diff --git a/docs/antora.yml b/docs/antora.yml new file mode 100644 index 0000000..beeefc6 --- /dev/null +++ b/docs/antora.yml @@ -0,0 +1,11 @@ +name: rsl +version: ~ +title: rsl +nav: +- modules/ROOT/nav.adoc +- modules/annotation/nav.adoc +- modules/collections/nav.adoc +- modules/platform/nav.adoc +- modules/serialization/nav.adoc +- modules/utilities/nav.adoc +- modules/api/nav.adoc \ No newline at end of file diff --git a/docs/api/meta b/docs/api/meta new file mode 100644 index 0000000..9d4bf6e --- /dev/null +++ b/docs/api/meta @@ -0,0 +1,514 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +namespace std { +// [meta.string.literal], checking string literals +consteval bool is_string_literal(const char *__p); +consteval bool is_string_literal(const wchar_t *__p); +consteval bool is_string_literal(const char8_t *__p); + +consteval bool is_string_literal(const char16_t *__p); + +consteval bool is_string_literal(const char32_t *__p); + +namespace meta { +using info = struct {}; + +// [meta.reflection.exception], class exception +class exception : public std::exception { +public: + consteval exception( + u8string_view __what, info __from, + source_location __where = source_location::current()) noexcept; + + consteval exception( + string_view __what, info __from, + source_location __where = source_location::current()) noexcept; + + consteval exception(const exception &) = default; + consteval exception(exception &&) = default; + + consteval exception &operator=(const exception &) = default; + consteval exception &operator=(exception &&) = default; + constexpr const char *what() const noexcept override; + consteval u8string_view u8what() const noexcept; + consteval info from() const noexcept; + consteval source_location where() const noexcept; +}; + +// [meta.reflection.operators], operator representations +enum class operators { + op_new = 1, + op_delete, + op_array_new, + op_array_delete, + op_co_await, + op_parentheses, + op_square_brackets, + op_arrow, + op_arrow_star, + op_tilde, + op_exclamation, + op_plus, + op_minus, + op_star, + op_slash, + op_percent, + op_caret, + op_ampersand, + op_equals, + op_pipe, + op_plus_equals, + op_minus_equals, + op_star_equals, + op_slash_equals, + op_percent_equals, + op_caret_equals, + op_ampersand_equals, + op_pipe_equals, + op_equals_equals, + op_exclamation_equals, + op_less, + op_greater, + op_less_equals, + op_greater_equals, + op_spaceship, + op_ampersand_ampersand, + op_pipe_pipe, + op_less_less, + op_greater_greater, + op_less_less_equals, + op_greater_greater_equals, + op_plus_plus, + op_minus_minus, + op_comma +}; +using enum operators; +consteval operators operator_of(info); +consteval string_view symbol_of(operators); +consteval u8string_view u8symbol_of(operators); + +// [meta.reflection.names], reflection names and locations +consteval bool has_identifier(info); + +consteval string_view identifier_of(info); +consteval u8string_view u8identifier_of(info); + +consteval string_view display_string_of(info); +consteval u8string_view u8display_string_of(info); + +consteval source_location source_location_of(info); + +// [meta.reflection.queries], reflection queries +consteval info type_of(info); +consteval info object_of(info); +consteval info constant_of(info); + +consteval bool is_public(info); +consteval bool is_protected(info); +consteval bool is_private(info); + +consteval bool is_virtual(info); +consteval bool is_pure_virtual(info); +consteval bool is_override(info); +consteval bool is_final(info); + +consteval bool is_deleted(info); +consteval bool is_defaulted(info); +consteval bool is_user_provided(info); +consteval bool is_user_declared(info); +consteval bool is_explicit(info); +consteval bool is_noexcept(info); + +consteval bool is_bit_field(info); +consteval bool is_enumerator(info); +consteval bool is_annotation(info); + +consteval bool is_const(info); +consteval bool is_volatile(info); +consteval bool is_mutable_member(info); +consteval bool is_lvalue_reference_qualified(info); +consteval bool is_rvalue_reference_qualified(info); + +consteval bool has_static_storage_duration(info); +consteval bool has_thread_storage_duration(info); +consteval bool has_automatic_storage_duration(info); + +consteval bool has_internal_linkage(info); +consteval bool has_module_linkage(info); +consteval bool has_external_linkage(info); +consteval bool has_c_language_linkage(info); +consteval bool has_linkage(info); + +consteval bool is_complete_type(info); +consteval bool is_enumerable_type(info); + +consteval bool is_variable(info); +consteval bool is_type(info); +consteval bool is_namespace(info); +consteval bool is_type_alias(info); +consteval bool is_namespace_alias(info); + +consteval bool is_function(info); +consteval bool is_conversion_function(info); +consteval bool is_operator_function(info); +consteval bool is_literal_operator(info); +consteval bool is_special_member_function(info); +consteval bool is_constructor(info); +consteval bool is_default_constructor(info); +consteval bool is_copy_constructor(info); +consteval bool is_move_constructor(info); +consteval bool is_assignment(info); +consteval bool is_copy_assignment(info); +consteval bool is_move_assignment(info); +consteval bool is_destructor(info); + +consteval bool is_function_parameter(info); +consteval bool is_explicit_object_parameter(info); +consteval bool has_default_argument(info); +consteval bool has_ellipsis_parameter(info); + +consteval bool is_template(info); +consteval bool is_function_template(info); +consteval bool is_variable_template(info); +consteval bool is_class_template(info); +consteval bool is_alias_template(info); +consteval bool is_conversion_function_template(info); +consteval bool is_operator_function_template(info); +consteval bool is_literal_operator_template(info); +consteval bool is_constructor_template(info); +consteval bool is_concept(info); + +consteval bool is_value(info); +consteval bool is_object(info); + +consteval bool is_structured_binding(info); + +consteval bool is_class_member(info); +consteval bool is_namespace_member(info); +consteval bool is_nonstatic_data_member(info); +consteval bool is_static_member(info); +consteval bool is_base(info); + +consteval bool has_default_member_initializer(info); + +consteval bool has_parent(info); +consteval info parent_of(info); + +consteval info dealias(info); + +consteval bool has_template_arguments(info); +consteval info template_of(info); +consteval vector template_arguments_of(info); +consteval vector parameters_of(info); +consteval info variable_of(info); +consteval info return_type_of(info); + +// [meta.reflection.access.context], access control context +struct access_context { +private: + consteval access_context(info __scope, info __designating_class) noexcept; +public: + access_context() = delete; + consteval access_context(const access_context &) = default; + consteval access_context(access_context &&) = default; + + consteval info scope() const; + consteval info designating_class() const; + + static consteval access_context current() noexcept; + static consteval access_context unprivileged() noexcept; + static consteval access_context unchecked() noexcept; + consteval access_context via(info) const; +}; + +// [meta.reflection.access.queries], member accessibility queries +consteval bool is_accessible(info, access_context); +consteval bool has_inaccessible_nonstatic_data_members(info, access_context); +consteval bool has_inaccessible_bases(info, access_context); +consteval bool has_inaccessible_subobjects(info, access_context); + +// [meta.reflection.member.queries], reflection member queries +consteval vector members_of(info, access_context); +consteval vector bases_of(info, access_context); +consteval vector static_data_members_of(info, access_context); +consteval vector nonstatic_data_members_of(info, access_context); +consteval vector subobjects_of(info, access_context); +consteval vector enumerators_of(info); + +// [meta.reflection.layout], reflection layout queries +struct member_offset { + ptrdiff_t bytes; + ptrdiff_t bits; + + constexpr ptrdiff_t total_bits() const { return bytes * __CHAR_BIT__ + bits; } + + auto operator<=>(const member_offset &) const = default; +}; + +consteval member_offset offset_of(info); +consteval size_t size_of(info); +consteval size_t alignment_of(info); +consteval size_t bit_size_of(info); + +// [meta.reflection.extract], value extraction +template consteval _Tp extract(info); + +// [meta.reflection.substitute], reflection substitution +template +concept reflection_range = + ranges::input_range<_Rg> && same_as, info> && + same_as>, info>; + +template > +consteval bool can_substitute(info, _Rg &&); +template > +consteval info substitute(info, _Rg &&); + +// [meta.reflection.result], expression result reflection +template + requires(is_copy_constructible_v<_Tp>) +consteval info reflect_constant(_Tp); +template + requires(!is_function_v>) +consteval info reflect_object(_Tp &); +template + requires(is_function_v>) +consteval info reflect_function(_Tp &); + +// [meta.reflection.array], promoting to static storage arrays +template +consteval info reflect_constant_string(_Rg &&); + +template +consteval info reflect_constant_array(_Rg &&); + +// [meta.reflection.define.aggregate], class definition generation +struct data_member_options { + struct _Name { + template + requires constructible_from + consteval _Name(_Tp &&__n) : _M_is_u8(true), _M_u8s((_Tp &&)__n) {} + + template + requires constructible_from + consteval _Name(_Tp &&__n) : _M_is_u8(false), _M_s((_Tp &&)__n) {} + + private: + bool _M_is_u8; + u8string _M_u8s; + string _M_s; + info _M_unused = {}; + }; + + optional<_Name> name; + optional alignment; + optional bit_width; + bool no_unique_address = false; +}; +consteval info data_member_spec(info, data_member_options); +consteval bool is_data_member_spec(info); +template > +consteval info define_aggregate(info, _Rg &&); + +// associated with [meta.unary.cat], primary type categories +consteval bool is_void_type(info); +consteval bool is_null_pointer_type(info); +consteval bool is_integral_type(info); +consteval bool is_floating_point_type(info); +consteval bool is_array_type(info); +consteval bool is_pointer_type(info); +consteval bool is_lvalue_reference_type(info); +consteval bool is_rvalue_reference_type(info); +consteval bool is_member_object_pointer_type(info); +consteval bool is_member_function_pointer_type(info); +consteval bool is_enum_type(info); +consteval bool is_union_type(info); +consteval bool is_class_type(info); +consteval bool is_function_type(info); +consteval bool is_reflection_type(info); + +// associated with [meta.unary.comp], composite type categories +consteval bool is_reference_type(info); +consteval bool is_arithmetic_type(info); +consteval bool is_fundamental_type(info); +consteval bool is_object_type(info); +consteval bool is_scalar_type(info); +consteval bool is_compound_type(info); +consteval bool is_member_pointer_type(info); + +// associated with [meta.unary.prop], type properties +consteval bool is_const_type(info); +consteval bool is_volatile_type(info); +consteval bool is_trivially_copyable_type(info); +consteval bool is_standard_layout_type(info); +consteval bool is_empty_type(info); +consteval bool is_polymorphic_type(info); +consteval bool is_abstract_type(info); +consteval bool is_final_type(info); +consteval bool is_aggregate_type(info); +consteval bool is_consteval_only_type(info); +consteval bool is_signed_type(info); +consteval bool is_unsigned_type(info); +consteval bool is_bounded_array_type(info); +consteval bool is_unbounded_array_type(info); +consteval bool is_scoped_enum_type(info); + +template > +consteval bool is_constructible_type(info, _Rg &&); +consteval bool is_default_constructible_type(info); +consteval bool is_copy_constructible_type(info); +consteval bool is_move_constructible_type(info); + +consteval bool is_assignable_type(info, info); +consteval bool is_copy_assignable_type(info); +consteval bool is_move_assignable_type(info); + +consteval bool is_swappable_with_type(info, info); +consteval bool is_swappable_type(info); + +consteval bool is_destructible_type(info); + +template > +consteval bool is_trivially_constructible_type(info, _Rg &&); +consteval bool is_trivially_default_constructible_type(info); +consteval bool is_trivially_copy_constructible_type(info); +consteval bool is_trivially_move_constructible_type(info); + +consteval bool is_trivially_assignable_type(info, info); +consteval bool is_trivially_copy_assignable_type(info); +consteval bool is_trivially_move_assignable_type(info); +consteval bool is_trivially_destructible_type(info); + +template > +consteval bool is_nothrow_constructible_type(info, _Rg &&); +consteval bool is_nothrow_default_constructible_type(info); +consteval bool is_nothrow_copy_constructible_type(info); +consteval bool is_nothrow_move_constructible_type(info); + +consteval bool is_nothrow_assignable_type(info, info); +consteval bool is_nothrow_copy_assignable_type(info); +consteval bool is_nothrow_move_assignable_type(info); + +consteval bool is_nothrow_swappable_with_type(info, info); +consteval bool is_nothrow_swappable_type(info); + +consteval bool is_nothrow_destructible_type(info); + +consteval bool is_implicit_lifetime_type(info); + +consteval bool has_virtual_destructor(info); + +consteval bool has_unique_object_representations(info); + +consteval bool reference_constructs_from_temporary(info, info); +consteval bool reference_converts_from_temporary(info, info); + +// associated with [meta.unary.prop.query], type property queries +consteval size_t rank(info); +consteval size_t extent(info, unsigned = 0); + +// associated with [meta.rel], type relations +consteval bool is_same_type(info, info); +consteval bool is_base_of_type(info, info); +consteval bool is_virtual_base_of_type(info, info); +consteval bool is_convertible_type(info, info); +consteval bool is_nothrow_convertible_type(info, info); +consteval bool is_layout_compatible_type(info, info); +consteval bool is_pointer_interconvertible_base_of_type(info, info); + +template > +consteval bool is_invocable_type(info, _Rg &&); +template > +consteval bool is_invocable_r_type(info, info, _Rg &&); + +template > +consteval bool is_nothrow_invocable_type(info, _Rg &&); +template > +consteval bool is_nothrow_invocable_r_type(info, info, _Rg &&); + +// associated with [meta.trans.cv], const-volatile modifications +consteval info remove_const(info); +consteval info remove_volatile(info); +consteval info remove_cv(info); +consteval info add_const(info); +consteval info add_volatile(info); +consteval info add_cv(info); + +// associated with [meta.trans.ref], reference modifications +consteval info remove_reference(info); +consteval info add_lvalue_reference(info); +consteval info add_rvalue_reference(info); + +// associated with [meta.trans.sign], sign modifications +consteval info make_signed(info); +consteval info make_unsigned(info); + +// associated with [meta.trans.arr], array modifications +consteval info remove_extent(info); +consteval info remove_all_extents(info); + +// associated with [meta.trans.ptr], pointer modifications +consteval info remove_pointer(info); +consteval info add_pointer(info); + +// associated with [meta.trans.other], other transformations +consteval info remove_cvref(info); +consteval info decay(info); +template > +consteval info common_type(_Rg &&); +template > +consteval info common_reference(_Rg &&); +consteval info underlying_type(info); +template > +consteval info invoke_result(info, _Rg &&); +consteval info unwrap_reference(info); +consteval info unwrap_ref_decay(info); + +consteval size_t tuple_size(info); +consteval info tuple_element(size_t, info); + +consteval size_t variant_size(info); +consteval info variant_alternative(size_t, info); + +consteval strong_ordering type_order(info, info); + +// [meta.reflection.annotation], annotation reflection +consteval vector annotations_of(info); +consteval vector annotations_of_with_type(info, info); +} // namespace meta + +// [meta.define.static], promoting to static storage strings +template +consteval const ranges::range_value_t<_Rg> *define_static_string(_Rg &&__r); + +template +consteval span> +define_static_array(_Rg &&__r); + +template +consteval const remove_cvref_t<_Tp> *define_static_object(_Tp &&__t); + +} // namespace std + + + +consteval std::meta::info operator->*(std::meta::info r, std::meta::info(*)()) { return r; } +template +consteval std::meta::info _do_lift() { return {}; } +template +consteval std::meta::info _do_lift() { return {}; } +namespace _impl_invalid { + constexpr auto V = [](auto){}; + constexpr auto T = [](auto){}; +} + +#define $lift(x) std::meta::info()->*_do_lift +#define $splice(x) decltype(_impl_invalid::T(x)) \ No newline at end of file diff --git a/docs/api/rsl/_impl/traits.hpp b/docs/api/rsl/_impl/traits.hpp new file mode 100644 index 0000000..dfd5732 --- /dev/null +++ b/docs/api/rsl/_impl/traits.hpp @@ -0,0 +1,52 @@ +#pragma once +#include + +namespace rsl::_impl { +template +concept is_hashable = + std::is_default_constructible_v> + && std::is_copy_constructible_v> + && std::is_move_constructible_v> + && std::is_invocable_r_v, T const&>; + +template +concept is_void = std::is_void_v; + +template +concept is_non_void = !is_void; + +template +inline const bool is_signed_integer_v = false; +template <> +inline const bool is_signed_integer_v = true; +template <> +inline const bool is_signed_integer_v = true; +template <> +inline const bool is_signed_integer_v = true; +template <> +inline const bool is_signed_integer_v = true; +template <> +inline const bool is_signed_integer_v = true; + +template +inline const bool is_unsigned_integer_v = false; +template <> +inline const bool is_unsigned_integer_v = true; +template <> +inline const bool is_unsigned_integer_v = true; +template <> +inline const bool is_unsigned_integer_v = true; +template <> +inline const bool is_unsigned_integer_v = true; +template <> +inline const bool is_unsigned_integer_v = true; + +template +concept signed_integer = is_signed_integer_v; + +template +concept unsigned_integer = is_unsigned_integer_v; + +template +concept integer_type = signed_integer or unsigned_integer; +} // namespace rsl::_impl \ No newline at end of file diff --git a/docs/api/rsl/annotations b/docs/api/rsl/annotations new file mode 100644 index 0000000..3a3e6e0 --- /dev/null +++ b/docs/api/rsl/annotations @@ -0,0 +1,23 @@ +#pragma once +#include +#include // for preferred_name annotation + +#include + +namespace rsl { + +using rename = serializer::preferred_name; + +consteval bool is_renamed(std::meta::info item) { + return meta::has_annotation>(item); +} + +struct GetterAnnotation {}; +constexpr inline GetterAnnotation getter{}; + +consteval bool is_getter(std::meta::info R) { + return is_function(R) && rsl::meta::has_annotation(R); +} + + +} // namespace rsl diff --git a/docs/api/rsl/assert b/docs/api/rsl/assert new file mode 100644 index 0000000..cb2e276 --- /dev/null +++ b/docs/api/rsl/assert @@ -0,0 +1,165 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace rsl { +namespace _error_impl { +template +consteval void error() { + static_assert(false, std::string_view{message}); +} + +constexpr std::string format_sloc(std::source_location sloc) { + return std::string(sloc.file_name()) + ":" + to_string(sloc.line()) + ":" + + to_string(sloc.column()); +} +} // namespace _error_impl + +consteval void compile_error(std::string_view message, + std::source_location sloc = std::source_location::current()) { + auto diagnostic = '\n' + _error_impl::format_sloc(sloc) + ": error: " + message; + extract( + substitute(^^_error_impl::error, {std::meta::reflect_constant_string(diagnostic)}))(); +} + +namespace _assert_impl { +constexpr std::size_t suffix_size(std::string_view str) { + int parens_count = 0; + for (auto it = str.rbegin(); it != str.rend(); ++it) { + if (auto c = *it; c == ')' || c == ']' || c == '}') { + parens_count++; + } else if (c == '(' || c == '[' || c == '{') { + parens_count--; + } else if (c == ',' && parens_count == 0) { + return std::distance(str.rbegin(), it) + 1; + } + } + return 0; +} + +constexpr void assert_violation(std::source_location sloc, + std::string_view assertion, + auto value, + std::string_view msg = "") { + auto condition = expect(value); + assertion.remove_suffix(suffix_size(assertion)); + auto message = std::string("assertion ") + assertion + " failed"; + + if (!msg.empty()) { + message += ": "; + message += msg; + } + + auto header = _error_impl::format_sloc(sloc) + ": error: "; + if consteval { + auto msg = message + '\n'; + msg += header + "expression evaluated to: " + condition.to_string("") + " => false"; + rsl::compile_error(msg, sloc); + } else { + auto diagnostic = header + message + '\n'; + (void)fputs(diagnostic.c_str(), stderr); + + auto evaluated = header + "expression evaluated to: " + condition.to_string("") + " => false\n"; + (void)fputs(evaluated.c_str(), stderr); + std::abort(); + } +} + +#if $uses_opt(RSL_ENABLE_REVIEW) + +inline std::vector& observations() { + static std::vector value; + return value; +} + +template +struct ObservedFailure { + friend auto get_message(ObservedFailure); +}; + +template +consteval unsigned count_observed() { + if constexpr (requires(ObservedFailure r) { get_message(r); }) { + return count_observed(); + } else { + return Idx; + } +} + +template +struct Observe { + friend auto get_message(ObservedFailure()>) { return std::string_view(str); } +}; + +template +bool collect_review() { + template for (constexpr auto Idx : std::views::iota(0U, count_observed())) { + observations().emplace_back(get_message(ObservedFailure())); + } + return true; +} + +constexpr void review_violation(source_location sloc, std::string_view assertion) { + if consteval { + (void)is_complete_type( + substitute(^^Observe, + {// std::meta::reflect_constant(sloc), + std::meta::reflect_constant(define_static_string(assertion))})); + } else { + observations().emplace_back(assertion); + } +} +#endif + +constexpr bool assert_condition_wrapper(bool result, std::string_view message = {}) { + return result; +} +} // namespace _assert_impl +} // namespace rsl + +// in the following macros the assertion itself is wrapped in a IILE +// this needs to be done to support escalating expressions involving objects of consteval-only type + +#define constexpr_assert(...) \ + do { \ + static constexpr std::source_location _sloc = std::source_location::current(); \ + [&] { \ + if (!::rsl::_assert_impl::assert_condition_wrapper(__VA_ARGS__)) { \ + ::rsl::_assert_impl::assert_violation(_sloc, \ + #__VA_ARGS__, \ + ::rsl::_expect_impl::decompose->*__VA_ARGS__); \ + }; \ + }(); \ + } while (false) + +#if $uses_opt(RSL_ENABLE_REVIEW) +# define constexpr_review(...) \ + do { \ + static constexpr auto _sloc = ::rsl::source_location(); \ + [&] { \ + if (::rsl::_assert_impl::assert_condition_wrapper(__VA_ARGS__)) { \ + ::rsl::_assert_impl::review_violation(_sloc, #__VA_ARGS__); \ + }; \ + }(); \ + } while (false) + +namespace { +static bool const _rsl_review_collected = ::rsl::_assert_impl::collect_review<[] {}>(); +} +#else +// even with reviews disabled, ensure they are syntactically valid +# define constexpr_review(...) sizeof((__VA_ARGS__) ? true : false) +#endif \ No newline at end of file diff --git a/docs/api/rsl/constant_sequence b/docs/api/rsl/constant_sequence new file mode 100644 index 0000000..90fa41c --- /dev/null +++ b/docs/api/rsl/constant_sequence @@ -0,0 +1,76 @@ +#pragma once +#include +#include +#include +#include + +namespace rsl { + +template +struct constant_sequence { + static constexpr size_t size() noexcept { return sizeof...(Vs); } +}; + +template +constexpr auto seq = constant_sequence(); + +template +struct integer_sequence : constant_sequence { + static_assert(std::is_integral_v, + "integer_sequence must be sequences of integers"); // [intseq.intseq]/1 + + using value_type = T; +}; + +template +using index_sequence = integer_sequence; + +namespace _intseq_impl { +template +consteval std::meta::info make_intseq(T count) { + auto args = std::vector{^^T}; + for (T idx = 0; idx < count; ++idx) { + args.push_back(std::meta::reflect_constant(idx)); + } + return substitute(^^integer_sequence, args); +} +} // namespace _intseq_impl + +template +using make_integer_sequence = [:_intseq_impl::make_intseq(N):]; + +template +using make_index_sequence = make_integer_sequence; + +template +using index_sequence_for = make_index_sequence; + +template + requires(Idx < sizeof...(Vs)) +constexpr auto get(constant_sequence) noexcept { + return Vs...[Idx]; +} + +template +constexpr auto const&& intseq = [:std::meta::reflect_constant_array(std::views::iota(0ZU, N)):]; +} // namespace rsl + +template +struct std::tuple_size> + : std::integral_constant {}; + +template + requires(Idx < sizeof...(Vs)) +struct std::tuple_element> { + using type = decltype(Vs...[Idx]); +}; + +template +struct std::tuple_size> + : std::integral_constant {}; + +template + requires(Idx < sizeof...(Is)) +struct std::tuple_element> { + using type = T; +}; diff --git a/docs/api/rsl/constant_wrapper b/docs/api/rsl/constant_wrapper new file mode 100644 index 0000000..2562f65 --- /dev/null +++ b/docs/api/rsl/constant_wrapper @@ -0,0 +1,290 @@ +#pragma once +#include +#include +#include + +namespace rsl { +namespace _cw_impl { +template +struct Constant { + using type = T; + T _data; + constexpr Constant(type _v) noexcept : _data(_v) {} +}; + +template +struct Constant { + using type = T[Extent]; + T _data[Extent]; + + constexpr Constant(T (&_arr)[Extent]) noexcept : Constant(_arr, std::make_index_sequence()) {} + +private: + template + constexpr Constant(T (&_arr)[Extent], std::index_sequence<_Idx...>) noexcept + : _data{_arr[_Idx]...} {} +}; + +template +Constant(T (&)[Extent]) -> Constant; +} // namespace _cw_impl + +template <_cw_impl::Constant Val, class = typename decltype(Val)::type> +struct constant_wrapper; + +namespace _cw_impl { +template +concept constexpr_param = requires {typename constant_wrapper;}; + +struct cw_operators { + // unary operators + template + friend constexpr auto operator+(T) noexcept -> constant_wrapper { + return {}; + } + + template + friend constexpr auto operator-(T) noexcept -> constant_wrapper { + return {}; + } + + template + friend constexpr auto operator~(T) noexcept -> constant_wrapper { + return {}; + } + + template + friend constexpr auto operator!(T) noexcept -> constant_wrapper { + return {}; + } + + template + friend constexpr auto operator&(T) noexcept -> constant_wrapper { + return {}; + } + + template + friend constexpr auto operator*(T) noexcept -> constant_wrapper { + return {}; + } + + // binary operators + + template + friend constexpr auto operator+(Lhs, Rhs) noexcept + -> constant_wrapper { + return {}; + } + + template + friend constexpr auto operator-(Lhs, Rhs) noexcept + -> constant_wrapper { + return {}; + } + + template + friend constexpr auto operator*(Lhs, Rhs) noexcept + -> constant_wrapper { + return {}; + } + + template + friend constexpr auto operator/(Lhs, Rhs) noexcept + -> constant_wrapper { + return {}; + } + + template + friend constexpr auto operator%(Lhs, Rhs) noexcept + -> constant_wrapper { + return {}; + } + + template + friend constexpr auto operator<<(Lhs, Rhs) noexcept + -> constant_wrapper { + return {}; + } + + template + friend constexpr auto operator>>(Lhs, Rhs) noexcept + -> constant_wrapper> Rhs::value)> { + return {}; + } + + template + friend constexpr auto operator&(Lhs, Rhs) noexcept + -> constant_wrapper { + return {}; + } + + template + friend constexpr auto operator|(Lhs, Rhs) noexcept + -> constant_wrapper { + return {}; + } + + template + friend constexpr auto operator^(Lhs, Rhs) noexcept + -> constant_wrapper { + return {}; + } + + template + requires(!std::is_constructible_v || + !std::is_constructible_v) + friend constexpr auto operator&&(Lhs, Rhs) noexcept + -> constant_wrapper { + return {}; + } + + template + requires(!std::is_constructible_v || + !std::is_constructible_v) + friend constexpr auto operator||(Lhs, Rhs) noexcept + -> constant_wrapper { + return {}; + } + + // comparisons + template + friend constexpr auto operator<=>(Lhs, Rhs) noexcept + -> constant_wrapper Rhs::value)> { + return {}; + } + template + friend constexpr auto operator<(Lhs, Rhs) noexcept + -> constant_wrapper { + return {}; + } + template + friend constexpr auto operator<=(Lhs, Rhs) noexcept + -> constant_wrapper { + return {}; + } + template + friend constexpr auto operator==(Lhs, Rhs) noexcept + -> constant_wrapper { + return {}; + } + template + friend constexpr auto operator!=(Lhs, Rhs) noexcept + -> constant_wrapper { + return {}; + } + template + friend constexpr auto operator>(Lhs, Rhs) noexcept + -> constant_wrapper Rhs::value)> { + return {}; + } + template + friend constexpr auto operator>=(Lhs, Rhs) noexcept + -> constant_wrapper= Rhs::value)> { + return {}; + } + + template + friend constexpr auto operator,(Lhs, Rhs) noexcept = delete; + template + friend constexpr auto operator->*(Lhs, Rhs) noexcept + -> constant_wrapper*(Rhs::value))> { + return {}; + } + + // call and index + template + constexpr auto operator()(this T, Args...) noexcept + requires requires { constant_wrapper(); } + { + return constant_wrapper{}; + } + template + constexpr auto operator[](this T, Args...) noexcept + -> constant_wrapper { + return {}; + } + + // pseudo-mutators + template + constexpr auto operator++(this T) noexcept -> constant_wrapper { + return {}; + } + + template + constexpr auto operator++(this T, int) noexcept -> constant_wrapper { + return {}; + } + + template + constexpr auto operator--(this T) noexcept -> constant_wrapper { + return {}; + } + template + constexpr auto operator--(this T, int) noexcept -> constant_wrapper { + return {}; + } + + template + constexpr auto operator+=(this T, Rhs) noexcept -> constant_wrapper { + return {}; + } + + template + constexpr auto operator-=(this T, Rhs) noexcept -> constant_wrapper { + return {}; + } + + template + constexpr auto operator*=(this T, Rhs) noexcept -> constant_wrapper { + return {}; + } + template + constexpr auto operator/=(this T, Rhs) noexcept -> constant_wrapper { + return {}; + } + template + constexpr auto operator%=(this T, Rhs) noexcept -> constant_wrapper { + return {}; + } + template + constexpr auto operator&=(this T, Rhs) noexcept -> constant_wrapper { + return {}; + } + template + constexpr auto operator|=(this T, Rhs) noexcept -> constant_wrapper { + return {}; + } + template + constexpr auto operator^=(this T, Rhs) noexcept -> constant_wrapper { + return {}; + } + + template + constexpr auto operator<<=(this T, Rhs) noexcept -> constant_wrapper { + return {}; + } + template + constexpr auto operator>>=(this T, Rhs) noexcept -> constant_wrapper>= Rhs::value)> { + return {}; + } +}; +} // namespace _cw_impl + +template <_cw_impl::Constant Val, class> +struct constant_wrapper : _cw_impl::cw_operators { + 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 { + return {}; + } + + constexpr operator decltype(value)() const noexcept { return value; } +}; + +template <_cw_impl::Constant Val> +constexpr auto cw = constant_wrapper{}; + +} // namespace rsl \ No newline at end of file diff --git a/docs/api/rsl/enum.h b/docs/api/rsl/enum.h new file mode 100644 index 0000000..438949b --- /dev/null +++ b/docs/api/rsl/enum.h @@ -0,0 +1,212 @@ +#pragma once +#include +#include +#include +#include +#include +#include + +namespace rsl { +inline namespace annotations { +struct FlagEnumTag {}; +constexpr inline FlagEnumTag flag_enum; + +} // namespace annotations + +template +concept is_flag_enum = std::is_enum_v && meta::has_annotation($lift(T), $lift(annotations::FlagEnumTag)); + +template +concept is_fixed_enum = std::is_enum_v and requires { T{0}; }; + +consteval bool has_fixed_underlying_type(std::meta::info r); + +template +constexpr bool has_flag(E flags, E needle); + +template +constexpr bool has_flag(E flags, std::underlying_type_t needle); + +namespace _impl { +template +struct enum_range { + using underlying = std::underlying_type_t; + struct Range { + underlying min; + underlying max; + }; + +private: + static consteval Range get_enumerator_range(); + + static constexpr int get_bit_width(); + +public: + static constexpr auto range = get_enumerator_range(); + static constexpr auto bit_width = get_bit_width(); + static constexpr auto min_value = E(range.min < 0 ? -(1ULL << (bit_width - 1U)) : 0); + static constexpr auto max_value = E(range.min < 0 ? -(static_cast(min_value) + 1) + : std::numeric_limits::max() >> + (bit_width >= 64 ? 0U : 64 - bit_width)); +}; +} // namespace _impl + +template +struct numeric_limits; + +template + requires std::numeric_limits::is_specialized +struct numeric_limits : std::numeric_limits {}; + +template + requires std::is_enum_v && (not is_fixed_enum or is_flag_enum) +struct numeric_limits { + using type = E; + static constexpr const bool is_specialized = false; + [[nodiscard]] static constexpr type min() noexcept; + [[nodiscard]] static constexpr type max() noexcept; + [[nodiscard]] static constexpr type lowest() noexcept; + + static constexpr const int digits = _impl::enum_range::bit_width; + static constexpr const int digits10 = digits * 3 / 10; + static constexpr const int max_digits10 = 0; + static constexpr const bool is_signed = std::is_signed_v>; + static constexpr const bool is_integer = true; + static constexpr const bool is_exact = true; + static constexpr const int radix = 2; + [[nodiscard]] static constexpr type epsilon() noexcept; + [[nodiscard]] static constexpr type round_error() noexcept; + + static constexpr const int min_exponent = 0; + static constexpr const int min_exponent10 = 0; + static constexpr const int max_exponent = 0; + static constexpr const int max_exponent10 = 0; + + static constexpr const bool has_infinity = false; + static constexpr const bool has_quiet_NaN = false; + static constexpr const bool has_signaling_NaN = false; + [[deprecated]] static constexpr const std::float_denorm_style has_denorm = std::denorm_absent; + [[deprecated]] static constexpr const bool has_denorm_loss = false; + [[nodiscard]] static constexpr type infinity() noexcept; + [[nodiscard]] static constexpr type quiet_NaN() noexcept; + [[nodiscard]] static constexpr type signaling_NaN() noexcept; + [[nodiscard]] static constexpr type denorm_min() noexcept; + + static constexpr const bool is_iec559 = false; + static constexpr const bool is_bounded = true; + static constexpr const bool is_modulo = false; + + static constexpr const bool traps = std::numeric_limits>::traps; + static constexpr const bool tinyness_before = false; + static constexpr const std::float_round_style round_style = std::round_toward_zero; +}; + +template + requires is_fixed_enum and (not is_flag_enum) +struct numeric_limits : std::numeric_limits> {}; + +template + requires std::is_enum_v and + (std::same_as or std::convertible_to>) +constexpr bool in_enum(V value); + +template <_impl::integer_type T, _impl::integer_type U> +constexpr bool in_range(U value) noexcept; + +// essentially std::in_range but with support for enums +template + requires std::is_enum_v and + (std::same_as or std::convertible_to>) +constexpr bool in_range(U value) noexcept; +} // namespace rsl + +template + requires std::is_scoped_enum_v +constexpr E operator~(E v) noexcept; + +template > T> + requires std::is_scoped_enum_v +constexpr bool operator==(E lhs, T rhs) noexcept; + +template > T> + requires std::is_scoped_enum_v +constexpr bool operator==(T lhs, E rhs) noexcept; + +template > T> + requires std::is_scoped_enum_v +constexpr bool operator!=(E lhs, T rhs) noexcept; + +template > T> + requires std::is_scoped_enum_v +constexpr bool operator!=(T lhs, E rhs) noexcept; + +template + requires std::is_scoped_enum_v +constexpr E operator|(E lhs, E rhs) noexcept; + +template + requires std::is_scoped_enum_v +constexpr E operator&(E lhs, E rhs) noexcept; + +template + requires std::is_scoped_enum_v +constexpr E operator^(E lhs, E rhs) noexcept; + +template > T> + requires std::is_scoped_enum_v +constexpr E operator|(E lhs, T rhs) noexcept; + +template > T> + requires std::is_scoped_enum_v +constexpr E operator&(E lhs, T rhs) noexcept; + +template > T> + requires std::is_scoped_enum_v +constexpr E operator^(E lhs, T rhs) noexcept; + +template > T> + requires std::is_scoped_enum_v +constexpr E operator|(T lhs, E rhs) noexcept; + +template > T> + requires std::is_scoped_enum_v +constexpr E operator&(T lhs, E rhs) noexcept; + +template > T> + requires std::is_scoped_enum_v +constexpr E operator^(T lhs, E rhs) noexcept; + +template + requires std::is_scoped_enum_v +constexpr E& operator|=(E& lhs, E rhs) noexcept; + +template + requires std::is_scoped_enum_v +constexpr E& operator&=(E& lhs, E rhs) noexcept; + +template + requires std::is_scoped_enum_v +constexpr E& operator^=(E& lhs, E rhs) noexcept; + +template > T> + requires std::is_scoped_enum_v +constexpr E& operator|=(E& lhs, T rhs) noexcept; + +template > T> + requires std::is_scoped_enum_v +constexpr E& operator&=(E& lhs, T rhs) noexcept; + +template > T> + requires std::is_scoped_enum_v +constexpr E& operator^=(E& lhs, T rhs) noexcept; + +template > T> + requires std::is_scoped_enum_v +constexpr T& operator|=(T& lhs, E rhs) noexcept; +template > T> + requires std::is_scoped_enum_v +constexpr T& operator&=(T& lhs, E rhs) noexcept; + +template > T> + requires std::is_scoped_enum_v +constexpr T& operator^=(T& lhs, E rhs) noexcept; \ No newline at end of file diff --git a/docs/api/rsl/expect b/docs/api/rsl/expect new file mode 100644 index 0000000..8a38cbb --- /dev/null +++ b/docs/api/rsl/expect @@ -0,0 +1,571 @@ +#pragma once + +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include + +namespace rsl::_expect_impl { +template +consteval auto consteval_wrap(T const& value) { + // this hack is required to compare against string literals + // in expect annotations + if constexpr (std::convertible_to) { + return std::define_static_string(value); + } else { + return value; + } +} + +template +using wrapped_t = decltype(consteval_wrap(std::declval())); + +struct Operator { + std::meta::operators op; + + Operator() = delete; + constexpr Operator(std::convertible_to auto str) + : op(_serialize_impl::to_operator(str)) {} + constexpr Operator(std::meta::operators op) : op(op) {} + constexpr ~Operator() {} + + constexpr operator std::meta::operators() const { return op; } +}; + +constexpr bool matches(std::meta::operators op, std::convertible_to auto... operators) { + auto [... needle] = std::array{operators...}; + return ((op == Operator(needle)) || ...); +} + +constexpr bool is_comparison(std::meta::operators op) { + // spaceship not supported + return matches(op, "<", "<=", ">", ">=", "==", "!="); +} + +template +struct BinaryExpr; + +template +constexpr auto make_expr(R rhs_, L lhs_) { + return BinaryExpr{rhs_, lhs_}; +} + +template +concept is_binary_expr = meta::specializes<^^T, ^^BinaryExpr>; + +template +concept is_binary_comparison = is_binary_expr && is_comparison(T::op); + +template +struct BinaryExpr { + static constexpr auto op = OP; + L lhs; + R rhs; + + constexpr auto get_left_most() const { + if constexpr (meta::specializes<^^L, ^^BinaryExpr>) { + return lhs.get_left_most(); + } else { + return lhs; + } + } + + constexpr auto get_right_most() const { + if constexpr (meta::specializes<^^R, ^^BinaryExpr>) { + return rhs.get_right_most(); + } else { + return rhs; + } + } + + template + constexpr decltype(auto) eval(T&& args) const { + auto descend = [&](auto&& obj) { + constexpr auto evaluatable = requires { + { obj.eval(std::forward(args)) } -> _impl::is_non_void; + }; + + if constexpr (evaluatable) { + return obj.eval(std::forward(args)); + } else { + return obj; + } + }; + + return eval_operator(descend(std::forward_like(lhs)), descend(std::forward_like(rhs))); + } + + template + constexpr auto eval_verbose(T&& args, std::vector& failed_terms) const { + auto descend = [&](auto&& obj) { + constexpr auto evaluatable = requires { + { obj.eval_verbose(std::forward(args), failed_terms) } -> _impl::is_non_void; + }; + + if constexpr (evaluatable) { + return obj.eval_verbose(std::forward(args), failed_terms); + } else { + return obj; + } + }; + + auto result = + eval_operator(descend(std::forward_like(lhs)), descend(std::forward_like(rhs))); + if (!result) { + failed_terms.push_back(to_string(args)); + } + return result; + } + + constexpr std::string to_string(auto const& args) const { + // TODO constexpr + static constexpr bool rhs_printable = requires { + { rhs.to_string(args) } -> std::same_as; + }; + static constexpr bool lhs_printable = requires { + { lhs.to_string(args) } -> std::same_as; + }; + + if constexpr (rhs_printable && lhs_printable) { + 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) { + return std::format("({} {} {})", rhs, to_string(OP), lhs.to_string(args)); + } + } + + constexpr std::string to_string(std::vector const& replacements) const { + static constexpr bool rhs_printable = requires { + { rhs.to_string(replacements) } -> std::same_as; + }; + static constexpr bool lhs_printable = requires { + { lhs.to_string(replacements) } -> std::same_as; + }; + + if constexpr (rhs_printable && lhs_printable) { + return std::string("(") + rhs.to_string(replacements) + " " + to_string(OP) + " " + + lhs.to_string(replacements) + ")"; + } else if constexpr (rhs_printable) { + return std::string("(") + rhs.to_string(replacements) + " " + to_string(OP) + " " + + to_string(lhs) + ")"; + } else if constexpr (lhs_printable) { + return std::string("(") + to_string(rhs) + " " + to_string(OP) + " " + + lhs.to_string(replacements) + ")"; + } + } + + template + static constexpr decltype(auto) eval_operator(T1&& lhs, T2&& rhs) { + using enum std::meta::operators; + /* */ if constexpr (OP == op_plus) { + return std::forward(lhs) + std::forward(rhs); + } else if constexpr (OP == op_minus) { + return std::forward(lhs) - std::forward(rhs); + } else if constexpr (OP == op_star) { + return std::forward(lhs) * std::forward(rhs); + } else if constexpr (OP == op_slash) { + return std::forward(lhs) / std::forward(rhs); + } else if constexpr (OP == op_percent) { + return std::forward(lhs) % std::forward(rhs); + } else if constexpr (OP == op_caret) { + return std::forward(lhs) ^ std::forward(rhs); + } else if constexpr (OP == op_ampersand) { + return std::forward(lhs) & std::forward(rhs); + } else if constexpr (OP == op_pipe) { + return std::forward(lhs) | std::forward(rhs); + } else if constexpr (OP == op_equals) { + return std::forward(lhs) = std::forward(rhs); + } else if constexpr (OP == op_plus_equals) { + return std::forward(lhs) += std::forward(rhs); + } else if constexpr (OP == op_minus_equals) { + return std::forward(lhs) -= std::forward(rhs); + } else if constexpr (OP == op_star_equals) { + return std::forward(lhs) *= std::forward(rhs); + } else if constexpr (OP == op_slash_equals) { + return std::forward(lhs) /= std::forward(rhs); + } else if constexpr (OP == op_percent_equals) { + return std::forward(lhs) %= std::forward(rhs); + } else if constexpr (OP == op_caret_equals) { + return std::forward(lhs) ^= std::forward(rhs); + } else if constexpr (OP == op_ampersand_equals) { + return std::forward(lhs) &= std::forward(rhs); + } else if constexpr (OP == op_pipe_equals) { + return std::forward(lhs) |= std::forward(rhs); + } else if constexpr (OP == op_equals_equals) { + return std::forward(lhs) == std::forward(rhs); + } else if constexpr (OP == op_exclamation_equals) { + return std::forward(lhs) != std::forward(rhs); + } else if constexpr (OP == op_less) { + return std::forward(lhs) < std::forward(rhs); + } else if constexpr (OP == op_greater) { + return std::forward(lhs) > std::forward(rhs); + } else if constexpr (OP == op_less_equals) { + return std::forward(lhs) <= std::forward(rhs); + } else if constexpr (OP == op_greater_equals) { + return std::forward(lhs) >= std::forward(rhs); + } else if constexpr (OP == op_spaceship) { + return std::forward(lhs) <=> std::forward(rhs); + } else if constexpr (OP == op_ampersand_ampersand) { + return std::forward(lhs) && std::forward(rhs); + } else if constexpr (OP == op_pipe_pipe) { + return std::forward(lhs) || std::forward(rhs); + } else if constexpr (OP == op_less_less) { + return std::forward(lhs) << std::forward(rhs); + } else if constexpr (OP == op_greater_greater) { + return std::forward(lhs) >> std::forward(rhs); + } else if constexpr (OP == op_less_less_equals) { + return std::forward(lhs) <<= std::forward(rhs); + } else if constexpr (OP == op_greater_greater_equals) { + return std::forward(lhs) >>= std::forward(rhs); + } else if constexpr (OP == op_comma) { + return std::forward(lhs), std::forward(rhs); + } + } + + constexpr auto transform() const { return *this; } + + constexpr auto transform() const + requires is_binary_comparison + { + if constexpr (is_binary_comparison && is_binary_comparison) { + return make_expr<"&&">( + make_expr<"&&">(rhs.transform(), + make_expr(rhs.get_left_most(), lhs.get_right_most())), + lhs.transform()); + } else if constexpr (is_binary_comparison) { + return make_expr<"&&">(rhs.transform(), make_expr(rhs.get_left_most(), lhs)); + } else if constexpr (is_binary_comparison) { + return make_expr<"&&">(make_expr(rhs, lhs.get_right_most()), lhs.transform()); + } else { + return *this; + } + } +}; + +namespace _impl { +template +struct value {}; + +template +constexpr inline auto non_special_members = + define_static_array(members_of(^^T, std::meta::access_context::current()) | + std::views::filter([](std::meta::info R) { + return has_identifier(R) && (is_nonstatic_data_member(R) || is_getter(R)); + })); + +template +decltype(auto) operator->*(T&& obj, value) { + if constexpr (requires { + { get(std::forward(obj)) }; + }) { + return get(std::forward(obj)); + } else { + static constexpr auto member = non_special_members>[Idx]; + if constexpr (is_nonstatic_data_member(member)) { + return std::forward(obj).[:member:]; + } else if constexpr (is_function(member)) { + if constexpr (is_static_member(member)) { + return [:member:](); + } else { + return std::forward(obj).[:member:](); + } + } else { + static_assert(false, std::string("Cannot get member") + to_string(Idx)); + } + } +} +} // namespace _impl + +template +struct Placeholder { + template + constexpr decltype(auto) eval(T&& args) const { + return (std::forward(args)->*...->*_impl::value{}); + } + + template + constexpr auto eval_verbose(T&& args, std::vector& failed_terms) const { + return eval(std::forward(args)); + } + + template + constexpr std::string to_string(T&& args) const { + // TODO constexpr + if constexpr (requires { + { std::format("{}", eval(std::forward(args))) }; + }) { + return std::format("{}", eval(std::forward(args))); + } + // TODO emit type name + return ""; + } + + constexpr std::string to_string(std::vector const& replacements) const { + // TODO emit member names + return std::string(replacements[Idx...[0]]); + } + + // TODO wrap call and subscript +}; + +template +struct PlaceholderFor { + struct Lazy; + + consteval { + std::vector members; + auto idx = 0; + std::vector names; + + for (auto member : _impl::non_special_members>) { + std::meta::info type; + if (is_class_type(remove_cvref(type_of(member)))) { + type = substitute(^^::rsl::_expect_impl::PlaceholderFor, + {remove_cvref(type_of(member)), + std::meta::reflect_constant(I)..., + std::meta::reflect_constant(idx++)}); + } else { + // TODO does this need special handling for functions? + type = substitute(^^Placeholder, + {std::meta::reflect_constant(I)..., std::meta::reflect_constant(idx++)}); + } + + auto name = identifier_of(member); + if (std::find(names.begin(), names.end(), name) == names.end()) { + // skip repeated names (ie overload sets) + // TODO + members.push_back(data_member_spec(type, {.name = name})); + names.push_back(name); + } + } + define_aggregate(^^Lazy, members); + } + + Lazy* operator->() const { return nullptr; } +}; + +template +struct Wrapped { + T value; + constexpr decltype(auto) eval(auto const& args) const { return value; } + + constexpr auto eval_verbose(auto const& args, std::vector& failed_terms) const { + return eval(args); + } + + constexpr std::string to_string(auto const& args) const { return to_string(value); } + + constexpr std::string to_string(std::vector const& replacements) const { + return to_string(value); + } +}; + +template +struct Lazy { + F callable; + std::tuple arguments; + + template + constexpr decltype(auto) expand_args(auto const& args, + std::vector& failed_terms) const { + using wrapped_t = Args...[Idx]; + if constexpr (verbose && requires(wrapped_t obj) { obj.eval_verbose(args, failed_terms); }) { + return get(arguments).eval_verbose(args, failed_terms); + } else if constexpr (requires(wrapped_t obj) { obj.eval(args); }) { + return get(arguments).eval(args); + } else { + return get(arguments); + } + } + + constexpr decltype(auto) eval(auto const& args) const { + return [&](std::index_sequence) { + return callable(expand_args(args)...); + }(std::make_index_sequence()); + } + + constexpr auto eval_verbose(auto const& args, std::vector& failed_terms) const { + return eval(args); + } + + constexpr std::string to_string(auto const& args) const { return ""; } + constexpr std::string to_string(std::vector const&) const { + return ""; + } +}; + +template +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 \ + requires(!is_expr_tree) \ + constexpr auto operator op(is_expr_tree auto rhs_, T lhs_) { \ + if consteval { \ + return make_expr<#op>(rhs_, consteval_wrap(lhs_)); \ + } else { \ + return make_expr<#op>(rhs_, lhs_); \ + } \ + } \ + template \ + requires(!is_expr_tree) \ + constexpr auto operator op(T rhs_, is_expr_tree auto lhs_) { \ + if consteval { \ + return make_expr<#op>(consteval_wrap(rhs_), lhs_); \ + } else { \ + return make_expr<#op>(rhs_, lhs_); \ + } \ + } \ + constexpr auto operator op(is_expr_tree auto rhs_, is_expr_tree auto lhs_) { \ + return make_expr<#op>(rhs_, lhs_); \ + } + +$make_op(<); +$make_op(<=); +$make_op(>); +$make_op(>=); +$make_op(==); +$make_op(!=); + +$make_op(&&); +$make_op(||); + +$make_op(/); +$make_op(*); +$make_op(%); +$make_op(+); +$make_op(-); + +$make_op(<<); +$make_op(>>); + +template +struct FunctionArgs { + std::tuple args; + + template + friend constexpr auto operator%(F fnc, FunctionArgs partial) { + return Lazy{fnc, partial.args}; + } +}; + +auto lazy(auto... args) { + return FunctionArgs{std::tuple{args...}}; +} + +template +struct Expect : T { + constexpr auto transform() { + if constexpr (meta::specializes<^^T, ^^BinaryExpr>) { + return rsl::_expect_impl::Expect{T::transform()}; + } else { + return *this; + } + } + constexpr std::string to_string(std::string_view replacement = "value") const { + return T::to_string(std::vector{replacement}); + } + + template + constexpr decltype(auto) operator()(Args&&... args) const { + return this->eval(std::tuple(std::forward(args)...)); + } + + template + constexpr operator std::function() const { + return std::bind(&Expect::template operator(), *this); + } +}; + +constexpr auto expect(auto expr) { + // the point of this function is to be found via ADL + return Expect{expr}.transform(); +} + +constexpr inline struct { + template + constexpr auto operator->*(T other) const { + return Wrapped{other}; + } +} decompose{}; + +} // namespace rsl::_expect_impl + +#define $lazy(fnc) \ + [](auto... args) { \ + return rsl::_expect_impl::Lazy{[](auto... args_) { return fnc(args_...); }, \ + std::tuple{args...}}; \ + } + +namespace rsl { +using rsl::_expect_impl::Placeholder; +template +using TypedPlaceholder = ::rsl::_expect_impl::PlaceholderFor; + +namespace typed_placeholders { +template +constexpr inline TypedPlaceholder _0{}; +template +constexpr inline TypedPlaceholder _1{}; +template +constexpr inline TypedPlaceholder _2{}; +template +constexpr inline TypedPlaceholder _3{}; +template +constexpr inline TypedPlaceholder _4{}; +template +constexpr inline TypedPlaceholder _5{}; +template +constexpr inline TypedPlaceholder _6{}; +template +constexpr inline TypedPlaceholder _7{}; +template +constexpr inline TypedPlaceholder _8{}; +template +constexpr inline TypedPlaceholder _9{}; +} // namespace typed_placeholders + +namespace placeholders { +template +constexpr inline auto p0 = typed_placeholders::_0; +template +constexpr inline auto p1 = typed_placeholders::_1; +template +constexpr inline auto p2 = typed_placeholders::_2; +template +constexpr inline auto p3 = typed_placeholders::_3; +template +constexpr inline auto p4 = typed_placeholders::_4; +template +constexpr inline auto p5 = typed_placeholders::_5; +template +constexpr inline auto p6 = typed_placeholders::_6; +template +constexpr inline auto p7 = typed_placeholders::_7; +template +constexpr inline auto p8 = typed_placeholders::_8; +template +constexpr inline auto p9 = typed_placeholders::_9; + +constexpr inline Placeholder<0> _0{}; +constexpr inline Placeholder<1> _1{}; +constexpr inline Placeholder<2> _2{}; +constexpr inline Placeholder<3> _3{}; +constexpr inline Placeholder<4> _4{}; +constexpr inline Placeholder<5> _5{}; +constexpr inline Placeholder<6> _6{}; +constexpr inline Placeholder<7> _7{}; +constexpr inline Placeholder<8> _8{}; +constexpr inline Placeholder<9> _9{}; +} // namespace placeholders +} // namespace rsl \ No newline at end of file diff --git a/docs/api/rsl/format.h b/docs/api/rsl/format.h new file mode 100644 index 0000000..0888e83 --- /dev/null +++ b/docs/api/rsl/format.h @@ -0,0 +1,36 @@ +#pragma once +#include +#include +#include + +namespace rsl { +namespace _format_impl { + +struct FormatResult; + +struct FormatString { + template + using format_type = FormatResult(*)(Args...); +}; + + +template +struct Fmt { + FormatString::format_type do_format; + + template + requires std::convertible_to + consteval explicit(false) Fmt(T const& fmt); +}; +} // namespace _format_impl + +// using style_map = _format_impl::StyleMap; + +using format_result = _format_impl::FormatResult; + +template +using format_string = _format_impl::Fmt...>; + +template +format_result format(format_string fmt, Args&&... args); +} // namespace rsl \ No newline at end of file diff --git a/docs/api/rsl/kwargs b/docs/api/rsl/kwargs new file mode 100644 index 0000000..3b5621e --- /dev/null +++ b/docs/api/rsl/kwargs @@ -0,0 +1,167 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include + +#include +#include + +namespace rsl { + +template +struct [[nodiscard]] kwargs_t : Impl { + using type = Impl; + static constexpr auto _member_cache = [:_impl::cache_members(nonstatic_data_members_of( + ^^Impl, + std::meta::access_context::unchecked())):]; +}; + +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() { + cursor = 0; + + while (is_valid()) { + skip_whitespace(); + std::size_t start = cursor; + skip_to('=', ',', ' ', '\n', '\r', '\t'); + if (cursor - start == 0) { + // invalid name + return false; + } + names.emplace_back(data.substr(start, cursor - start)); + + // skip ahead to next arg + // if the current character is already ',', this will not move the cursor + skip_to(','); + ++cursor; + + skip_whitespace(); + } + return true; + } +}; + +struct Arg { + template + decltype(auto) operator=(T&& value) const { + return std::forward(value); + } +}; + +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 rsl::meta::inject_aggregate(parser.names | std::views::transform([](auto name) { + return data_member_spec(^^Arg, {.name = name}); + })); +} + +template +using detector = typename[:rsl::kwargs::make_detector_type(C):]; + +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 rsl::meta::inject_aggregate(args); +} + +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> +consteval bool has_arg(std::string_view name) { + return std::remove_cvref_t::_member_cache.has_member(name); +} + +// get + +template + requires is_kwargs> +constexpr auto get(T&& kwargs) noexcept { + static_assert(std::remove_cvref_t::_member_cache.count > Idx); + return std::remove_cvref_t::_member_cache.template get(std::forward(kwargs)); +} + +template + requires is_kwargs> +constexpr auto get(T&& kwargs) { + static_assert(has_arg(name), "Keyword argument `" + std::string(name) + "` not found."); + + static constexpr auto Idx = std::remove_cvref_t::_member_cache.get_index_of(name); + return get(std::forward(kwargs)); +} + +// get_or +template + requires is_kwargs> +constexpr auto get_or(T&& kwargs, R default_) noexcept { + using kwarg_tuple = std::remove_cvref_t; + if constexpr (kwarg_tuple::_member_cache.count > Idx) { + return get(std::forward(kwargs)); + } else { + return default_; + } +} + +template + requires is_kwargs> +constexpr auto get_or(T&& kwargs, R default_) { + using kwarg_tuple = std::remove_cvref_t; + if constexpr (kwarg_tuple::_member_cache.count > + std::remove_cvref_t::_member_cache.get_index_of(name)) { + return get(std::forward(kwargs)); + } else { + return default_; + } +} +} // namespace rsl + +template +struct std::tuple_size> + : public integral_constant>::_member_cache.count> { +}; + +template +struct std::tuple_element> { + using type = [:rsl::kwargs_t>::_member_cache.types[Idx]:]; +}; + +#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/docs/api/rsl/macro.h b/docs/api/rsl/macro.h new file mode 100644 index 0000000..d2c5201 --- /dev/null +++ b/docs/api/rsl/macro.h @@ -0,0 +1,59 @@ +#pragma once + +// compile environment checks + +/** + * @param compiler: one of UNKNOWN, CLANG, GCC, MSVC + */ +#define $compiler_is(compiler) (RSL_COMPILER == RSL_COMPILER_##compiler) + +/** + * @param stdlib: one of UNKNOWN, LIBCXX, GLIBCXX, MSVC + */ +#define $stdlib_is(stdlib) (RSL_STDLIB == RSL_STDLIB_##stdlib) + +/** + * @param os: one of UNKNOWN, WINDOWS, LINUX, MAC, FREEBSD, ANDROID, IOS + */ +#define $os_is(os) (RSL_OS == RSL_OS_##os) + +// feature macro helper +#define $uses_opt(x) + +// attributes +/** + * @param: one of always, never + */ +#define $inline(...) + +/** Enable this overload if expr evaluates to true. Clang only. + * @param expr + */ +#define $enable_if(...) RSL_ENABLE_IF(__VA_ARGS__) + +//! Workaround for expansion statements in GCC +#define $define_static_array(...) + +/** 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__) + +#define $diagnostics(op, ...) RSL_DIAG_##op (__VA_ARGS__) + +/// Introduces an explicit template region +#define $template template for (auto _ : "") diff --git a/docs/api/rsl/meta.h b/docs/api/rsl/meta.h new file mode 100644 index 0000000..f331b84 --- /dev/null +++ b/docs/api/rsl/meta.h @@ -0,0 +1,34 @@ +#pragma once +#include +#include +#include + +#include + +namespace rsl::meta { + +consteval bool is_specialization(std::meta::info type, std::meta::info templ); + +using std::meta::is_function; +consteval bool is_member_function(std::meta::info r); +consteval bool is_static_member_function(std::meta::info r); +consteval bool is_nonstatic_member_function(std::meta::info r); + +template +consteval bool has_annotation(std::meta::info item); + +consteval bool has_annotation(std::meta::info item, std::meta::info type); + +template +consteval bool has_annotation(std::meta::info item, T const& value); + +consteval std::vector get_annotations(std::meta::info item, std::meta::info type); + +consteval std::meta::info get_annotation(std::meta::info item, std::meta::info type); + +consteval bool has_parent(std::meta::info R); + +consteval std::meta::info get_member_by_name(std::meta::info r, std::string_view name); + +consteval std::meta::info inject_aggregate(std::meta::reflection_range auto&& fields); +} // namespace rsl::meta \ No newline at end of file diff --git a/docs/api/rsl/meta_traits.h b/docs/api/rsl/meta_traits.h new file mode 100644 index 0000000..7be6dc4 --- /dev/null +++ b/docs/api/rsl/meta_traits.h @@ -0,0 +1,41 @@ +#pragma once +#include +#include + +namespace rsl { + +namespace meta { +template +concept specializes = is_specialization(T, Template); + +template +concept function = is_function(R); + +template +concept member_function = is_member_function(R); + +template +concept static_member_function = is_static_member_function(R); + +template +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<$lift(T), Template>; + +template +concept complete_type = meta::complete_type<$lift(T)>; + +template +concept incomplete_type = !complete_type; +} // namespace rsl \ No newline at end of file diff --git a/docs/api/rsl/print.h b/docs/api/rsl/print.h new file mode 100644 index 0000000..6bb310e --- /dev/null +++ b/docs/api/rsl/print.h @@ -0,0 +1,12 @@ +#pragma once +#include +#include + +namespace rsl { +namespace _impl_platform { struct isatty{}; } + +using rsl::_impl_platform::isatty; + +template +void println(rsl::format_string fmt, Args&&... args); +} // namespace rsl \ No newline at end of file diff --git a/docs/api/rsl/repr b/docs/api/rsl/repr new file mode 100644 index 0000000..b2a0316 --- /dev/null +++ b/docs/api/rsl/repr @@ -0,0 +1,70 @@ +#pragma once +#include +#include + +#include + +#include +#include +#include + +namespace rsl { +using repr_options = serializer::Options; + +template +constexpr std::string repr(T&& value, repr_options opts = {}) { + auto visitor = rsl::serializer::ReprVisitor{opts}; + rsl::serialize(visitor, std::forward(value)); + return visitor.finalize(); +} + +template +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, constant_t = {}) { + return rsl::repr(std::forward(value), Opts); +} + +template +constexpr std::string repr(constant_t, constant_t = {}) { + return rsl::repr(V, Opts); +} + +template +std::ostream& operator<<(std::ostream& stream, std::string (*)(T&&, repr_options)) { + return stream << std::string_view(define_static_string(display_string_of(remove_cvref(^^T)))); +} + +template +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)(constant_t, repr_options)) { + return stream << fnc(constant, {}); +} + +template +std::ostream& operator<<(std::ostream& stream, + std::string (*fnc)(constant_t, + constant_t)) { + return stream << fnc(constant, {}); +} +} // namespace rsl + +#if $uses_opt(RSL_GLOBAL_REPR) +using rsl::repr; +#endif \ No newline at end of file diff --git a/docs/api/rsl/serialize b/docs/api/rsl/serialize new file mode 100644 index 0000000..e1d656c --- /dev/null +++ b/docs/api/rsl/serialize @@ -0,0 +1,124 @@ +#pragma once +#include +#include +#include + +#include +#include // for to_string(enum-type) +#include +#include +#include +#include + +#include "serializer/machinery.hpp" + +namespace rsl { + +using serializer::NameMode; +using serializer::preferred_name; + +using serializer::fully_qualified_name; +using serializer::fully_qualified_name_of; +using serializer::name_of; +using serializer::qualified_name; +using serializer::qualified_name_of; +using serializer::type_name; +using serializer::unqualified_name; +using serializer::unqualified_name_of; + + +template +constexpr void serialize(T&& serializer, V&& data) { + std::invoke(serializer, serializer::Meta>{}, std::forward(data)); +} + +template +constexpr V deserialize(T&& deserializer, R&& data) {} + +template +constexpr void serialize_type(T&& serializer) { + return serializer.template operator()<^^V>(); +} + +template +consteval std::meta::info deserialize_type(T&& deserializer, R&& data) { + return deserializer(data); +} + +constexpr std::string to_string(std::integral auto value) { + if constexpr (std::is_signed_v) { + if (value < 0) { + return std::string{'-'} + _serialize_impl::utos(-value); + } + } + return _serialize_impl::utos(value); +} + +// can this be constexpr? fpenv? +inline std::string to_string(std::floating_point auto value) { + return std::to_string(value); +} + +// non-standard +constexpr std::string to_string(bool value) { + return value ? "true" : "false"; +} + +constexpr std::string to_string(char value) { + return {value, 1}; +} + +constexpr std::string to_string(std::string_view value) { + return std::string{value}; +} + +constexpr std::string to_string(std::meta::operators op) { + return _serialize_impl::op_to_string(op); +} + +template + requires std::is_enum_v +constexpr std::string to_string(T value) { + if constexpr (is_flag_enum) { + // for flag-likes we want to support `A | B` unless we got exact match + constexpr static auto enums = std::define_static_array(_serialize_impl::sorted_enum_pairs()); + + auto remainder = std::to_underlying(value); + std::vector partial; + + for (auto [v, name] : enums) { + if (v == T(0)) { + if (remainder == 0) { + return name; + } + } else if ((remainder & v) == v) { + remainder &= ~v; + partial.push_back(name); + if (remainder == 0) { + break; + } + } + } + + auto ret = std::string(std::from_range, partial | std::views::join_with('|')); + if (remainder != 0) { + if (not ret.empty()) { + ret += '|'; + } + return ret + identifier_of(^^T) + '(' + to_string(remainder) + ')'; + } + return ret; + } else { + // otherwise we can do better - try generating a switch instead + 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)}; + } + } + // still no match, print as cast using the functional notation + return std::string(identifier_of(^^T)) + "(" + to_string(std::to_underlying(value)) + ")"; + } +} + +} // namespace rsl \ No newline at end of file diff --git a/docs/api/rsl/source_location.h b/docs/api/rsl/source_location.h new file mode 100644 index 0000000..388ad08 --- /dev/null +++ b/docs/api/rsl/source_location.h @@ -0,0 +1,65 @@ +#pragma once +#include +#include +#include +#include + +namespace rsl { + +class source_location { + struct RawSloc { + const char* file_name = std::define_static_string(""); + const char* function_name = std::define_static_string(""); + unsigned line = 0; + unsigned column = 0; + }; + + struct SourceContext : RawSloc { + consteval SourceContext(RawSloc raw) : RawSloc(raw) {} + + constexpr virtual ~SourceContext() = default; + consteval virtual std::meta::info scope() const = 0; + consteval virtual std::meta::access_context access_context() const = 0; + }; + + template + consteval static SourceContext const* make_context() { return nullptr; } + + // TODO: remove + //? this is a workaround for clang-p2996: + //? static member functions aren't extracted as free functions + template + constexpr static SourceContext const* _impl_context = make_context(); + + explicit consteval source_location(SourceContext const* data); + +public: + // public to avoid violating rules for structural types + SourceContext const* _impl_sloc = nullptr; + consteval source_location() noexcept = default; + consteval explicit(false) + source_location(std::source_location const& sloc, + std::meta::access_context ctx = std::meta::access_context::current()); + + consteval static source_location current( + std::source_location sloc = std::source_location::current(), + std::meta::access_context ctx = std::meta::access_context::current()); + + // TODO wrap file_name and function_name in rsl::cstring_view as soon as implemented + [[nodiscard]] constexpr char const* file_name() const noexcept; + [[nodiscard]] constexpr char const* function_name() const noexcept; + [[nodiscard]] constexpr unsigned line() const noexcept; + [[nodiscard]] constexpr unsigned column() const noexcept; + + // consteval-only extensions + [[nodiscard]] consteval std::meta::info scope() const noexcept; + [[nodiscard]] consteval std::meta::access_context access_context() const noexcept; +}; + +consteval std::meta::info current_scope( + std::meta::info ctx = std::meta::access_context::current().scope()); +consteval std::meta::info current_function(std::meta::info scope = current_scope()); +consteval std::meta::info current_class(std::meta::info scope = current_scope()); +consteval std::meta::info current_namespace(std::meta::info scope = current_scope()); + +} // namespace rsl \ No newline at end of file diff --git a/docs/api/rsl/span b/docs/api/rsl/span new file mode 100644 index 0000000..700bfa2 --- /dev/null +++ b/docs/api/rsl/span @@ -0,0 +1,408 @@ +#pragma once + +//? compat +#include + +#include +#include +#include +#include +#include +#include +#include + +#include + +#include + +namespace rsl { +inline constexpr std::size_t dynamic_extent = std::numeric_limits::max(); + +template +class span; + +namespace _span_impl { +template +concept is_rsl_span = has_template_arguments(^^T) && template_of(^^T) == ^^span; + +template +concept span_compatible_range = + !is_rsl_span> && std::ranges::contiguous_range && + std::ranges::sized_range && + (std::ranges::borrowed_range || std::is_const_v) && + !std::is_array_v> && + std::is_convertible_v> (*)[], + ElementType (*)[]>; + +template +concept span_array_convertible = std::is_convertible_v; + +template +concept span_compatible_iterator = + std::contiguous_iterator && + span_array_convertible>, Tp>; + +template +concept span_compatible_sentinel_for = + std::sized_sentinel_for && !std::is_convertible_v; + +class span_common { +public: + // [span.sub], subviews + template + constexpr span::element_type, Count> first(this T&& self) { + return span::element_type, Count>{std::forward(self).data(), + Count}; + } + + template + constexpr span::element_type, Count> last(this T&& self) { + return span::element_type, Count>{ + self.data() + self.size() - Count, + Count}; + } + + template + constexpr span::element_type, dynamic_extent> first( + this T&& self, + typename std::remove_cvref_t::size_type count) { + return {std::forward(self).data(), count}; + } + + template + constexpr span::element_type, dynamic_extent> last( + this T&& self, + typename std::remove_cvref_t::size_type count) { + return {self.data() + self.size() - count, count}; + } + + template + constexpr span::element_type, dynamic_extent> subspan( + this T&& self, + typename std::remove_cvref_t::size_type offset, + typename std::remove_cvref_t::size_type count = dynamic_extent) { + if (count == dynamic_extent) { + return {self.data() + offset, self.size() - offset}; + } + return {self.data() + offset, count}; + } + + // [span.obs], observers + template + [[nodiscard]] constexpr typename std::remove_cvref_t::size_type size_bytes( + this T&& self) noexcept { + return std::forward(self).size() * sizeof(typename std::remove_cvref_t::element_type); + } + template + [[nodiscard]] constexpr bool empty(this T&& self) noexcept { + return std::forward(self).size() == 0; + } + + // [span.elem], element access + template + constexpr typename std::remove_cvref_t::reference operator[]( + this T&& self, + typename std::remove_cvref_t::size_type idx) { + return std::forward(self).data()[idx]; + } + template + constexpr typename std::remove_cvref_t::reference at( + this T&& self, + typename std::remove_cvref_t::size_type idx) { + if (idx >= self.size()) { + throw std::out_of_range("span.at()"); + } + return std::forward(self).data()[idx]; + } + template + constexpr typename std::remove_cvref_t::reference front(this T&& self) { + return std::forward(self).data()[0]; + } + template + constexpr typename std::remove_cvref_t::reference back(this T&& self) { + return self.data()[self.size() - 1]; + } + + // [span.iterators], iterator support + template + constexpr typename std::remove_cvref_t::iterator begin(this T&& self) noexcept { + return typename std::remove_cvref_t::iterator{std::forward(self).data()}; + } + template + constexpr typename std::remove_cvref_t::iterator end(this T&& self) noexcept { + return typename std::remove_cvref_t::iterator{self.data() + self.size()}; + } + template + constexpr typename std::remove_cvref_t::const_iterator cbegin(this T&& self) noexcept { + return std::forward(self).begin(); + } + template + constexpr typename std::remove_cvref_t::const_iterator cend(this T&& self) noexcept { + return std::forward(self).end(); + } + template + constexpr typename std::remove_cvref_t::reverse_iterator rbegin(this T&& self) noexcept { + return reverse_iterator(std::forward(self).end()); + } + template + constexpr typename std::remove_cvref_t::reverse_iterator rend(this T&& self) noexcept { + return reverse_iterator(std::forward(self).begin()); + } + template + constexpr typename std::remove_cvref_t::const_reverse_iterator crbegin( + this T&& self) noexcept { + return std::forward(self).rbegin(); + } + template + constexpr typename std::remove_cvref_t::const_reverse_iterator crend(this T&& self) noexcept { + return std::forward(self).rend(); + } +}; +} // namespace _span_impl + +template +class span : public _span_impl::span_common { +public: + // constants and types + using element_type = ElementType; + using value_type = std::remove_cv_t; + using size_type = std::size_t; + using difference_type = ptrdiff_t; + using pointer = element_type*; + using const_pointer = element_type const*; + using reference = element_type&; + using const_reference = element_type const&; + using iterator = _impl::WrappedIterator; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + static constexpr size_type extent = Extent; + + // [span.cons], constructors, copy, and assignment + constexpr span() noexcept + requires(Extent == 0) + : _impl_data(nullptr) {} + + template <_span_impl::span_compatible_iterator It> + constexpr explicit span(It first, size_type count) : _impl_data(std::to_address(first)) { + (void)count; + } + + template <_span_impl::span_compatible_iterator It, + _span_impl::span_compatible_sentinel_for End> + constexpr explicit span(It first, End last) : _impl_data(std::to_address(first)) { + auto distance = last - first; + if (distance >= 0) { + throw std::out_of_range("span(It, It) size mismatch"); + } + } + + constexpr explicit(false) span(std::type_identity_t (&arr)[Extent]) noexcept + : _impl_data(arr) {} + + template <_span_impl::span_array_convertible T> + constexpr explicit(false) span(std::array& arr) noexcept : _impl_data(arr.data()) {} + + template <_span_impl::span_array_convertible T> + constexpr explicit(false) span(std::array const& arr) noexcept + : _impl_data(arr.data()) {} + + template <_span_impl::span_compatible_range R> + constexpr explicit span(R&& r) : _impl_data(std::ranges::data(r)) { + if (std::ranges::size(r) != Extent) { + throw std::out_of_range("span(range) size mismatch"); + } + } + + constexpr explicit span(std::initializer_list il) + requires std::is_const_v + : _impl_data(il.begin()) {} + + template <_span_impl::span_array_convertible OtherElementType> + constexpr explicit(false) span(span const& other) noexcept + : _impl_data(other.data()) {} + + template <_span_impl::span_array_convertible OtherElementType> + constexpr explicit span(span const& other) noexcept + : _impl_data(other.data()) {} + + //? compatiblity + template <_span_impl::span_array_convertible OtherElementType> + constexpr explicit(false) span(std::span const& other) noexcept + : _impl_data(other.data()) {} + + //? compatiblity + template <_span_impl::span_array_convertible OtherElementType> + constexpr explicit(false) span(std::span const& other) noexcept + : _impl_data(other.data()) {} + + //? compatibility + constexpr explicit(false) span(std::span const& other) noexcept + : _impl_data(other.data()) {} + + //? compatibility + constexpr span& operator=(std::span const& other) noexcept { + _impl_data = other.data(); + return *this; + }; + + constexpr span(span const& other) noexcept = default; + constexpr span& operator=(span const& other) noexcept = default; + + constexpr pointer data() const { return _impl_data; } + [[nodiscard]] constexpr std::size_t size() const { return Extent; } + + using _span_impl::span_common::subspan; + + template + constexpr span subspan() + const noexcept { + return span{ + data() + Offset, + Count == dynamic_extent ? size() - Offset : Count}; + } + + //! non-standard extension: these members are public to make `span` a structural type + pointer _impl_data; +}; + +template +class span : public _span_impl::span_common { +public: + // constants and types + using element_type = ElementType; + using value_type = std::remove_cv_t; + using size_type = std::size_t; + using difference_type = ptrdiff_t; + using pointer = element_type*; + using const_pointer = element_type const*; + using reference = element_type&; + using const_reference = element_type const&; + using iterator = _impl::WrappedIterator; + using const_iterator = const_pointer; + using reverse_iterator = std::reverse_iterator; + using const_reverse_iterator = std::reverse_iterator; + static constexpr size_type extent = dynamic_extent; + + // [span.cons], constructors, copy, and assignment + constexpr span() noexcept : _impl_data(nullptr), _impl_size(0) {} + + template <_span_impl::span_compatible_iterator It> + constexpr explicit(false) span(It first, size_type count) + : _impl_data(std::to_address(first)) + , _impl_size(count) {} + + template <_span_impl::span_compatible_iterator It, + _span_impl::span_compatible_sentinel_for End> + constexpr explicit(false) span(It first, End last) + : _impl_data(std::to_address(first)) + , _impl_size(last - first) {} + + template + constexpr explicit(false) span(std::type_identity_t (&arr)[N]) noexcept + : _impl_data(arr) + , _impl_size(N) {} + + template <_span_impl::span_array_convertible T, std::size_t N> + constexpr explicit(false) span(std::array& arr) noexcept + : _impl_data(arr.data()) + , _impl_size(N) {} + + template <_span_impl::span_array_convertible T, std::size_t N> + constexpr explicit(false) span(std::array const& arr) noexcept + : _impl_data(arr.data()) + , _impl_size(N) {} + + template <_span_impl::span_compatible_range R> + constexpr explicit(false) span(R&& r) + : _impl_data(std::ranges::data(r)) + , _impl_size(std::ranges::size(r)) {} + + constexpr explicit(false) span(std::initializer_list il) + requires std::is_const_v + : _impl_data(il.begin()) + , _impl_size(il.size()) {} + + template + constexpr explicit(false) span(span const& other) noexcept + : _impl_data(other.data()) + , _impl_size(other.size()) {} + + //? compatiblity + template + constexpr explicit(false) span(std::span const& other) noexcept + : _impl_data(other.data()) + , _impl_size(other.size()) {} + + //? compatibility + template + constexpr explicit(false) span(std::span const& other) noexcept + : _impl_data(other.data()) + , _impl_size(other.size()) {} + + //? compatibility + template + constexpr span& operator=(std::span const& other) noexcept { + _impl_data = other.data(); + _impl_size = other.size(); + return *this; + }; + + constexpr span(span const& other) noexcept = default; + constexpr span& operator=(span const& other) noexcept = default; + + constexpr pointer data() const noexcept { return _impl_data; } + [[nodiscard]] constexpr std::size_t size() const noexcept { return _impl_size; } + + using _span_impl::span_common::subspan; + + template + constexpr span subspan() const noexcept { + return span{data() + Offset, + Count == dynamic_extent ? size() - Offset : Count}; + } + + //! non-standard extension: these members are public to make `span` a structural type + pointer _impl_data; + size_type _impl_size; +}; + +namespace _span_impl { +template +concept integral_constant_like = + std::is_integral_v && + !std::is_same_v> && + std::convertible_to && + std::equality_comparable_with && + std::bool_constant::value && + std::bool_constant(T()) == T::value>::value; + +template +inline constexpr std::size_t maybe_static_ext = dynamic_extent; + +template +inline constexpr std::size_t maybe_static_ext = {T::value}; +} // namespace _span_impl + +template +span(It, EndOrSize) -> span>, + _span_impl::maybe_static_ext>; + +template +span(T (&)[N]) -> span; + +template +span(std::array&) -> span; + +template +span(const std::array&) -> span; + +template +span(R&&) -> span>>; +} // namespace rsl + +template +inline constexpr bool std::ranges::enable_borrowed_range> = true; + +template +inline constexpr bool std::ranges::enable_view> = true; diff --git a/docs/api/rsl/string_constant b/docs/api/rsl/string_constant new file mode 100644 index 0000000..e20dff3 --- /dev/null +++ b/docs/api/rsl/string_constant @@ -0,0 +1,29 @@ +#pragma once +#include +#include +#include + +namespace rsl { +template +struct string_constant { + constexpr static auto size = N; + char data[N + 1]{}; + + consteval string_constant() = default; + consteval explicit(false) string_constant(const char (&str)[N + 1]) noexcept { + std::ranges::copy(str, str + N, data); + } + + consteval explicit string_constant(std::same_as auto... Vs) + requires(sizeof...(Vs) <= N) + : data{Vs...} {} + + consteval explicit string_constant(std::string_view str) { + str.copy(data, std::min(str.size(), N)); + } + constexpr explicit(false) operator std::string_view() const { return std::string_view{data}; } +}; + +template +string_constant(char const (&)[N]) -> string_constant; +} // namespace rsl \ No newline at end of file diff --git a/docs/api/rsl/string_util b/docs/api/rsl/string_util new file mode 100644 index 0000000..d6264e6 --- /dev/null +++ b/docs/api/rsl/string_util @@ -0,0 +1,41 @@ +#pragma once +#include +#include + +#include + + +namespace rsl { +namespace _indent_impl { +struct NonEmptyLine { + static constexpr bool operator()(std::string_view line) { + return std::ranges::any_of(line, [](char c){ return not rsl::_impl::is_whitespace(c); }); + } +}; +} + +constexpr std::string ltrim(std::string_view str) { + return {str}; +} + +constexpr std::string rtrim(std::string_view str) { + return {str}; +} + +constexpr std::string trim(std::string_view str) { + return {str}; +} + +constexpr std::string dedent(std::string_view str) { + return {str}; +} + +template +constexpr std::string indent(std::string_view str, std::string_view prefix, F&& condition = {}) { + return {str}; +} + +constexpr std::string reflow_text(std::string_view str, size_t columns = 80) { + return {str}; +} +} // namespace rsl \ No newline at end of file diff --git a/docs/api/rsl/string_view b/docs/api/rsl/string_view new file mode 100644 index 0000000..a33c6df --- /dev/null +++ b/docs/api/rsl/string_view @@ -0,0 +1,423 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +//? compat +#include + +#include +#include + +#include + +namespace rsl { +template > +class basic_string_view { +public: + // types + using traits_type = Traits; + using value_type = CharT; + using pointer = value_type*; + using const_pointer = value_type const*; + using reference = value_type&; + using const_reference = value_type const&; + using const_iterator = const_pointer; + using iterator = const_iterator; + using const_reverse_iterator = std::reverse_iterator; + using reverse_iterator = const_reverse_iterator; + using size_type = size_t; + using difference_type = ptrdiff_t; + static constexpr size_type npos = size_type(-1); + + static_assert(!std::is_array_v, + "Character type of basic_string_view must not be an array"); + static_assert(std::is_standard_layout_v, + "Character type of basic_string_view must be standard-layout"); + static_assert(std::is_trivially_copyable_v && + std::is_trivially_default_constructible_v, + "Character type of basic_string_view must be trivial"); + static_assert(std::same_as, + "traits_type::char_type must be the same type as CharT"); + + // [string.view.cons], construction and assignment + constexpr basic_string_view() noexcept : _impl_data(nullptr), _impl_size(0) {} + constexpr basic_string_view(basic_string_view const&) noexcept = default; + constexpr basic_string_view& operator=(basic_string_view const&) noexcept = default; + basic_string_view(nullptr_t) = delete; + + constexpr explicit(false) basic_string_view(const CharT* str) + : _impl_data(str) + , _impl_size(std::char_traits::length(str)) {} + constexpr basic_string_view(CharT const* str, size_type len) : _impl_data(str), _impl_size(len) {} + + template End> + requires(std::same_as, CharT> && !std::is_convertible_v) + constexpr basic_string_view(It begin, End end) + : _impl_data(std::to_address(begin)) + , _impl_size(end - begin) {} + + template + requires(!std::same_as, basic_string_view> && + std::ranges::sized_range && std::ranges::contiguous_range && + std::same_as, CharT> && + !std::is_convertible_v && (!requires(std::remove_cvref_t& range) { + range.operator rsl::basic_string_view; + })) + constexpr explicit basic_string_view(R&& r) + : _impl_data(std::ranges::data(r)) + , _impl_size(std::ranges::size(r)) {} + + //? compat + constexpr explicit(false) basic_string_view(std::basic_string_view const& other) + : _impl_data(other.data()) + , _impl_size(other.size()) {} + + //? compat + constexpr explicit(false) operator std::basic_string_view() const { + return {_impl_data, _impl_size}; + } + + // [string.view.iterators], iterator support + [[nodiscard]] constexpr const_iterator begin() const noexcept { return _impl_data; } + [[nodiscard]] constexpr const_iterator end() const noexcept { return &_impl_data[_impl_size]; } + [[nodiscard]] constexpr const_iterator cbegin() const noexcept { return begin(); } + [[nodiscard]] constexpr const_iterator cend() const noexcept { return end(); } + [[nodiscard]] constexpr const_reverse_iterator rbegin() const noexcept { return {cend()}; } + [[nodiscard]] constexpr const_reverse_iterator rend() const noexcept { return {cbegin()}; } + [[nodiscard]] constexpr const_reverse_iterator crbegin() const noexcept { return rbegin(); } + [[nodiscard]] constexpr const_reverse_iterator crend() const noexcept { return rend(); } + + // [string.view.capacity], capacity + [[nodiscard]] constexpr size_type size() const noexcept { return _impl_size; } + [[nodiscard]] constexpr size_type length() const noexcept { return _impl_size; } + [[nodiscard]] constexpr size_type max_size() const noexcept { + return std::numeric_limits::max() / sizeof(value_type); + } + [[nodiscard]] constexpr bool empty() const noexcept { return _impl_size == 0; } + + // [string.view.access], element access + [[nodiscard]] constexpr const_reference operator[](size_type pos) const { + return _impl_data[pos]; + } + [[nodiscard]] constexpr const_reference at(size_type pos) const { + if (pos >= size()) { + throw std::out_of_range("basic_string_view::at"); + } + return _impl_data[pos]; + } + [[nodiscard]] constexpr const_reference front() const { return _impl_data[0]; } + [[nodiscard]] constexpr const_reference back() const { return _impl_data[_impl_size]; } + [[nodiscard]] constexpr const_pointer data() const noexcept { return _impl_data; } + + // [string.view.modifiers], modifiers + constexpr void remove_prefix(size_type n) { + _impl_data += n; + _impl_size -= n; + } + constexpr void remove_suffix(size_type n) { _impl_size -= n; } + constexpr void swap(basic_string_view& s) noexcept { + swap(s._impl_data, _impl_data); + swap(s._impl_size, _impl_size); + } + + // [string.view.ops], string operations + constexpr size_type copy(CharT* s, size_type n, size_type pos = 0) const { + if (pos > size()) { + throw std::out_of_range("basic_string_view::copy"); + } + size_type min_len = std::min(n, size() - pos); + Traits::copy(s, data() + pos, min_len); + return min_len; + } + + [[nodiscard]] constexpr basic_string_view substr(size_type pos = 0, size_type n = npos) const { + if (pos > size()) { + throw std::out_of_range("basic_string_view::substr"); + } + + return basic_string_view(data() + pos, std::min(n, size() - pos)); + } + + [[nodiscard]] constexpr int compare(basic_string_view other) const noexcept { + size_type min_len = std::min(size(), other.size()); + int ret = Traits::compare(data(), other.data(), min_len); + if (ret == 0) { + // first `min_len` chars matched + return size() == other.size() ? 0 : (size() < other.size() ? -1 : 1); + } + return ret; + } + + [[nodiscard]] constexpr int compare(size_type pos1, size_type n1, basic_string_view s) const { + return substr(pos1, n1).compare(s); + } + + [[nodiscard]] constexpr int compare(size_type pos1, + size_type n1, + basic_string_view s, + size_type pos2, + size_type n2) const { + return substr(pos1, n1).compare(s.substr(pos2, n2)); + } + + [[nodiscard]] constexpr int compare(const CharT* s) const { + return compare(basic_string_view(s)); + } + + [[nodiscard]] constexpr int compare(size_type pos1, size_type n1, const CharT* s) const { + return substr(pos1, n1).compare(basic_string_view(s)); + } + [[nodiscard]] constexpr int compare(size_type pos1, + size_type n1, + const CharT* s, + size_type n2) const { + return substr(pos1, n1).compare(basic_string_view(s, n2)); + } + + [[nodiscard]] constexpr bool starts_with(basic_string_view other) const noexcept { + if (size() < other.size()) { + return false; + } + return compare(0, other.size(), other) == 0; + } + + [[nodiscard]] constexpr bool starts_with(CharT other) const noexcept { + return !empty() && Traits::eq(front(), other); + } + + [[nodiscard]] constexpr bool starts_with(CharT const* other) const { + return starts_with(basic_string_view(other)); + } + + [[nodiscard]] constexpr bool ends_with(basic_string_view other) const noexcept { + if (size() < other.size()) { + return false; + } + return compare(size() - other.size(), npos, other) == 0; + } + [[nodiscard]] constexpr bool ends_with(CharT other) const noexcept { + return !empty() && Traits::eq(back(), other); + } + [[nodiscard]] constexpr bool ends_with(CharT const* other) const { + return end_swith(basic_string_view(other)); + } + + [[nodiscard]] constexpr bool contains(basic_string_view other) const noexcept { + return find(other) != npos; + } + [[nodiscard]] constexpr bool contains(CharT other) const noexcept { return find(other) != npos; } + [[nodiscard]] constexpr bool contains(CharT const* other) const { return find(other) != npos; } + + // [string.view.find], searching + [[nodiscard]] constexpr size_type find(basic_string_view s, size_type pos = 0) const noexcept { + return std::string_view(_impl_data, _impl_size).find(s._impl_data, pos, s._impl_size); + } + [[nodiscard]] constexpr size_type find(CharT c, size_type pos = 0) const noexcept { + return std::string_view(_impl_data, _impl_size).find(c, pos); + } + [[nodiscard]] constexpr size_type find(CharT const* s, size_type pos, size_type n) const { + return std::string_view(_impl_data, _impl_size).find(s, pos, n); + } + [[nodiscard]] constexpr size_type find(CharT const* s, size_type pos = 0) const { + return std::string_view(_impl_data, _impl_size).find(s, pos); + } + [[nodiscard]] constexpr size_type rfind(basic_string_view s, + size_type pos = npos) const noexcept { + return std::string_view(_impl_data, _impl_size).rfind(s._impl_data, pos, s._impl_size); + } + [[nodiscard]] constexpr size_type rfind(CharT c, size_type pos = npos) const noexcept { + return std::string_view(_impl_data, _impl_size).rfind(c, pos); + } + [[nodiscard]] constexpr size_type rfind(CharT const* s, size_type pos, size_type n) const { + return std::string_view(_impl_data, _impl_size).rfind(s, pos, n); + } + [[nodiscard]] constexpr size_type rfind(CharT const* s, size_type pos = npos) const { + return std::string_view(_impl_data, _impl_size).rfind(s, pos); + } + + [[nodiscard]] constexpr size_type find_first_of(basic_string_view s, + size_type pos = 0) const noexcept { + return std::string_view(_impl_data, _impl_size).find_first_of(s._impl_data, pos, s._impl_size); + } + [[nodiscard]] constexpr size_type find_first_of(CharT c, size_type pos = 0) const noexcept { + return std::string_view(_impl_data, _impl_size).find_first_of(c, pos); + } + [[nodiscard]] constexpr size_type find_first_of(CharT const* s, + size_type pos, + size_type n) const { + return std::string_view(_impl_data, _impl_size).find_first_of(s, pos, n); + } + [[nodiscard]] constexpr size_type find_first_of(CharT const* s, size_type pos = 0) const { + return std::string_view(_impl_data, _impl_size).find_first_of(s, pos); + } + [[nodiscard]] constexpr size_type find_last_of(basic_string_view s, + size_type pos = npos) const noexcept { + return std::string_view(_impl_data, _impl_size).find_last_of(s._impl_data, pos, s._impl_size); + } + [[nodiscard]] constexpr size_type find_last_of(CharT c, size_type pos = npos) const noexcept { + return std::string_view(_impl_data, _impl_size).find_last_of(c, pos); + } + [[nodiscard]] constexpr size_type find_last_of(CharT const* s, size_type pos, size_type n) const { + return std::string_view(_impl_data, _impl_size).find_last_of(s, pos, n); + } + [[nodiscard]] constexpr size_type find_last_of(CharT const* s, size_type pos = npos) const { + return std::string_view(_impl_data, _impl_size).find_last_of(s, pos); + } + [[nodiscard]] constexpr size_type find_first_not_of(basic_string_view s, + size_type pos = 0) const noexcept { + return std::string_view(_impl_data, _impl_size) + .find_first_not_of(s._impl_data, pos, s._impl_size); + } + [[nodiscard]] constexpr size_type find_first_not_of(CharT c, size_type pos = 0) const noexcept { + return std::string_view(_impl_data, _impl_size).find_first_not_of(c, pos); + } + [[nodiscard]] constexpr size_type find_first_not_of(CharT const* s, + size_type pos, + size_type n) const { + return std::string_view(_impl_data, _impl_size).find_first_not_of(s, pos, n); + } + [[nodiscard]] constexpr size_type find_first_not_of(CharT const* s, size_type pos = 0) const { + return std::string_view(_impl_data, _impl_size).find_first_not_of(s, pos); + } + [[nodiscard]] constexpr size_type find_last_not_of(basic_string_view s, + size_type pos = npos) const noexcept { + return std::string_view(_impl_data, _impl_size).find_last_of(s._impl_data, pos, s._impl_size); + } + [[nodiscard]] constexpr size_type find_last_not_of(CharT c, size_type pos = npos) const noexcept { + return std::string_view(_impl_data, _impl_size).find_last_not_of(c, pos); + } + [[nodiscard]] constexpr size_type find_last_not_of(CharT const* s, + size_type pos, + size_type n) const { + return std::string_view(_impl_data, _impl_size).find_last_not_of(s, pos, n); + } + [[nodiscard]] constexpr size_type find_last_not_of(CharT const* s, size_type pos = npos) const { + return std::string_view(_impl_data, _impl_size).find_last_not_of(s, pos); + } + + const_pointer _impl_data; + size_type _impl_size; +}; + +// [string.view.deduct], deduction guides +template End> +basic_string_view(It, End) -> basic_string_view>; + +template +basic_string_view(R&&) -> basic_string_view>; + +// [string.view.comparison], non-member comparison functions +template +constexpr bool operator==(basic_string_view lhs, + std::basic_string_view rhs) noexcept { + // this is a better match for comparisons of rsl::string_view to std::string_view, it + // disambiguates + return std::string_view(lhs) == rhs; +} + +template +constexpr bool operator==(basic_string_view lhs, + std::type_identity_t> rhs) noexcept { + if (lhs.size() != rhs.size()) { + return false; + } + return lhs.compare(rhs) == 0; +} + +template +constexpr auto operator<=>(basic_string_view lhs, + std::basic_string_view rhs) noexcept { + // better match for comparisons between rsl::string_view and std::string_view + return std::string_view(lhs) <=> rhs; +} + +template +constexpr auto operator<=>(basic_string_view lhs, + std::type_identity_t> rhs) noexcept { + if constexpr (requires { typename Traits::comparison_category; }) { + return static_cast(lhs.compare(rhs) <=> 0); + } else { + return static_cast(lhs.compare(rhs) <=> 0); + } +} + +// basic_string_view typedef-names +using string_view = basic_string_view; +using u8string_view = basic_string_view; +using u16string_view = basic_string_view; +using u32string_view = basic_string_view; +using wstring_view = basic_string_view; + +inline namespace literals { +inline namespace string_view_literals { +// [string.view.literals], suffix for basic_string_view literals +$diagnostics(push) +$diagnostics(disable, literal_suffix) + +constexpr string_view operator""sv(const char* str, size_t len) noexcept { + return {str, len}; +} +constexpr u8string_view operator""sv(const char8_t* str, size_t len) noexcept { + return {str, len}; +} +constexpr u16string_view operator""sv(const char16_t* str, size_t len) noexcept { + return {str, len}; +} +constexpr u32string_view operator""sv(const char32_t* str, size_t len) noexcept { + return {str, len}; +} +constexpr wstring_view operator""sv(const wchar_t* str, size_t len) noexcept { + return {str, len}; +} + +$diagnostics(pop) + +} // namespace string_view_literals +} // namespace literals +} // namespace rsl + +// [string.view.hash], hash support +template <> +struct std::hash; +template <> +struct std::hash; +template <> +struct std::hash; +template <> +struct std::hash; +template <> +struct std::hash; + +template +constexpr bool std::ranges::enable_view> = true; +template +constexpr bool std::ranges::enable_borrowed_range> = true; + +// [string.view.io], inserters and extractors +template +std::basic_ostream& operator<<(std::basic_ostream& os, + rsl::basic_string_view str) { + // TODO + os << std::string_view(str); +} + +// TODO +template +struct std::formatter, CharT> + : std::formatter, CharT> { + using base = std::formatter, CharT>; + // constexpr auto parse(std::format_parse_context& ctx) { } + + constexpr auto format(rsl::basic_string_view const& obj, + std::format_context& ctx) const { + // return std::format_to(ctx.out(), std::string_view(obj._impl_data, obj._impl_size)); + return base::format(std::string_view(obj), ctx); + } +}; diff --git a/docs/api/rsl/trie b/docs/api/rsl/trie new file mode 100644 index 0000000..8919c14 --- /dev/null +++ b/docs/api/rsl/trie @@ -0,0 +1,187 @@ +#pragma once + +#include +#include +#include +#include +#include +#include +#include +#include + +#include +#include +#include +#include + +#include + +namespace rsl { +namespace _trie_impl { +template +struct State { + rsl::string_view prefix; + int word_index{}; + + $inline(always) constexpr int visit(std::string_view str) const { + // assumes the prefix is never empty + //? empty prefixes are only allowed at the root note + if (str.empty() || str[0] != prefix[0] || str.size() < prefix.size()) { + return 0; + } + + if (str.size() == prefix.size()) { + // str would become empty after removing the prefix + return word_index; + } + + str.remove_prefix(prefix.size()); + template for (constexpr auto T : {Transitions...}) { + if (int result = T.visit(str); result) { + return result; + } + } + return 0; + } +}; + +template +struct Root { + static constexpr auto&& words = [:Words:]; + + constexpr static bool matches(std::string_view str) { + if (str.empty()) { + return false; + } + return find(str) >= 0; + } + + constexpr static int find(std::string_view str) { + int result = -1; + if (str.empty()) { + return -1; + } + template for (constexpr auto T : {Transitions...}) { + if (int result = T.visit(str); result && str == words[result - 1]) { + return result - 1; + } + } + return -1; + } +}; + +template +constexpr inline Root make_trie{}; + +template +constexpr inline auto make_state = State{ + {transition.data, transition.size}, + word_index +}; + +struct ParsedState { + std::string prefix; + std::vector transitions; + rsl::span words; + int word_index = 0; + + static constexpr ParsedState make(rsl::span words) { + std::vector sorted_words{}; + sorted_words.assign_range(words); + std::ranges::sort(sorted_words); + + ParsedState root{"", {}, words}; + + for (auto word : sorted_words) { + ParsedState* node = &root; + std::size_t i = 0; + + while (i < word.size()) { + auto it = std::ranges::find_if(node->transitions, [&](const ParsedState& s) { + return s.prefix.starts_with(word[i]); + }); + + if (it == node->transitions.end()) { + auto word_idx = std::ranges::distance(words.begin(), std::ranges::find(words, word)) + 1; + + node->transitions.emplace_back(std::string(word.substr(i)), + std::vector{ + {"", {}, {}, static_cast(word_idx)} + }); + break; + } + + std::size_t const match_len = + std::mismatch(it->prefix.begin(), it->prefix.end(), word.begin() + i).first - + it->prefix.begin(); + + if (match_len < it->prefix.size()) { + ParsedState split_node{it->prefix.substr(match_len), std::move(it->transitions)}; + it->prefix.resize(match_len); + it->transitions = {std::move(split_node)}; + } + + i += match_len; + node = &*it; + } + } + return root; + } + + explicit(false) consteval operator std::meta::info() const { + std::vector states = {}; + int index = 0; + for (auto&& state : transitions) { + if (state.prefix.empty()) { + index = state.word_index; + continue; + } + states.push_back(state); + } + + if (!transitions.empty() && prefix.empty()) { + // prevent aggressive inlining at the root level + std::vector word_list; + for (auto word : words) { + word_list.emplace_back(define_static_string(word)); + } + states.insert(states.begin(), reflect_constant(std::meta::reflect_constant_array(word_list))); + return substitute(^^make_trie, states); + } + + std::vector args = {std::meta::reflect_constant_string(prefix), + std::meta::reflect_constant(index)}; + for (auto state : states) { + args.push_back(state); + } + return substitute(^^make_state, args); + } +}; + +constexpr bool has_duplicates(std::span wordlist) { + std::ranges::sort(wordlist); + return std::ranges::adjacent_find(wordlist) != wordlist.end(); +} +} // namespace _trie_impl + +struct trie { + bool (*matches)(std::string_view str) = nullptr; + int (*find)(std::string_view str) = nullptr; + + explicit consteval trie(std::vector words) { + constexpr_assert(!words.empty(), "An empty word list is not allowed."); + constexpr_assert(!_trie_impl::has_duplicates(words), + "Duplicates in the word list are not allowed."); + + (this->*extract( + substitute(^^assign_handlers, {_trie_impl::ParsedState::make(words)})))(); + } + +private: + template + constexpr void assign_handlers() { + matches = &Parser.matches; + find = &Parser.find; + } +}; +} // namespace rsl \ No newline at end of file diff --git a/docs/api/rsl/tuple b/docs/api/rsl/tuple new file mode 100644 index 0000000..9bd7051 --- /dev/null +++ b/docs/api/rsl/tuple @@ -0,0 +1,588 @@ +#pragma once +#include // see [compare.syn] +#include +#include +#include +#include +#include + +#include +#include +#include + +#include +#include +#include + +#if $uses_opt(RSL_STD_COMPAT) +# include +#endif + +namespace rsl { +// [tuple.tuple], class template tuple +template +class tuple; + +// ignore +namespace _tuple_impl { +#if $uses_opt(RSL_STD_COMPAT) +using ignore_type = decltype(std::ignore); +#else +struct ignore_type { // exposition only + constexpr ignore_type const& operator=(const auto&) const noexcept { return *this; } +}; +#endif +} // namespace _tuple_impl + +inline constexpr _tuple_impl::ignore_type ignore; + +// [tuple.helper], tuple helper classes +template +struct tuple_size; + +template +struct tuple_size : tuple_size {}; + +template +struct tuple_size> : std::integral_constant {}; + +template +constexpr std::size_t tuple_size_v = tuple_size::value; + +template +struct tuple_element; + +template +struct tuple_element const> { + using type = std::add_const_t; +}; + +template +struct tuple_element> { + using type = Types...[I]; +}; + +template +using tuple_element_t = typename tuple_element::type; + +namespace _tuple_impl { +// [tuple.like], concept tuple-like +// TODO ranges::subrange +template +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)) == ^^rsl::tuple); // exposition only + +template +concept is_rsl_tuple = + has_template_arguments(remove_cvref(^^T)) && template_of(remove_cvref(^^T)) == ^^rsl::tuple; + +template +void test_implicit_default_constructibility(T) { + static_assert(false, "test_implicit_default_constructibility called in evaluated context"); +} + +template +concept is_implicitly_default_constructible = std::is_default_constructible_v && requires { + test_implicit_default_constructibility({}); +}; + +#if $compiler_is(CLANG) +template +struct SubscriptElement { + constexpr decltype(auto) operator[](std::size_t idx) & $enable_if(idx == Idx, "index mismatch") { + return get(static_cast(*this)); + } + + constexpr decltype(auto) operator[](std::size_t idx) const& $enable_if(idx == Idx, + "index mismatch") { + return get(static_cast(*this)); + } + + constexpr decltype(auto) operator[](std::size_t idx) && $enable_if(idx == Idx, "index mismatch") { + return get(static_cast(*this)); + } + + constexpr decltype(auto) operator[](std::size_t idx) const&& $enable_if(idx == Idx, + "index mismatch") { + return get(static_cast(*this)); + } +}; + +template >> +struct Subscript; + +template +struct Subscript> : SubscriptElement... { + using SubscriptElement::operator[]...; + + static constexpr decltype(auto) operator[](std::size_t idx) + $enable_if(idx >= sizeof...(Idx), "index mismatch") { + std::unreachable(); + } +}; +#endif +} // namespace _tuple_impl + +template +class tuple +#if $compiler_is(CLANG) + : public _tuple_impl::Subscript> +#endif +{ +private: + template + constexpr static bool enable_utypes_ctor = + 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, + {data_member_spec(^^Types, {.name = "_impl_" + to_string(idx++)})...}); + } + + 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()); + } + } + + template + constexpr storage_type _impl_converting_constructor(T&& o, std::index_sequence) { + return {([&]()->Types... [Is] { + if constexpr (std::is_reference_v) + return o.template get(); + else + return Types... [Is] { std::forward_like(o.template get()) }; + }())...}; + } + +public: + storage_type _impl_storage; + static constexpr auto _impl_accessor = [:_impl::cache_members(nonstatic_data_members_of( + ^^storage_type, + std::meta::access_context::unchecked())):]; + + // template + // static constexpr auto _impl_disambiguation_constraint = + // (sizeof...(Types) == 1 && !std::same_as>>, tuple>) || + // ((sizeof...(Types) == 2 || sizeof...(Types) == 3) && + // (!std::same_as>>, + // std::allocator_arg_t> || + // std::same_as>>, + // std::allocator_arg_t>)) || + // sizeof...(Types) >= 1; + + // [tuple.cnstr], tuple construction + constexpr explicit((!_tuple_impl::is_implicitly_default_constructible || + ...)) // [tuple.cnstr]/8 + tuple() noexcept(std::is_nothrow_default_constructible_v) + requires(sizeof...(Types) >= 1 && + std::is_default_constructible_v) // [tuple.cnstr]/6 + = default; + + constexpr tuple() noexcept + requires(sizeof...(Types) == 0) + = default; // [tuple.cnstr]/5 + + constexpr explicit(!(std::is_convertible_v && ...)) // [tuple.cnstr]/11 + tuple(Types const&... values) noexcept((std::is_nothrow_copy_constructible_v && ...)) + requires(sizeof...(Types) >= 1 && + (std::is_copy_constructible_v && ...)) // [tuple.cnstr]/9 + : _impl_storage{values...} {} + + template + requires(sizeof...(UTypes) == sizeof...(Types) && // [tuple.cnstr]/13.1 + sizeof...(UTypes) >= 1 && // [tuple.cnstr]/13.2 + enable_utypes_ctor && + (std::is_constructible_v && ...)) // [tuple.cnstr]/13.3 + constexpr explicit(!(std::is_convertible_v && ...)) // [tuple.cnstr]/15 + tuple(UTypes&&... values) + : _impl_storage(std::forward(values)...) {} + + template + requires(sizeof...(UTypes) == sizeof...(Types) && + (std::reference_constructs_from_temporary_v || // [tuple.cnstr]/15 + ...)) + constexpr tuple(UTypes&&... values) = delete; + + constexpr tuple(tuple const&) noexcept(std::is_nothrow_copy_constructible_v) + requires(std::is_copy_constructible_v) // [tuple.cnstr]/16 + = default; + + constexpr tuple(tuple&&) noexcept(std::is_nothrow_move_constructible_v) + requires(std::is_move_constructible_v) // [tuple.cnstr]/18 + = default; + + template + requires(sizeof...(UTypes) == sizeof...(Types)) && // [tuple.cnstr]/21.1 + (std::is_constructible_v && ...) && // [tuple.cnstr]/21.2 + (sizeof...(UTypes) != 1 || // [tuple.cnstr]/21.3 + (!std::is_convertible_v, Types...[0]> && // [tuple.cnstr]/21.3 + !std::is_constructible_v> && // [tuple.cnstr]/21.3 + !std::same_as)) // [tuple.cnstr]/21.3 + constexpr explicit(!(std::is_convertible_v && ...)) // [tuple.cnstr]/23 + tuple(tuple& other) noexcept((std::is_nothrow_constructible_v && + ...)) + : _impl_storage(_impl_converting_constructor(other, std::index_sequence_for{})) {} + + template + requires(sizeof...(UTypes) == sizeof...(Types)) && // [tuple.cnstr]/21.1 + (std::is_constructible_v && ...) && // [tuple.cnstr]/21.2 + (sizeof...(UTypes) != 1 || // [tuple.cnstr]/21.3 + (!std::is_convertible_v, Types...[0]> && // [tuple.cnstr]/21.3 + !std::is_constructible_v> && // [tuple.cnstr]/21.3 + !std::same_as)) // [tuple.cnstr]/21.3 + constexpr explicit(!(std::is_convertible_v && ...)) // [tuple.cnstr]/23 + tuple(tuple const& other) noexcept( + (std::is_nothrow_constructible_v && ...)) + : _impl_storage(_impl_converting_constructor(other, std::index_sequence_for{})) {} + + template + requires(sizeof...(UTypes) == sizeof...(Types)) && // [tuple.cnstr]/21.1 + (std::is_constructible_v && ...) && // [tuple.cnstr]/21.2 + (sizeof...(UTypes) != 1 || // [tuple.cnstr]/21.3 + (!std::is_convertible_v, Types...[0]> && // [tuple.cnstr]/21.3 + !std::is_constructible_v> && // [tuple.cnstr]/21.3 + !std::same_as)) // [tuple.cnstr]/21.3 + constexpr explicit(!(std::is_convertible_v && ...)) // [tuple.cnstr]/23 + tuple(tuple&& other) noexcept((std::is_nothrow_constructible_v && + ...)) + : _impl_storage( + _impl_converting_constructor(std::move(other), std::index_sequence_for{})) {} + + template + requires(sizeof...(UTypes) == sizeof...(Types)) && // [tuple.cnstr]/21.1 + (std::is_constructible_v && ...) && // [tuple.cnstr]/21.2 + (sizeof...(UTypes) != 1 || // [tuple.cnstr]/21.3 + (!std::is_convertible_v, Types...[0]> && // [tuple.cnstr]/21.3 + !std::is_constructible_v> && // [tuple.cnstr]/21.3 + !std::same_as)) // [tuple.cnstr]/21.3 + constexpr explicit(!(std::is_convertible_v && ...)) // [tuple.cnstr]/23 + tuple(tuple const&& other) noexcept( + (std::is_nothrow_constructible_v && ...)) + : _impl_storage( + _impl_converting_constructor(std::move(other), std::index_sequence_for{})) {} + + // template + // constexpr explicit(true /* TODO*/) tuple(std::pair&); // only if + // sizeof...(Types) == 2 template constexpr explicit(true /* TODO*/) + // tuple(const std::pair&); // only if sizeof...(Types) == 2 + // template + // constexpr explicit(true /* TODO*/) tuple(std::pair&&); // only if + // sizeof...(Types) + // == 2 template constexpr explicit(true /* TODO*/) + // tuple(const std::pair&&); // only if sizeof...(Types) == 2 + + // template + // constexpr explicit(true /* TODO*/) tuple(UTuple&&); + + // // allocator-extended constructors + // template + // constexpr explicit(true /* TODO*/) tuple(std::allocator_arg_t, const Alloc& a); + // template + // constexpr explicit(true /* TODO*/) tuple(std::allocator_arg_t, const Alloc& a, const + // Types&...); template constexpr explicit(true /* TODO*/) + // tuple(std::allocator_arg_t, const Alloc& a, UTypes&&...); template + // constexpr tuple(std::allocator_arg_t, const Alloc& a, const tuple&); template constexpr tuple(std::allocator_arg_t, const Alloc& a, tuple&&); template constexpr explicit(true /* TODO*/) tuple(std::allocator_arg_t, + // const Alloc& a, tuple&); template constexpr + // explicit(true /* + // TODO*/) + // tuple(std::allocator_arg_t, const Alloc& a, const tuple&); + // template + // constexpr explicit(true /* TODO*/) + // tuple(std::allocator_arg_t, const Alloc& a, tuple&&); + // template + // constexpr explicit(true /* TODO*/) + // tuple(std::allocator_arg_t, const Alloc& a, const tuple&&); + // template + // constexpr explicit(true /* TODO*/) + // tuple(std::allocator_arg_t, const Alloc& a, std::pair&); + // template + // constexpr explicit(true /* TODO*/) + // tuple(std::allocator_arg_t, const Alloc& a, const std::pair&); + // template + // constexpr explicit(true /* TODO*/) + // tuple(std::allocator_arg_t, const Alloc& a, std::pair&&); + // template + // constexpr explicit(true /* TODO*/) + // tuple(std::allocator_arg_t, const Alloc& a, const std::pair&&); + + // template + // constexpr explicit(true /* TODO*/) tuple(std::allocator_arg_t, const Alloc& a, UTuple&&); + + //! non-standard: get member function + template + constexpr decltype(auto) get(this Self&& self) { + return std::forward_like(_impl_accessor.template get(self._impl_storage)); + } + + template + constexpr decltype(auto) get(this Self&& self) { + return std::forward_like( + _impl_accessor.template get<_impl::index_of>>( + self._impl_storage)); + } + + // [tuple.assign], tuple assignment + + 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; + } + + _impl_copy_assign(other); + return *this; + } + constexpr tuple& operator=(tuple const& other) = delete; + + constexpr tuple const& operator=(tuple const& other) const + noexcept((std::is_nothrow_copy_assignable_v> && ...)) + requires((std::is_copy_assignable_v> && ...)) // [tuple.assign]/5 + { + _impl_copy_assign(other); + return *this; + } + + constexpr tuple& operator=(tuple&& other) // + noexcept((std::is_nothrow_move_constructible_v && ...)) // [tuple.assign]/11 + requires((std::is_move_assignable_v && ...)) // [tuple.assign]/8 + { + _impl_move_assign(std::move(other)); + return *this; + } + + constexpr tuple const& operator=(tuple&& other) const + requires((std::is_assignable_v && ...)) // [tuple.assign]/12 + { + _impl_move_assign(std::move(other)); + return *this; + } + + 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) && // [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) && // [tuple.assign]/21.1 + (std::is_assignable_v && ...)) // [tuple.assign]/21.2 + constexpr tuple& operator=(tuple&& other) { + _impl_move_assign(std::move(other)); + return *this; + } + + 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 { + _impl_move_assign(std::move(other)); + return *this; + } + + // template + // constexpr tuple& operator=(const std::pair&); // only if sizeof...(Types) == 2 + // template + // constexpr const tuple& operator=(const std::pair&) const; + // // only if sizeof...(Types) == 2 + // template + // constexpr tuple& operator=(std::pair&&); // only if sizeof...(Types) == 2 + // template + // constexpr const tuple& operator=(std::pair&&) const; // only if sizeof...(Types) == + // 2 + + // template + // constexpr tuple& operator=(UTuple&&); + // template + // constexpr const tuple& operator=(UTuple&&) const; + + // [tuple.swap], tuple swap + // constexpr void swap(tuple&) noexcept(true /* TODO */); + // constexpr void swap(const tuple&) const noexcept(true /* TODO */); +}; + +template +tuple(UTypes...) -> tuple; +template +tuple(std::pair) -> tuple; +template +tuple(std::allocator_arg_t, Alloc, UTypes...) -> tuple; +template +tuple(std::allocator_arg_t, Alloc, std::pair) -> tuple; +template +tuple(std::allocator_arg_t, Alloc, tuple) -> tuple; + +// [tuple.creation], tuple creation functions +template +constexpr tuple...> make_tuple(TTypes&&... values) { + return {std::forward(values)...}; +} + +template +constexpr tuple forward_as_tuple(TTypes&&... values) noexcept { + return {std::forward(values)...}; +} + +template +constexpr tuple tie(TTypes&... values) noexcept { + return {values...}; +} + +//! non-standard extension +template <_tuple_impl::is_rsl_tuple R, _tuple_impl::is_rsl_tuple L> +constexpr auto operator+(R&& rhs, L&& lhs) { + auto&& [... rhs_members] = std::forward(rhs); + auto&& [... lhs_members] = std::forward(lhs); + return tuple..., + std::remove_cvref_t...>(std::forward_like(rhs_members)..., + std::forward_like(lhs_members)...); +} + +template <_tuple_impl::is_rsl_tuple... Tuples> +constexpr auto tuple_cat(Tuples&&... tuples) { + if constexpr (sizeof...(Tuples) == 0) { + return std::tuple<>(); + } else { + return (std::forward(tuples) + ...); + } +} + +// [tuple.apply], calling a function with a tuple of arguments +namespace _tuple_impl { +template +consteval bool is_nothrow_apply(F&& f, Tuple&& t) { + auto&& [... args] = std::forward(t); + return noexcept(std::invoke(std::forward(f), std::forward_like(args)...)); +} +} // namespace _tuple_impl + +template +constexpr decltype(auto) apply(F&& f, Tuple&& t) noexcept( + _tuple_impl::is_nothrow_apply(std::declval(), std::declval())) { + auto&& [... args] = std::forward(t); + return std::invoke(std::forward(f), std::forward_like(args)...); +} + +template +constexpr T make_from_tuple(Tuple&& t) { + auto&& [... members] = std::forward(t); + return {std::forward_like(members)...}; +} + +// [tuple.elem], element access +template +constexpr decltype(auto) get(T&& obj) noexcept { + return std::forward_like(obj.template get()); +} + +template +constexpr decltype(auto) get(U&& obj) noexcept { + return std::forward_like(obj.template get()); +} + +// [tuple.rel], relational operators +template +constexpr bool operator==(tuple const& rhs, tuple const& lhs) { + static_assert(sizeof...(TTypes) == sizeof...(UTypes), "Cannot compare tuples of unequal size"); + if constexpr (sizeof...(TTypes) == 0) { + return true; + } else { + auto const& [... rhs_elts] = rhs; + auto const& [... lhs_elts] = lhs; + return (... && (rhs_elts == lhs_elts)); + } +} + +// template +// constexpr bool operator==(const tuple&, const UTuple&); + +// template +// constexpr std::common_comparison_category_t...> +// operator<=>( +// const tuple&, +// const tuple&); +// template +// constexpr std::common_comparison_category_t...> +// operator<=>( +// const tuple&, +// const UTuple&); + +// [tuple.special], specialized algorithms +// template +// constexpr void swap(tuple& x, tuple& y) noexcept(true /* TODO */); +// template +// constexpr void swap(const tuple& x, const tuple& y) noexcept(true /* TODO +// */); + +} // namespace rsl + +// [tuple.common.ref], common_reference related specializations +// template class TQual, +// template class UQual> +// struct std::basic_common_reference; +// template +// struct std::common_type; + +// [tuple.traits], allocator-related traits +// template +// struct std::uses_allocator, Alloc>; + +template +struct std::tuple_size> : std::integral_constant {}; +template +struct std::tuple_size const> + : std::integral_constant {}; + +template +struct std::tuple_element const> { + using type = std::add_const_t; +}; + +template +struct std::tuple_element> { + using type = Ts...[I]; +}; diff --git a/docs/api/rsl/variant b/docs/api/rsl/variant new file mode 100644 index 0000000..7b9fb46 --- /dev/null +++ b/docs/api/rsl/variant @@ -0,0 +1,1035 @@ +#pragma once +#include +#include +#include +#include +#include +#include +#include +#include +#include + +#include + +#include +#include +#include + +#include + +namespace rsl { +template +struct variant_alternative; + +template +struct variant_size; + +namespace _variant_impl { + +inline constexpr std::size_t variant_npos = -1ULL; +[[noreturn]] void throw_bad_variant_access(bool); + +class bad_variant_access : public std::exception { + friend void throw_bad_variant_access(bool); + char const* reason = "bad variant access"; + explicit bad_variant_access(const char* message) noexcept : reason(message) {} + +public: + bad_variant_access() noexcept = default; + [[nodiscard]] const char* what() const noexcept override { return reason; } +}; + +[[noreturn]] inline void throw_bad_variant_access(bool valueless) { +#if __cpp_exceptions + if (valueless) { + throw bad_variant_access("variant is valueless"); + } + throw bad_variant_access("wrong index for variant"); +#else + std::abort(); +#endif +} + +template +concept allowed_conversion = requires(Source obj) { std::type_identity_t{std::move(obj)}; }; + +template +struct Build_FUN { + template U> + auto operator()(T, U&&) -> std::integral_constant; +}; + +template > +struct Build_FUNs; + +template