Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 1 addition & 0 deletions CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand Down
91 changes: 91 additions & 0 deletions include/stdx/env.hpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,91 @@
#pragma once

#if __cplusplus >= 202002L

#include <stdx/compiler.hpp>
#include <stdx/ct_string.hpp>

#include <boost/mp11/algorithm.hpp>

namespace stdx {
inline namespace v1 {
template <auto Query, auto Value> struct ct_prop {
[[nodiscard]] CONSTEVAL static auto query(decltype(Query)) noexcept {
return Value;
}
};

namespace _env {
template <typename Q, typename Env>
concept valid_query_for = requires(Env const &e) { e.query(Q{}); };

template <typename Q, typename... Envs>
concept valid_query_over = (... or valid_query_for<Q, Envs>);

template <typename Q> struct has_query {
template <typename Env>
using fn = std::bool_constant<valid_query_for<Q, Env>>;
};
} // namespace _env

template <typename... Envs> struct env {
template <_env::valid_query_over<Envs...> Q>
CONSTEVAL static auto query(Q) noexcept {
using I = boost::mp11::mp_find_if_q<boost::mp11::mp_list<Envs...>,
_env::has_query<Q>>;
using E = boost::mp11::mp_at<boost::mp11::mp_list<Envs...>, I>;
return Q{}(E{});
}
};

namespace _env {
template <typename T> struct autowrap {
// NOLINTNEXTLINE(google-explicit-constructor)
CONSTEVAL autowrap(T t) : value(t) {}
T value;
};

// NOLINTNEXTLINE(modernize-avoid-c-arrays)
template <std::size_t N> using str_lit_t = char const (&)[N];

template <std::size_t N> struct autowrap<str_lit_t<N>> {
// NOLINTNEXTLINE(google-explicit-constructor)
CONSTEVAL autowrap(str_lit_t<N> str) : value(str) {}
stdx::ct_string<N> value;
};

template <typename T> autowrap(T) -> autowrap<T>;
template <std::size_t N> autowrap(str_lit_t<N>) -> autowrap<str_lit_t<N>>;

template <auto V> struct wrap {
constexpr static auto value = V;
};

template <typename> struct for_each_pair;
template <std::size_t... Is> struct for_each_pair<std::index_sequence<Is...>> {
template <auto... Args>
using type =
env<ct_prop<boost::mp11::mp_at_c<boost::mp11::mp_list<wrap<Args>...>,
2 * Is>::value.value,
boost::mp11::mp_at_c<boost::mp11::mp_list<wrap<Args>...>,
(2 * Is) + 1>::value.value>...>;
};

template <typename Env = env<>>
constexpr auto make_env = []<autowrap... Args> {
using new_env_t = typename for_each_pair<
std::make_index_sequence<sizeof...(Args) / 2>>::template type<Args...>;
return boost::mp11::mp_append<new_env_t, Env>{};
};
} // namespace _env

template <typename Env, _env::autowrap... Args>
using extend_env_t =
decltype(_env::make_env<Env>.template operator()<Args...>());

template <_env::autowrap... Args>
using make_env_t = extend_env_t<env<>, Args...>;
} // namespace v1
} // namespace stdx

#endif
9 changes: 8 additions & 1 deletion test/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -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)
Expand Down
44 changes: 44 additions & 0 deletions test/env.cpp
Original file line number Diff line number Diff line change
@@ -0,0 +1,44 @@
#include <stdx/ct_string.hpp>
#include <stdx/env.hpp>

#include <catch2/catch_test_macros.hpp>

namespace {
[[maybe_unused]] constexpr inline struct custom_t {
template <typename T>
requires true // more constrained
CONSTEVAL auto operator()(T &&t) const
noexcept(noexcept(std::forward<T>(t).query(std::declval<custom_t>())))
-> decltype(std::forward<T>(t).query(*this)) {
return std::forward<T>(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<custom, 17>;
static_assert(custom(E{}) == 17);
}

TEST_CASE("make an environment", "[env]") {
using E = stdx::make_env_t<custom, 17>;
static_assert(custom(E{}) == 17);
}

TEST_CASE("extend an environment", "[env]") {
using E1 = stdx::make_env_t<custom, 17>;
using E2 = stdx::extend_env_t<E1, custom, 18>;
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<custom, "hello">;
static_assert(custom(E{}) == "hello"_cts);
}
Loading