/*============================================================================= Copyright (c) 2025 Nana Sakisaka Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) =============================================================================*/ #include "test.hpp" #include #include #include #include #include // Don't place these in anonymous namespace; they make debug harder template struct ref_holder { T& ref; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members) }; struct existent_tag; struct non_existent_tag; struct next_tag; TEST_CASE("context") { using x4::context; { using NextContext = context; using Context = context; STATIC_CHECK(sizeof(NextContext) == sizeof(ref_holder)); STATIC_CHECK(sizeof(Context) == sizeof(ref_holder) + sizeof(NextContext)); } { using NextContext = context; using Context = context; STATIC_CHECK(sizeof(NextContext) == sizeof(ref_holder)); STATIC_CHECK(sizeof(Context) == sizeof(ref_holder) + sizeof(ref_holder)); } { struct IntRef { int& ref; }; // NOLINT(cppcoreguidelines-avoid-const-or-ref-data-members) STATIC_CHECK(sizeof(context) == sizeof(IntRef)); STATIC_CHECK(sizeof(context>) == sizeof(IntRef) * 2); STATIC_CHECK(sizeof(context const&>) == sizeof(IntRef) * 2); } { // Replace the first context of `context<..., non-owning>` { double j = 3.14; auto next_ctx = x4::make_context(j); int i = 42; auto ctx = x4::make_context(i, next_ctx); STATIC_CHECK(std::same_as const&>>); int replaced_i = 43; auto&& replaced_ctx = x4::replace_first_context(ctx, replaced_i); STATIC_CHECK(std::same_as const&>&&>); } // Replace the first context of `context<..., owning>` { double j = 3.14; int i = 42; auto ctx = x4::make_context(i, x4::make_context(j)); STATIC_CHECK(std::same_as>>); int replaced_i = 43; auto&& replaced_ctx = x4::replace_first_context(ctx, replaced_i); // Should not create copy of `Next` STATIC_CHECK(std::same_as /* not reference */>&&>); } } { int i = 42; auto ctx = x4::make_context(i); using Context = decltype(ctx); STATIC_CHECK(std::same_as>); STATIC_CHECK(std::move_constructible); STATIC_CHECK(std::copy_constructible); STATIC_CHECK(!std::assignable_from); CHECK(x4::get(ctx) == 42); { auto&& removed_ctx = x4::remove_first_context(ctx); STATIC_CHECK(std::same_as); // monostate tests { auto&& mono_removed_ctx = x4::remove_first_context(removed_ctx); STATIC_CHECK(std::same_as); (void)mono_removed_ctx; } { double d = 3.14; auto&& mono_replaced_ctx = x4::replace_first_context(removed_ctx, d); STATIC_CHECK(std::same_as&&>); CHECK(x4::get(ctx) == 42); CHECK(x4::get(mono_replaced_ctx) == 3.14); } } { auto&& removed_ctx = x4::remove_first_context(ctx); STATIC_CHECK(std::same_as const&>); CHECK(x4::get(ctx) == 42); CHECK(x4::get(removed_ctx) == 42); } { double d = 3.14; auto&& replaced_ctx = x4::replace_first_context(ctx, d); STATIC_CHECK(std::same_as&&>); CHECK(x4::get(ctx) == 42); CHECK(x4::get(replaced_ctx) == 3.14); } { double d = 3.14; auto&& replaced_ctx = x4::replace_first_context(ctx, d); STATIC_CHECK(std::same_as>&&>); CHECK(x4::get(ctx) == 42); CHECK(x4::get(replaced_ctx) == 42); CHECK(x4::get(replaced_ctx) == 3.14); i = 43; CHECK(x4::get(ctx) == 43); CHECK(x4::get(replaced_ctx) == 43); i = 42; } { auto ctx_ctx = x4::make_context(i, ctx); using ContextContext = decltype(ctx_ctx); STATIC_CHECK(std::same_as const&>>); STATIC_CHECK(std::move_constructible); STATIC_CHECK(std::copy_constructible); STATIC_CHECK(!std::assignable_from); CHECK(x4::get(ctx) == 42); CHECK(x4::get(ctx_ctx) == 42); i = 43; CHECK(x4::get(ctx) == 43); CHECK(x4::get(ctx_ctx) == 43); i = 42; STATIC_CHECK(std::move_constructible); STATIC_CHECK(std::copy_constructible); STATIC_CHECK(!std::assignable_from); } } // --------------------------------------- { int i = 42; auto ctx = x4::make_context(std::as_const(i)); CHECK(x4::get(ctx) == 42); STATIC_CHECK(std::same_as(ctx)), int const&>); STATIC_CHECK(std::same_as(std::as_const(ctx))), int const&>); } // Start with plain `context` { int i = 42; auto ctx = x4::make_context(i); STATIC_CHECK(std::same_as>); CHECK(x4::get(ctx) == 42); STATIC_CHECK(std::same_as(ctx)), int&>); STATIC_CHECK(std::same_as(std::as_const(ctx))), int&>); CHECK(std::addressof(ctx.val) == std::addressof(i)); { int j = 999; auto&& replaced_ctx = x4::replace_first_context(ctx, j); STATIC_CHECK(std::same_as&&>); CHECK(x4::get(replaced_ctx) == 999); CHECK(std::addressof(ctx.val) == std::addressof(i)); CHECK(std::addressof(replaced_ctx.val) == std::addressof(j)); { auto&& removed_ctx = x4::remove_first_context(replaced_ctx); STATIC_CHECK(std::same_as const&>); CHECK(std::addressof(removed_ctx) == std::addressof(replaced_ctx)); } { auto&& removed_ctx = x4::remove_first_context(replaced_ctx); STATIC_CHECK(std::same_as); { [[maybe_unused]] auto&& removed_removed_ctx = x4::remove_first_context(removed_ctx); STATIC_CHECK(std::same_as); } { [[maybe_unused]] auto&& new_ctx = x4::make_context(i, removed_ctx); STATIC_CHECK(std::same_as&&>); } { auto&& new_ctx = x4::replace_first_context(removed_ctx, i); STATIC_CHECK(std::same_as&&>); CHECK(std::addressof(new_ctx.val) == std::addressof(i)); } } } { double d = 3.14; auto replaced_ctx = x4::replace_first_context(ctx, d); STATIC_CHECK(std::same_as>); CHECK(x4::get(replaced_ctx) == 3.14); CHECK(std::addressof(ctx.val) == std::addressof(i)); CHECK(std::addressof(replaced_ctx.val) == std::addressof(d)); } { double d = 3.14; auto replaced_ctx = x4::replace_first_context(ctx, d); STATIC_CHECK(std::same_as>>); CHECK(x4::get(replaced_ctx) == 42); CHECK(std::addressof(ctx.val) == std::addressof(i)); CHECK(std::addressof(x4::get(replaced_ctx)) == std::addressof(d)); } } // Start with nested; `context>` { struct dummy_tag; struct dummy_t {}; constexpr dummy_t dummy; auto const dummy_ctx = x4::make_context(dummy); using dummy_ctx_t = context; STATIC_CHECK(std::same_as); int i = 42; auto ctx = x4::make_context(i, dummy_ctx); STATIC_CHECK(std::same_as>); CHECK(x4::get(ctx) == 42); STATIC_CHECK(std::same_as(ctx)), int&>); STATIC_CHECK(std::same_as(std::as_const(ctx))), int&>); CHECK(std::addressof(ctx.val) == std::addressof(i)); { int j = 999; auto&& replaced_ctx = x4::replace_first_context(ctx, j); STATIC_CHECK(std::same_as&&>); CHECK(x4::get(replaced_ctx) == 999); CHECK(std::addressof(ctx.val) == std::addressof(i)); CHECK(std::addressof(replaced_ctx.val) == std::addressof(j)); { auto&& removed_ctx = x4::remove_first_context(replaced_ctx); STATIC_CHECK(std::same_as const&>); CHECK(std::addressof(removed_ctx) == std::addressof(replaced_ctx)); { [[maybe_unused]] auto&& removed_removed_ctx = x4::remove_first_context(removed_ctx); STATIC_CHECK(std::same_as); } { [[maybe_unused]] auto new_ctx = x4::make_context(i, removed_ctx); STATIC_CHECK(std::same_as const&>>); } { auto&& new_ctx = x4::replace_first_context(removed_ctx, i); STATIC_CHECK(std::same_as&&>); CHECK(std::addressof(new_ctx.val) == std::addressof(i)); } } } { double d = 3.14; auto&& replaced_ctx = x4::replace_first_context(ctx, d); STATIC_CHECK(std::same_as&&>); CHECK(x4::get(replaced_ctx) == 3.14); CHECK(std::addressof(ctx.val) == std::addressof(i)); CHECK(std::addressof(replaced_ctx.val) == std::addressof(d)); } { double d = 3.14; auto&& replaced_ctx = x4::replace_first_context(ctx, d); STATIC_CHECK(std::same_as< decltype(replaced_ctx), context< existent_tag, int, context< dummy_tag, double > >&& >); CHECK(x4::get(replaced_ctx) == 42); CHECK(std::addressof(ctx.val) == std::addressof(i)); CHECK(std::addressof(x4::get(replaced_ctx)) == std::addressof(d)); } { double d = 3.14; auto&& replaced_ctx = x4::replace_first_context(ctx, d); STATIC_CHECK(std::same_as< decltype(replaced_ctx), context< existent_tag, int, context< dummy_tag, dummy_t const, context< non_existent_tag, double > > >&& >); CHECK(x4::get(replaced_ctx) == 42); CHECK(std::addressof(ctx.val) == std::addressof(i)); CHECK(std::addressof(x4::get(replaced_ctx)) == std::addressof(d)); } } }