From 378b64e5d548848764c005a1ab3778505a1af8a2 Mon Sep 17 00:00:00 2001 From: Ben Deane Date: Fri, 28 Feb 2025 09:49:42 -0700 Subject: [PATCH] :sparkles: Add `env` to hold compile-time values Problem: - CIB log env is a generic component that is useful beyond logging and should live in stdx. Solution: - Add `stdx::env`. Note: - For the moment this is dealing with compile-time values only. It may be possible in future to unify this with runtime environments used by S&R. --- CMakeLists.txt | 1 + include/stdx/env.hpp | 91 ++++++++++++++++++++++++++++++++++++++++++++ test/CMakeLists.txt | 9 ++++- test/env.cpp | 44 +++++++++++++++++++++ 4 files changed, 144 insertions(+), 1 deletion(-) create mode 100644 include/stdx/env.hpp create mode 100644 test/env.cpp diff --git a/CMakeLists.txt b/CMakeLists.txt index e448fa8..a784133 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -60,6 +60,7 @@ target_sources( include/stdx/cx_vector.hpp include/stdx/detail/bitset_common.hpp include/stdx/detail/list_common.hpp + include/stdx/env.hpp include/stdx/for_each_n_args.hpp include/stdx/functional.hpp include/stdx/function_traits.hpp diff --git a/include/stdx/env.hpp b/include/stdx/env.hpp new file mode 100644 index 0000000..8c7b0a5 --- /dev/null +++ b/include/stdx/env.hpp @@ -0,0 +1,91 @@ +#pragma once + +#if __cplusplus >= 202002L + +#include +#include + +#include + +namespace stdx { +inline namespace v1 { +template struct ct_prop { + [[nodiscard]] CONSTEVAL static auto query(decltype(Query)) noexcept { + return Value; + } +}; + +namespace _env { +template +concept valid_query_for = requires(Env const &e) { e.query(Q{}); }; + +template +concept valid_query_over = (... or valid_query_for); + +template struct has_query { + template + using fn = std::bool_constant>; +}; +} // namespace _env + +template struct env { + template <_env::valid_query_over Q> + CONSTEVAL static auto query(Q) noexcept { + using I = boost::mp11::mp_find_if_q, + _env::has_query>; + using E = boost::mp11::mp_at, I>; + return Q{}(E{}); + } +}; + +namespace _env { +template struct autowrap { + // NOLINTNEXTLINE(google-explicit-constructor) + CONSTEVAL autowrap(T t) : value(t) {} + T value; +}; + +// NOLINTNEXTLINE(modernize-avoid-c-arrays) +template using str_lit_t = char const (&)[N]; + +template struct autowrap> { + // NOLINTNEXTLINE(google-explicit-constructor) + CONSTEVAL autowrap(str_lit_t str) : value(str) {} + stdx::ct_string value; +}; + +template autowrap(T) -> autowrap; +template autowrap(str_lit_t) -> autowrap>; + +template struct wrap { + constexpr static auto value = V; +}; + +template struct for_each_pair; +template struct for_each_pair> { + template + using type = + env...>, + 2 * Is>::value.value, + boost::mp11::mp_at_c...>, + (2 * Is) + 1>::value.value>...>; +}; + +template > +constexpr auto make_env = [] { + using new_env_t = typename for_each_pair< + std::make_index_sequence>::template type; + return boost::mp11::mp_append{}; +}; +} // namespace _env + +template +using extend_env_t = + decltype(_env::make_env.template operator()()); + +template <_env::autowrap... Args> +using make_env_t = extend_env_t, Args...>; +} // namespace v1 +} // namespace stdx + +#endif diff --git a/test/CMakeLists.txt b/test/CMakeLists.txt index 6cf74f2..1f73f6b 100644 --- a/test/CMakeLists.txt +++ b/test/CMakeLists.txt @@ -74,7 +74,14 @@ target_compile_definitions( PRIVATE -DATOMIC_CFG="${CMAKE_CURRENT_LIST_DIR}/detail/atomic_cfg.hpp") if(${CMAKE_CXX_STANDARD} GREATER_EQUAL 20) - add_tests(FILES ct_format ct_string indexed_tuple tuple tuple_algorithms) + add_tests( + FILES + ct_format + ct_string + env + indexed_tuple + tuple + tuple_algorithms) endif() add_subdirectory(fail) diff --git a/test/env.cpp b/test/env.cpp new file mode 100644 index 0000000..61700fa --- /dev/null +++ b/test/env.cpp @@ -0,0 +1,44 @@ +#include +#include + +#include + +namespace { +[[maybe_unused]] constexpr inline struct custom_t { + template + requires true // more constrained + CONSTEVAL auto operator()(T &&t) const + noexcept(noexcept(std::forward(t).query(std::declval()))) + -> decltype(std::forward(t).query(*this)) { + return std::forward(t).query(*this); + } + + CONSTEVAL auto operator()(auto &&) const { return 42; } +} custom; +} // namespace + +TEST_CASE("lookup query with default", "[env]") { + static_assert(custom(stdx::env<>{}) == 42); +} + +TEST_CASE("lookup with single-value prop", "[env]") { + using E = stdx::ct_prop; + static_assert(custom(E{}) == 17); +} + +TEST_CASE("make an environment", "[env]") { + using E = stdx::make_env_t; + static_assert(custom(E{}) == 17); +} + +TEST_CASE("extend an environment", "[env]") { + using E1 = stdx::make_env_t; + using E2 = stdx::extend_env_t; + static_assert(custom(E2{}) == 18); +} + +TEST_CASE("environment converts string literals to ct_string", "[env]") { + using namespace stdx::literals; + using E = stdx::make_env_t; + static_assert(custom(E{}) == "hello"_cts); +}