Skip to content

Commit 87cd5ae

Browse files
committed
✨ Add CX_WRAP
Problem: - We want to not forget to apply `CX_VALUE` to arguments in macros when they are usable at compile time. Solution: - Add `CX_WRAP` which wrap its argument in a `CX_VALUE` when it's possible to do so.
1 parent 2447a48 commit 87cd5ae

File tree

4 files changed

+100
-10
lines changed

4 files changed

+100
-10
lines changed

include/stdx/ct_format.hpp

Lines changed: 4 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -8,6 +8,7 @@
88
#include <stdx/ct_string.hpp>
99
#include <stdx/tuple.hpp>
1010
#include <stdx/tuple_algorithms.hpp>
11+
#include <stdx/type_traits.hpp>
1112
#include <stdx/utility.hpp>
1213

1314
#include <fmt/compile.h>
@@ -107,8 +108,8 @@ template <std::size_t N> CONSTEVAL auto split_specifiers(std::string_view fmt) {
107108
}
108109

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

113114
template <typename T, T V>
114115
CONSTEVAL auto arg_value(std::integral_constant<T, V>) {
@@ -125,7 +126,7 @@ template <typename T> CONSTEVAL auto arg_value(type_identity<T>) {
125126

126127
template <ct_string S> CONSTEVAL auto arg_value(cts_t<S>) { return S; }
127128

128-
CONSTEVAL auto arg_value(cx_value auto a) {
129+
CONSTEVAL auto arg_value(fmt_cx_value auto a) {
129130
if constexpr (requires { ct_string_from_type(a); }) {
130131
return ct_string_from_type(a);
131132
} else if constexpr (std::is_enum_v<decltype(a())>) {

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: 36 additions & 6 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 &&) -> 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,6 +206,9 @@ 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() {
@@ -227,21 +234,21 @@ template <typename T> constexpr auto is_ct_v<T const> = is_ct_v<T>;
227234

228235
#ifndef CX_VALUE
229236
#define CX_VALUE(...) \
230-
[]() constexpr { \
237+
[&]() constexpr { \
231238
STDX_PRAGMA(diagnostic push) \
232239
STDX_PRAGMA(diagnostic ignored "-Wold-style-cast") \
233240
STDX_PRAGMA(diagnostic ignored "-Wunused-value") \
234241
if constexpr (decltype(stdx::cxv_detail::is_type< \
235242
stdx::cxv_detail::from_any( \
236243
__VA_ARGS__)>())::value) { \
237-
return stdx::overload{stdx::cxv_detail::cx_base{}, [] { \
244+
return stdx::overload{stdx::cxv_detail::cx_base{}, [&] { \
238245
return stdx::type_identity< \
239246
decltype(stdx::cxv_detail::type_of< \
240247
stdx::cxv_detail::from_any( \
241248
__VA_ARGS__)>())>{}; \
242249
}}; \
243250
} else { \
244-
return stdx::overload{stdx::cxv_detail::cx_base{}, [] { \
251+
return stdx::overload{stdx::cxv_detail::cx_base{}, [&] { \
245252
return (__VA_ARGS__) + \
246253
stdx::cxv_detail::type_val{}; \
247254
}}; \
@@ -265,6 +272,29 @@ template <typename T> constexpr auto is_ct_v<T const> = is_ct_v<T>;
265272
} \
266273
}([&] { return X; })
267274

275+
#define CX_WRAP(X) \
276+
[&]<typename F>(F) { \
277+
STDX_PRAGMA(diagnostic push) \
278+
STDX_PRAGMA(diagnostic ignored "-Wold-style-cast") \
279+
if constexpr (::stdx::is_cx_value_v<std::invoke_result_t<F>>) { \
280+
return (X) + ::stdx::cxv_detail::type_val{}; \
281+
} else if constexpr (requires { \
282+
::stdx::detail::cx_detect0< \
283+
::stdx::detail::cx_detect1( \
284+
CX_VALUE(X)())>; \
285+
}) { \
286+
return CX_VALUE(X); \
287+
} else { \
288+
return (X) + ::stdx::cxv_detail::type_val{}; \
289+
} \
290+
STDX_PRAGMA(diagnostic pop) \
291+
}([&] { \
292+
STDX_PRAGMA(diagnostic push) \
293+
STDX_PRAGMA(diagnostic ignored "-Wold-style-cast") \
294+
return (X) + stdx::cxv_detail::type_val{}; \
295+
STDX_PRAGMA(diagnostic pop) \
296+
})
297+
268298
#endif
269299

270300
// NOLINTEND(cppcoreguidelines-macro-usage)

test/utility.cpp

Lines changed: 59 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -305,4 +305,63 @@ TEST_CASE("CT_WRAP", "[utility]") {
305305
}.template operator()<17>();
306306
}
307307

308+
TEST_CASE("CX_WRAP integer runtime arg", "[utility]") {
309+
auto x = 17;
310+
STATIC_REQUIRE(std::is_same_v<decltype(CX_WRAP(x)), int>);
311+
CHECK(CX_WRAP(x) == 17);
312+
}
313+
314+
TEST_CASE("CX_WRAP string_view runtime arg", "[utility]") {
315+
auto x = std::string_view{"hello"};
316+
STATIC_REQUIRE(std::is_same_v<decltype(CX_WRAP(x)), std::string_view>);
317+
CHECK(CX_WRAP(x) == std::string_view{"hello"});
318+
}
319+
320+
TEST_CASE("CX_WRAP const integral type", "[utility]") {
321+
auto const x = 17;
322+
STATIC_REQUIRE(stdx::is_cx_value_v<decltype(CX_WRAP(x))>);
323+
STATIC_REQUIRE(CX_WRAP(x)() == 17);
324+
}
325+
326+
TEST_CASE("CX_WRAP constexpr integral type", "[utility]") {
327+
constexpr auto x = 17;
328+
STATIC_REQUIRE(stdx::is_cx_value_v<decltype(CX_WRAP(x))>);
329+
STATIC_REQUIRE(CX_WRAP(x)() == 17);
330+
}
331+
332+
TEST_CASE("CX_WRAP constexpr non-structural type", "[utility]") {
333+
constexpr static auto x = std::string_view{"hello"};
334+
STATIC_REQUIRE(stdx::is_cx_value_v<decltype(CX_WRAP(x))>);
335+
STATIC_REQUIRE(CX_WRAP(x)() == std::string_view{"hello"});
336+
}
337+
338+
TEST_CASE("CX_WRAP integer literal", "[utility]") {
339+
STATIC_REQUIRE(stdx::is_cx_value_v<decltype(CX_WRAP(17))>);
340+
STATIC_REQUIRE(CX_WRAP(17)() == 17);
341+
}
342+
343+
TEST_CASE("CX_WRAP string literal", "[utility]") {
344+
STATIC_REQUIRE(stdx::is_cx_value_v<decltype(CX_WRAP("hello"))>);
345+
STATIC_REQUIRE(CX_WRAP("hello")() == std::string_view{"hello"});
346+
}
347+
348+
TEST_CASE("CX_WRAP existing CX_VALUE", "[utility]") {
349+
auto x = CX_VALUE(17);
350+
STATIC_REQUIRE(stdx::is_cx_value_v<decltype(CX_WRAP(x))>);
351+
STATIC_REQUIRE(CX_WRAP(x)() == 17);
352+
}
353+
354+
TEST_CASE("CX_WRAP template argument", "[utility]") {
355+
[]<int x> {
356+
STATIC_REQUIRE(stdx::is_cx_value_v<decltype(CX_WRAP(x))>);
357+
STATIC_REQUIRE(CX_WRAP(x)() == 17);
358+
}.template operator()<17>();
359+
}
360+
361+
TEST_CASE("CX_WRAP type argument", "[utility]") {
362+
STATIC_REQUIRE(stdx::is_cx_value_v<decltype(CX_WRAP(int))>);
363+
STATIC_REQUIRE(
364+
std::is_same_v<decltype(CX_WRAP(int)()), stdx::type_identity<int>>);
365+
}
366+
308367
#endif

0 commit comments

Comments
 (0)