Skip to content

Commit ec95cd6

Browse files
authored
Merge pull request #200 from elbeno/add-env
✨ Add `env` to hold compile-time values
2 parents 0b3242b + 378b64e commit ec95cd6

File tree

4 files changed

+144
-1
lines changed

4 files changed

+144
-1
lines changed

CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -60,6 +60,7 @@ target_sources(
6060
include/stdx/cx_vector.hpp
6161
include/stdx/detail/bitset_common.hpp
6262
include/stdx/detail/list_common.hpp
63+
include/stdx/env.hpp
6364
include/stdx/for_each_n_args.hpp
6465
include/stdx/functional.hpp
6566
include/stdx/function_traits.hpp

include/stdx/env.hpp

Lines changed: 91 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,91 @@
1+
#pragma once
2+
3+
#if __cplusplus >= 202002L
4+
5+
#include <stdx/compiler.hpp>
6+
#include <stdx/ct_string.hpp>
7+
8+
#include <boost/mp11/algorithm.hpp>
9+
10+
namespace stdx {
11+
inline namespace v1 {
12+
template <auto Query, auto Value> struct ct_prop {
13+
[[nodiscard]] CONSTEVAL static auto query(decltype(Query)) noexcept {
14+
return Value;
15+
}
16+
};
17+
18+
namespace _env {
19+
template <typename Q, typename Env>
20+
concept valid_query_for = requires(Env const &e) { e.query(Q{}); };
21+
22+
template <typename Q, typename... Envs>
23+
concept valid_query_over = (... or valid_query_for<Q, Envs>);
24+
25+
template <typename Q> struct has_query {
26+
template <typename Env>
27+
using fn = std::bool_constant<valid_query_for<Q, Env>>;
28+
};
29+
} // namespace _env
30+
31+
template <typename... Envs> struct env {
32+
template <_env::valid_query_over<Envs...> Q>
33+
CONSTEVAL static auto query(Q) noexcept {
34+
using I = boost::mp11::mp_find_if_q<boost::mp11::mp_list<Envs...>,
35+
_env::has_query<Q>>;
36+
using E = boost::mp11::mp_at<boost::mp11::mp_list<Envs...>, I>;
37+
return Q{}(E{});
38+
}
39+
};
40+
41+
namespace _env {
42+
template <typename T> struct autowrap {
43+
// NOLINTNEXTLINE(google-explicit-constructor)
44+
CONSTEVAL autowrap(T t) : value(t) {}
45+
T value;
46+
};
47+
48+
// NOLINTNEXTLINE(modernize-avoid-c-arrays)
49+
template <std::size_t N> using str_lit_t = char const (&)[N];
50+
51+
template <std::size_t N> struct autowrap<str_lit_t<N>> {
52+
// NOLINTNEXTLINE(google-explicit-constructor)
53+
CONSTEVAL autowrap(str_lit_t<N> str) : value(str) {}
54+
stdx::ct_string<N> value;
55+
};
56+
57+
template <typename T> autowrap(T) -> autowrap<T>;
58+
template <std::size_t N> autowrap(str_lit_t<N>) -> autowrap<str_lit_t<N>>;
59+
60+
template <auto V> struct wrap {
61+
constexpr static auto value = V;
62+
};
63+
64+
template <typename> struct for_each_pair;
65+
template <std::size_t... Is> struct for_each_pair<std::index_sequence<Is...>> {
66+
template <auto... Args>
67+
using type =
68+
env<ct_prop<boost::mp11::mp_at_c<boost::mp11::mp_list<wrap<Args>...>,
69+
2 * Is>::value.value,
70+
boost::mp11::mp_at_c<boost::mp11::mp_list<wrap<Args>...>,
71+
(2 * Is) + 1>::value.value>...>;
72+
};
73+
74+
template <typename Env = env<>>
75+
constexpr auto make_env = []<autowrap... Args> {
76+
using new_env_t = typename for_each_pair<
77+
std::make_index_sequence<sizeof...(Args) / 2>>::template type<Args...>;
78+
return boost::mp11::mp_append<new_env_t, Env>{};
79+
};
80+
} // namespace _env
81+
82+
template <typename Env, _env::autowrap... Args>
83+
using extend_env_t =
84+
decltype(_env::make_env<Env>.template operator()<Args...>());
85+
86+
template <_env::autowrap... Args>
87+
using make_env_t = extend_env_t<env<>, Args...>;
88+
} // namespace v1
89+
} // namespace stdx
90+
91+
#endif

test/CMakeLists.txt

Lines changed: 8 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -74,7 +74,14 @@ target_compile_definitions(
7474
PRIVATE -DATOMIC_CFG="${CMAKE_CURRENT_LIST_DIR}/detail/atomic_cfg.hpp")
7575

7676
if(${CMAKE_CXX_STANDARD} GREATER_EQUAL 20)
77-
add_tests(FILES ct_format ct_string indexed_tuple tuple tuple_algorithms)
77+
add_tests(
78+
FILES
79+
ct_format
80+
ct_string
81+
env
82+
indexed_tuple
83+
tuple
84+
tuple_algorithms)
7885
endif()
7986

8087
add_subdirectory(fail)

test/env.cpp

Lines changed: 44 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,44 @@
1+
#include <stdx/ct_string.hpp>
2+
#include <stdx/env.hpp>
3+
4+
#include <catch2/catch_test_macros.hpp>
5+
6+
namespace {
7+
[[maybe_unused]] constexpr inline struct custom_t {
8+
template <typename T>
9+
requires true // more constrained
10+
CONSTEVAL auto operator()(T &&t) const
11+
noexcept(noexcept(std::forward<T>(t).query(std::declval<custom_t>())))
12+
-> decltype(std::forward<T>(t).query(*this)) {
13+
return std::forward<T>(t).query(*this);
14+
}
15+
16+
CONSTEVAL auto operator()(auto &&) const { return 42; }
17+
} custom;
18+
} // namespace
19+
20+
TEST_CASE("lookup query with default", "[env]") {
21+
static_assert(custom(stdx::env<>{}) == 42);
22+
}
23+
24+
TEST_CASE("lookup with single-value prop", "[env]") {
25+
using E = stdx::ct_prop<custom, 17>;
26+
static_assert(custom(E{}) == 17);
27+
}
28+
29+
TEST_CASE("make an environment", "[env]") {
30+
using E = stdx::make_env_t<custom, 17>;
31+
static_assert(custom(E{}) == 17);
32+
}
33+
34+
TEST_CASE("extend an environment", "[env]") {
35+
using E1 = stdx::make_env_t<custom, 17>;
36+
using E2 = stdx::extend_env_t<E1, custom, 18>;
37+
static_assert(custom(E2{}) == 18);
38+
}
39+
40+
TEST_CASE("environment converts string literals to ct_string", "[env]") {
41+
using namespace stdx::literals;
42+
using E = stdx::make_env_t<custom, "hello">;
43+
static_assert(custom(E{}) == "hello"_cts);
44+
}

0 commit comments

Comments
 (0)