Skip to content

Commit 3641035

Browse files
authored
Merge pull request #243 from elbeno/cx-utils
✨ Add `CT_WRAP`
2 parents 5f65449 + 5657978 commit 3641035

File tree

11 files changed

+350
-24
lines changed

11 files changed

+350
-24
lines changed

CMakeLists.txt

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -62,8 +62,8 @@ target_sources(
6262
include/stdx/detail/list_common.hpp
6363
include/stdx/env.hpp
6464
include/stdx/for_each_n_args.hpp
65-
include/stdx/functional.hpp
6665
include/stdx/function_traits.hpp
66+
include/stdx/functional.hpp
6767
include/stdx/intrusive_forward_list.hpp
6868
include/stdx/intrusive_list.hpp
6969
include/stdx/iterator.hpp
@@ -72,14 +72,15 @@ target_sources(
7272
include/stdx/numeric.hpp
7373
include/stdx/optional.hpp
7474
include/stdx/panic.hpp
75+
include/stdx/pp_map.hpp
7576
include/stdx/priority.hpp
7677
include/stdx/ranges.hpp
7778
include/stdx/rollover.hpp
7879
include/stdx/span.hpp
7980
include/stdx/static_assert.hpp
81+
include/stdx/tuple.hpp
8082
include/stdx/tuple_algorithms.hpp
8183
include/stdx/tuple_destructure.hpp
82-
include/stdx/tuple.hpp
8384
include/stdx/type_traits.hpp
8485
include/stdx/udls.hpp
8586
include/stdx/utility.hpp)

include/stdx/ct_format.hpp

Lines changed: 12 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -6,8 +6,10 @@
66
#include <stdx/concepts.hpp>
77
#include <stdx/ct_conversions.hpp>
88
#include <stdx/ct_string.hpp>
9+
#include <stdx/pp_map.hpp>
910
#include <stdx/tuple.hpp>
1011
#include <stdx/tuple_algorithms.hpp>
12+
#include <stdx/type_traits.hpp>
1113
#include <stdx/utility.hpp>
1214

1315
#include <fmt/compile.h>
@@ -107,8 +109,8 @@ template <std::size_t N> CONSTEVAL auto split_specifiers(std::string_view fmt) {
107109
}
108110

109111
template <typename T>
110-
concept cx_value = requires { typename T::cx_value_t; } or
111-
requires(T t) { ct_string_from_type(t); };
112+
concept fmt_cx_value =
113+
is_cx_value_v<T> or requires(T t) { ct_string_from_type(t); };
112114

113115
template <typename T, T V>
114116
CONSTEVAL auto arg_value(std::integral_constant<T, V>) {
@@ -125,7 +127,7 @@ template <typename T> CONSTEVAL auto arg_value(type_identity<T>) {
125127

126128
template <ct_string S> CONSTEVAL auto arg_value(cts_t<S>) { return S; }
127129

128-
CONSTEVAL auto arg_value(cx_value auto a) {
130+
CONSTEVAL auto arg_value(fmt_cx_value auto a) {
129131
if constexpr (requires { ct_string_from_type(a); }) {
130132
return ct_string_from_type(a);
131133
} else if constexpr (std::is_enum_v<decltype(a())>) {
@@ -258,4 +260,11 @@ constexpr auto num_fmt_specifiers =
258260
} // namespace v1
259261
} // namespace stdx
260262

263+
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
264+
265+
#define STDX_CT_FORMAT(S, ...) \
266+
stdx::ct_format<S>(STDX_MAP(CX_WRAP __VA_OPT__(, ) __VA_ARGS__))
267+
268+
// NOLINTEND(cppcoreguidelines-macro-usage)
269+
261270
#endif

include/stdx/ct_string.hpp

Lines changed: 2 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -173,6 +173,8 @@ template <std::size_t N> struct ct_helper<ct_string<N>>;
173173

174174
template <ct_string Value> CONSTEVAL auto ct() { return cts_t<Value>{}; }
175175

176+
template <ct_string Value> constexpr auto is_ct_v<cts_t<Value>> = true;
177+
176178
inline namespace literals {
177179
inline namespace ct_string_literals {
178180
template <ct_string S> CONSTEVAL_UDL auto operator""_cts() { return S; }

include/stdx/pp_map.hpp

Lines changed: 39 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,39 @@
1+
#pragma once
2+
3+
// NOLINTBEGIN(cppcoreguidelines-macro-usage)
4+
5+
#define STDX_EVAL0(...) __VA_ARGS__
6+
#define STDX_EVAL1(...) STDX_EVAL0(STDX_EVAL0(STDX_EVAL0(__VA_ARGS__)))
7+
#define STDX_EVAL2(...) STDX_EVAL1(STDX_EVAL1(STDX_EVAL1(__VA_ARGS__)))
8+
#define STDX_EVAL3(...) STDX_EVAL2(STDX_EVAL2(STDX_EVAL2(__VA_ARGS__)))
9+
#define STDX_EVAL4(...) STDX_EVAL3(STDX_EVAL3(STDX_EVAL3(__VA_ARGS__)))
10+
#define STDX_EVAL5(...) STDX_EVAL4(STDX_EVAL4(STDX_EVAL4(__VA_ARGS__)))
11+
#define STDX_EVAL(...) STDX_EVAL5(__VA_ARGS__)
12+
13+
#define STDX_MAP_END(...)
14+
#define STDX_MAP_OUT
15+
16+
#define STDX_EMPTY()
17+
#define STDX_DEFER(id) id STDX_EMPTY()
18+
19+
#define STDX_MAP_GET_END2() 0, STDX_MAP_END
20+
#define STDX_MAP_GET_END1(...) STDX_MAP_GET_END2
21+
#define STDX_MAP_GET_END(...) STDX_MAP_GET_END1
22+
#define STDX_MAP_NEXT0(test, next, ...) next STDX_MAP_OUT
23+
#define STDX_MAP_NEXT1(test, next) STDX_DEFER(STDX_MAP_NEXT0)(test, next, 0)
24+
#define STDX_MAP_NEXT(test, next) STDX_MAP_NEXT1(STDX_MAP_GET_END test, next)
25+
#define STDX_MAP_INC(X) STDX_MAP_INC_##X
26+
27+
#define STDX_MAP_A(f, x, peek, ...) \
28+
, f(x) STDX_DEFER(STDX_MAP_NEXT(peek, STDX_MAP_B))(f, peek, __VA_ARGS__)
29+
#define STDX_MAP_B(f, x, peek, ...) \
30+
, f(x) STDX_DEFER(STDX_MAP_NEXT(peek, STDX_MAP_A))(f, peek, __VA_ARGS__)
31+
32+
#define STDX_DROP0(X, ...) __VA_ARGS__
33+
#define STDX_DROP1(...) STDX_DROP0(__VA_ARGS__)
34+
35+
#define STDX_MAP(f, ...) \
36+
__VA_OPT__(STDX_DROP1( \
37+
0 STDX_EVAL(STDX_MAP_A(f, __VA_ARGS__, ()()(), ()()(), ()()(), 0))))
38+
39+
// NOLINTEND(cppcoreguidelines-macro-usage)

include/stdx/static_assert.hpp

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,7 +26,7 @@ template <bool B> constexpr auto ct_check = ct_check_t<B>{};
2626
namespace detail {
2727
template <ct_string Fmt, auto... Args> constexpr auto static_format() {
2828
constexpr auto make_ct = []<auto V>() {
29-
if constexpr (cx_value<decltype(V)>) {
29+
if constexpr (fmt_cx_value<decltype(V)>) {
3030
return V;
3131
} else {
3232
return CX_VALUE(V);

include/stdx/utility.hpp

Lines changed: 93 additions & 16 deletions
Original file line numberDiff line numberDiff line change
@@ -148,14 +148,18 @@ struct from_any {
148148
struct type_val {
149149
template <typename T, typename U,
150150
typename = std::enable_if_t<same_as_unqualified<type_val, U>>>
151-
friend constexpr auto operator+(T &&t, U &&) -> T {
151+
friend constexpr auto operator+(T t, U const &) -> T {
152152
return t;
153153
}
154154
friend constexpr auto operator+(type_val const &f) -> type_val { return f; }
155155
// NOLINTNEXTLINE(google-explicit-constructor)
156156
template <typename T> constexpr operator T() const {
157-
extern auto cxv_type_val_get_t(T *) -> T;
158-
return cxv_type_val_get_t(nullptr);
157+
if constexpr (std::is_default_constructible_v<T>) {
158+
return T{};
159+
} else {
160+
extern auto cxv_type_val_get_t(T *) -> T;
161+
return cxv_type_val_get_t(nullptr);
162+
}
159163
}
160164
};
161165

@@ -202,13 +206,22 @@ template <typename T> struct ct_helper {
202206
T value;
203207
};
204208
template <typename T> ct_helper(T) -> ct_helper<T>;
209+
210+
template <auto> CONSTEVAL auto cx_detect0() {}
211+
CONSTEVAL auto cx_detect1(auto) { return 0; }
205212
} // namespace detail
206213

207214
template <detail::ct_helper Value> CONSTEVAL auto ct() {
208215
return std::integral_constant<decltype(Value.value), Value.value>{};
209216
}
210217
template <typename T> CONSTEVAL auto ct() { return type_identity<T>{}; }
211218

219+
template <typename> constexpr auto is_ct_v = false;
220+
template <typename T, T V>
221+
constexpr auto is_ct_v<std::integral_constant<T, V>> = true;
222+
template <typename T> constexpr auto is_ct_v<type_identity<T>> = true;
223+
template <typename T> constexpr auto is_ct_v<T const> = is_ct_v<T>;
224+
212225
#endif
213226
} // namespace v1
214227
} // namespace stdx
@@ -221,28 +234,92 @@ template <typename T> CONSTEVAL auto ct() { return type_identity<T>{}; }
221234

222235
#ifndef CX_VALUE
223236
#define CX_VALUE(...) \
224-
[]() constexpr { \
237+
[&]() constexpr { \
225238
STDX_PRAGMA(diagnostic push) \
226239
STDX_PRAGMA(diagnostic ignored "-Wold-style-cast") \
227240
STDX_PRAGMA(diagnostic ignored "-Wunused-value") \
228-
if constexpr (decltype(stdx::cxv_detail::is_type< \
229-
stdx::cxv_detail::from_any( \
241+
if constexpr (decltype(::stdx::cxv_detail::is_type< \
242+
::stdx::cxv_detail::from_any( \
230243
__VA_ARGS__)>())::value) { \
231-
return stdx::overload{stdx::cxv_detail::cx_base{}, [] { \
232-
return stdx::type_identity< \
233-
decltype(stdx::cxv_detail::type_of< \
234-
stdx::cxv_detail::from_any( \
235-
__VA_ARGS__)>())>{}; \
236-
}}; \
244+
return ::stdx::overload{ \
245+
::stdx::cxv_detail::cx_base{}, [&] { \
246+
return ::stdx::type_identity< \
247+
decltype(::stdx::cxv_detail::type_of< \
248+
::stdx::cxv_detail::from_any( \
249+
__VA_ARGS__)>())>{}; \
250+
}}; \
237251
} else { \
238-
return stdx::overload{stdx::cxv_detail::cx_base{}, [] { \
239-
return (__VA_ARGS__) + \
240-
stdx::cxv_detail::type_val{}; \
241-
}}; \
252+
return ::stdx::overload{::stdx::cxv_detail::cx_base{}, [&] { \
253+
return (__VA_ARGS__) + \
254+
::stdx::cxv_detail::type_val{}; \
255+
}}; \
242256
} \
243257
STDX_PRAGMA(diagnostic pop) \
244258
}()
245259
#endif
246260

261+
#if __cplusplus >= 202002L
262+
263+
#define CT_WRAP(X) \
264+
[&](auto f) { \
265+
if constexpr (::stdx::is_ct_v<decltype(f())>) { \
266+
return f(); \
267+
} else if constexpr (requires { \
268+
::stdx::ct<[&]() constexpr { return X; }()>; \
269+
}) { \
270+
return ::stdx::ct<[&]() constexpr { return X; }()>(); \
271+
} else { \
272+
return f(); \
273+
} \
274+
}([&] { return X; })
275+
276+
#ifdef __clang__
277+
#define CX_DETECT(X) std::is_empty_v<decltype(CX_VALUE(X))>
278+
#else
279+
namespace stdx {
280+
inline namespace v1 {
281+
template <auto> constexpr auto cx_detect0() {}
282+
constexpr auto cx_detect1(auto) { return 0; }
283+
} // namespace v1
284+
} // namespace stdx
285+
#define CX_DETECT(X) \
286+
requires { \
287+
::stdx::cx_detect0<::stdx::cx_detect1( \
288+
(X) + ::stdx::cxv_detail::type_val{})>; \
289+
}
290+
#endif
291+
292+
#define CX_WRAP(X) \
293+
[&]<typename F>(F f) { \
294+
STDX_PRAGMA(diagnostic push) \
295+
STDX_PRAGMA(diagnostic ignored "-Wold-style-cast") \
296+
if constexpr (::stdx::is_cx_value_v<std::invoke_result_t<F>>) { \
297+
return f(); \
298+
} else if constexpr (CX_DETECT(X)) { \
299+
if constexpr (decltype(::stdx::cxv_detail::is_type< \
300+
::stdx::cxv_detail::from_any( \
301+
X)>())::value) { \
302+
return ::stdx::overload{ \
303+
::stdx::cxv_detail::cx_base{}, [&] { \
304+
return ::stdx::type_identity< \
305+
decltype(::stdx::cxv_detail::type_of< \
306+
::stdx::cxv_detail::from_any(X)>())>{}; \
307+
}}; \
308+
} else { \
309+
return ::stdx::overload{::stdx::cxv_detail::cx_base{}, f}; \
310+
} \
311+
} else { \
312+
return f(); \
313+
} \
314+
STDX_PRAGMA(diagnostic pop) \
315+
}([&] { \
316+
STDX_PRAGMA(diagnostic push) \
317+
STDX_PRAGMA(diagnostic ignored "-Wold-style-cast") \
318+
return (X) + ::stdx::cxv_detail::type_val{}; \
319+
STDX_PRAGMA(diagnostic pop) \
320+
})
321+
322+
#endif
323+
247324
// NOLINTEND(cppcoreguidelines-macro-usage)
248325
// NOLINTEND(modernize-use-constraints)

test/CMakeLists.txt

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -54,6 +54,7 @@ add_tests(
5454
optional
5555
overload
5656
panic
57+
pp_map
5758
priority
5859
ranges
5960
remove_cvref

test/ct_format.cpp

Lines changed: 48 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -242,6 +242,52 @@ constexpr auto ct_format_as(S const &s) {
242242
} // namespace user
243243

244244
TEST_CASE("user-defined formatting", "[ct_format]") {
245-
auto r = stdx::ct_format<"Hello {}">(user::S{42});
246-
CHECK(r == stdx::format_result{"Hello S: {}"_ctst, stdx::make_tuple(42)});
245+
auto r = stdx::ct_format<"Hello {}">(user::S{17});
246+
CHECK(r == stdx::format_result{"Hello S: {}"_ctst, stdx::make_tuple(17)});
247+
}
248+
249+
TEST_CASE("FORMAT with no arguments", "[ct_format]") {
250+
STATIC_REQUIRE(STDX_CT_FORMAT("Hello") == "Hello"_fmt_res);
251+
}
252+
253+
TEST_CASE("FORMAT a compile-time string argument", "[ct_format]") {
254+
STATIC_REQUIRE(STDX_CT_FORMAT("Hello {}", "world") ==
255+
"Hello world"_fmt_res);
256+
}
257+
258+
TEST_CASE("FORMAT a compile-time int argument", "[ct_format]") {
259+
STATIC_REQUIRE(STDX_CT_FORMAT("Hello {}", 17) == "Hello 17"_fmt_res);
260+
}
261+
262+
TEST_CASE("FORMAT a type argument", "[ct_format]") {
263+
STATIC_REQUIRE(STDX_CT_FORMAT("Hello {}", int) == "Hello int"_fmt_res);
264+
}
265+
266+
TEST_CASE("FORMAT a constexpr string argument", "[ct_format]") {
267+
constexpr static auto S = "world"_cts;
268+
STATIC_REQUIRE(STDX_CT_FORMAT("Hello {}", S) == "Hello world"_fmt_res);
269+
}
270+
271+
TEST_CASE("FORMAT a constexpr int argument", "[ct_format]") {
272+
constexpr static auto I = 17;
273+
STATIC_REQUIRE(STDX_CT_FORMAT("Hello {}", I) == "Hello 17"_fmt_res);
274+
}
275+
276+
#ifdef __clang__
277+
TEST_CASE("FORMAT a constexpr nonstatic string_view argument", "[ct_format]") {
278+
constexpr auto S = std::string_view{"world"};
279+
constexpr auto expected =
280+
stdx::format_result{"Hello {}"_ctst, stdx::make_tuple(S)};
281+
STATIC_REQUIRE(STDX_CT_FORMAT("Hello {}", S) == expected);
282+
}
283+
#endif
284+
285+
TEST_CASE("FORMAT a constexpr string_view argument", "[ct_format]") {
286+
constexpr static auto S = std::string_view{"world"};
287+
STATIC_REQUIRE(STDX_CT_FORMAT("Hello {}", S) == "Hello world"_fmt_res);
288+
}
289+
290+
TEST_CASE("FORMAT an integral_constant argument", "[ct_format]") {
291+
constexpr static auto I = std::integral_constant<int, 17>{};
292+
STATIC_REQUIRE(STDX_CT_FORMAT("Hello {}", I) == "Hello 17"_fmt_res);
247293
}

test/ct_string.cpp

Lines changed: 27 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -165,3 +165,30 @@ TEST_CASE("operator+ works to concat cts_t and ct_string", "[ct_string]") {
165165
STATIC_REQUIRE("Hello"_ctst + " world"_cts == "Hello world"_cts);
166166
STATIC_REQUIRE("Hello"_cts + " world"_ctst == "Hello world"_cts);
167167
}
168+
169+
TEST_CASE("is_ct (ct_string)", "[ct_string]") {
170+
using namespace stdx::ct_string_literals;
171+
constexpr auto v1 = stdx::ct<"Hello">();
172+
STATIC_REQUIRE(stdx::is_ct_v<decltype(v1)>);
173+
}
174+
175+
TEST_CASE("CT_WRAP", "[ct_string]") {
176+
using namespace stdx::ct_string_literals;
177+
auto x1 = "hello"_cts;
178+
STATIC_REQUIRE(std::is_same_v<decltype(CT_WRAP(x1)), stdx::ct_string<6>>);
179+
CHECK(CT_WRAP(x1) == "hello"_cts);
180+
181+
auto x2 = "hello"_ctst;
182+
STATIC_REQUIRE(std::is_same_v<decltype(CT_WRAP(x2)), stdx::cts_t<"hello">>);
183+
STATIC_REQUIRE(CT_WRAP(x2) == "hello"_ctst);
184+
185+
constexpr static auto x3 = "hello"_cts;
186+
STATIC_REQUIRE(std::is_same_v<decltype(CT_WRAP(x3)), stdx::cts_t<"hello">>);
187+
STATIC_REQUIRE(CT_WRAP(x3) == "hello"_ctst);
188+
189+
[]<stdx::ct_string X>() {
190+
STATIC_REQUIRE(
191+
std::is_same_v<decltype(CT_WRAP(X)), stdx::cts_t<"hello">>);
192+
STATIC_REQUIRE(CT_WRAP(X) == "hello"_ctst);
193+
}.template operator()<"hello">();
194+
}

0 commit comments

Comments
 (0)