Skip to content

Commit bfeeada

Browse files
committed
Restructure constexpr testing for C++20
C++20 compilers more restricted in their constexpr capabilities. Use fewer of the gtest macros, which are not consteval capable in C++20 with GCC.
1 parent cf05829 commit bfeeada

File tree

4 files changed

+93
-77
lines changed

4 files changed

+93
-77
lines changed

etc/clang-flags.cmake

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
include_guard(GLOBAL)
22

3-
set(CMAKE_CXX_STANDARD 23)
3+
set(CMAKE_CXX_STANDARD 20)
44

55
set(CMAKE_CXX_FLAGS
66
"-stdlib=libc++ -Wall -Wextra "
@@ -28,13 +28,13 @@ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO
2828
FORCE
2929
)
3030
set(CMAKE_CXX_FLAGS_TSAN
31-
"-O3 -g -DNDEBUG -fsanitize=thread"
31+
"-O3 -g -fsanitize=thread"
3232
CACHE STRING
3333
"C++ TSAN Flags"
3434
FORCE
3535
)
3636
set(CMAKE_CXX_FLAGS_ASAN
37-
"-O3 -g -DNDEBUG -fsanitize=address,undefined,leak"
37+
"-O3 -g -fsanitize=address,undefined,leak"
3838
CACHE STRING
3939
"C++ ASAN Flags"
4040
FORCE

etc/gcc-flags.cmake

Lines changed: 3 additions & 3 deletions
Original file line numberDiff line numberDiff line change
@@ -1,6 +1,6 @@
11
include_guard(GLOBAL)
22

3-
set(CMAKE_CXX_STANDARD 23)
3+
set(CMAKE_CXX_STANDARD 20)
44

55
set(CMAKE_CXX_FLAGS "-Wall -Wextra " CACHE STRING "CXX_FLAGS" FORCE)
66

@@ -23,13 +23,13 @@ set(CMAKE_CXX_FLAGS_RELWITHDEBINFO
2323
FORCE
2424
)
2525
set(CMAKE_CXX_FLAGS_TSAN
26-
"-O3 -g -DNDEBUG -fsanitize=thread"
26+
"-O3 -g -fsanitize=thread"
2727
CACHE STRING
2828
"C++ TSAN Flags"
2929
FORCE
3030
)
3131
set(CMAKE_CXX_FLAGS_ASAN
32-
"-O3 -g -DNDEBUG -fsanitize=address,undefined,leak"
32+
"-O3 -g -fsanitize=address,undefined,leak"
3333
CACHE STRING
3434
"C++ ASAN Flags"
3535
FORCE

etc/gcc-toolchain.cmake

Lines changed: 1 addition & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -4,8 +4,7 @@ set(CMAKE_C_COMPILER gcc)
44
set(CMAKE_CXX_COMPILER g++)
55

66
set(CMAKE_CXX_FLAGS
7-
"-std=c++20 \
8-
-Wall -Wextra "
7+
"-Wall -Wextra "
98
CACHE STRING
109
"CXX_FLAGS"
1110
FORCE

tests/beman/optional/optional_range_support.t.cpp

Lines changed: 86 additions & 69 deletions
Original file line numberDiff line numberDiff line change
@@ -14,28 +14,19 @@
1414

1515
#include <beman/optional/detail/iterator.hpp>
1616
#include <beman/optional/test_types.hpp>
17+
#include <beman/optional/test_utilities.hpp>
1718

1819
#include <algorithm>
19-
#include <concepts>
2020
#include <cstdlib>
2121
#if defined(__cpp_lib_format_ranges)
2222
#include <format>
2323
#endif
24-
#include <functional>
2524
#include <ranges>
2625
#include <tuple>
27-
#include <optional>
2826
#include <type_traits>
2927
#include <unordered_set>
3028
#include <vector>
3129

32-
#define CONSTEXPR_EXPECT_EQ(val1, val2) \
33-
if (::std::is_constant_evaluated()) { \
34-
if (!((val1) == (val2))) { \
35-
::std::abort(); \
36-
} \
37-
} else \
38-
EXPECT_EQ(val1, val2)
3930

4031
#define CONSTEXPR_EXPECT_TRUE(val) \
4132
if (::std::is_constant_evaluated()) { \
@@ -45,21 +36,20 @@
4536
} else \
4637
EXPECT_TRUE(val)
4738

48-
#define CONSTEXPR_ASSERT_TRUE(val) \
49-
if (::std::is_constant_evaluated()) { \
50-
if (!(val)) { \
51-
::std::abort(); \
52-
} \
53-
} else \
54-
ASSERT_TRUE(val)
39+
template <typename U, typename V>
40+
auto gtest_expect_eq(U&& val1, V&& val2) {
41+
EXPECT_EQ(std::forward<U>(val1), std::forward<V>(val2));
42+
}
5543

56-
#define CONSTEXPR_ASSERT_FALSE(val) \
57-
if (::std::is_constant_evaluated()) { \
58-
if (val) { \
59-
::std::abort(); \
60-
} \
61-
} else \
62-
ASSERT_FALSE(val)
44+
template <typename U, typename V>
45+
constexpr auto constexpr_expect_eq(U&& val1, V&& val2) {
46+
if (::std::is_constant_evaluated()) {
47+
if (!(val1 == val2))
48+
std::abort();
49+
} else {
50+
gtest_expect_eq(std::forward<U>(val1), std::forward<V>(val2));
51+
}
52+
}
6353

6454
using namespace beman::optional::tests;
6555

@@ -123,6 +113,7 @@ TEST(RangeSupportTest, IteratorConcepts) {
123113
test(beman::optional::optional<derived>{});
124114
}
125115

116+
126117
TEST(RangeSupportTest, BeginOnEmptyOptional) {
127118
auto lambda = [&] {
128119
const auto test = [](auto&& opt) {
@@ -132,10 +123,10 @@ TEST(RangeSupportTest, BeginOnEmptyOptional) {
132123
// e.g. const_iterator = optional<T>::const_iterator if opt is optional<T>
133124
using const_iterator = typename std::remove_reference_t<decltype(opt)>::const_iterator;
134125

135-
CONSTEXPR_EXPECT_EQ(opt.begin(), iterator());
126+
constexpr_expect_eq(iterator(), opt.begin());
136127

137128
const auto& const_opt = opt;
138-
CONSTEXPR_EXPECT_EQ(const_opt.begin(), const_iterator());
129+
constexpr_expect_eq(const_iterator(), const_opt.begin());
139130
};
140131

141132
test(beman::optional::optional<int>{});
@@ -144,9 +135,11 @@ TEST(RangeSupportTest, BeginOnEmptyOptional) {
144135
test(beman::optional::optional<no_default_ctor>{});
145136
test(beman::optional::optional<base>{});
146137
test(beman::optional::optional<derived>{});
138+
return true;
147139
};
148-
static_assert((lambda(), true));
149-
lambda();
140+
using beman::optional::tests::constify;
141+
EXPECT_TRUE(constify(lambda()));
142+
EXPECT_TRUE(lambda());
150143
}
151144

152145
TEST(RangeSupportTest, BeginOnNonEmptyOptional) {
@@ -158,10 +151,10 @@ TEST(RangeSupportTest, BeginOnNonEmptyOptional) {
158151
// e.g. const_iterator = optional<T>::const_iterator if opt is optional<T>
159152
using const_iterator = typename std::remove_reference_t<decltype(opt)>::const_iterator;
160153

161-
CONSTEXPR_EXPECT_EQ(opt.begin(), iterator(&*opt));
154+
constexpr_expect_eq(opt.begin(), iterator(&*opt));
162155

163156
const auto& const_opt = opt;
164-
CONSTEXPR_EXPECT_EQ(const_opt.begin(), const_iterator(&*opt));
157+
constexpr_expect_eq(const_opt.begin(), const_iterator(&*opt));
165158
};
166159

167160
test(beman::optional::optional<int>{26});
@@ -172,9 +165,10 @@ TEST(RangeSupportTest, BeginOnNonEmptyOptional) {
172165
test(beman::optional::optional<no_default_ctor>{no_default_ctor{empty{}}});
173166
test(beman::optional::optional<base>{base{}});
174167
test(beman::optional::optional<derived>{derived{}});
168+
return true;
175169
};
176-
static_assert((lambda(), true));
177-
lambda();
170+
EXPECT_TRUE(constify(lambda()));
171+
EXPECT_TRUE(lambda());
178172
}
179173

180174
TEST(RangeSupportTest, EndOnEmptyOptional) {
@@ -186,10 +180,10 @@ TEST(RangeSupportTest, EndOnEmptyOptional) {
186180
// e.g. const_iterator = optional<T>::const_iterator if opt is optional<T>
187181
using const_iterator = typename std::remove_reference_t<decltype(opt)>::const_iterator;
188182

189-
CONSTEXPR_EXPECT_EQ(opt.end(), iterator());
183+
constexpr_expect_eq(opt.end(), iterator());
190184

191185
const auto& const_opt = opt;
192-
CONSTEXPR_EXPECT_EQ(const_opt.end(), const_iterator());
186+
constexpr_expect_eq(const_opt.end(), const_iterator());
193187
};
194188

195189
test(beman::optional::optional<int>{});
@@ -198,9 +192,10 @@ TEST(RangeSupportTest, EndOnEmptyOptional) {
198192
test(beman::optional::optional<no_default_ctor>{});
199193
test(beman::optional::optional<base>{});
200194
test(beman::optional::optional<derived>{});
195+
return true;
201196
};
202-
static_assert((lambda(), true));
203-
lambda();
197+
EXPECT_TRUE(constify(lambda()));
198+
EXPECT_TRUE(lambda());
204199
}
205200

206201
TEST(RangeSupportTest, EndOnNonEmptyOptional) {
@@ -212,10 +207,10 @@ TEST(RangeSupportTest, EndOnNonEmptyOptional) {
212207
// e.g. const_iterator = optional<T>::const_iterator if opt is optional<T>
213208
using const_iterator = typename std::remove_reference_t<decltype(opt)>::const_iterator;
214209

215-
CONSTEXPR_EXPECT_EQ(opt.end(), iterator(&*opt + 1));
210+
constexpr_expect_eq(opt.end(), iterator(&*opt + 1));
216211

217212
const auto& const_opt = opt;
218-
CONSTEXPR_EXPECT_EQ(const_opt.end(), const_iterator(&*opt + 1));
213+
constexpr_expect_eq(const_opt.end(), const_iterator(&*opt + 1));
219214
};
220215

221216
test(beman::optional::optional<int>{26});
@@ -226,12 +221,14 @@ TEST(RangeSupportTest, EndOnNonEmptyOptional) {
226221
test(beman::optional::optional<no_default_ctor>{no_default_ctor{empty{}}});
227222
test(beman::optional::optional<base>{base{}});
228223
test(beman::optional::optional<derived>{derived{}});
224+
return true;
229225
};
230-
static_assert((lambda(), true));
231-
lambda();
226+
EXPECT_TRUE(constify(lambda()));
227+
EXPECT_TRUE(lambda());
232228
}
233229

234-
#if ((__GNUC__ >= 15) && (__GNUC_MINOR__ >= 1) && (__GNUC_PATCHLEVEL__ >= 1)) || ((__GNUC__ >= 16))
230+
#if (__cplusplus >= 202302L) && \
231+
(((__GNUC__ >= 15) && (__GNUC_MINOR__ >= 1) && (__GNUC_PATCHLEVEL__ >= 1)) || ((__GNUC__ >= 16)))
235232
static_assert(std::format_kind<beman::optional::optional<int>> == std::range_format::disabled);
236233
#endif
237234

@@ -258,70 +255,89 @@ TEST(RangeSupportTest, FormatOptionalIsStillDisabled) {
258255
#endif
259256
}
260257

258+
template <typename U>
259+
auto gtest_assert_true(U&& val1) {
260+
ASSERT_TRUE(std::forward<U>(val1));
261+
}
262+
263+
template <typename U>
264+
constexpr auto constexpr_assert(U&& val1) {
265+
if (::std::is_constant_evaluated()) {
266+
if (!(val1))
267+
std::abort();
268+
} else {
269+
gtest_assert_true(std::forward<U>(val1));
270+
}
271+
}
272+
261273
TEST(RangeSupportTest, LoopOverEmptyRange) {
262-
auto lambda = [&] {
274+
auto lambda = [&]() -> bool {
263275
beman::optional::optional<int> empty;
264-
CONSTEXPR_ASSERT_FALSE(empty.has_value());
276+
constexpr_assert(!empty.has_value());
265277

266278
for ([[maybe_unused]] auto _ : empty) {
267-
CONSTEXPR_ASSERT_TRUE(false) << "Should not be reached: expected not to loop over empty optional";
279+
constexpr_assert(false); // << "Should not be reached: expected not to loop over empty optional";
268280
}
281+
return true;
269282
};
270-
static_assert((lambda(), true));
271-
lambda();
283+
EXPECT_TRUE(constify(lambda()));
284+
EXPECT_TRUE(lambda());
272285
}
273286

274287
TEST(RangeSupportTest, LoopOverNonEmptyRange) {
275288
auto lambda = [&] {
276289
const int expected_value = 0xCAFEBABE;
277290
beman::optional::optional<int> empty{expected_value};
278-
CONSTEXPR_ASSERT_TRUE(empty.has_value());
291+
constexpr_assert(empty.has_value());
279292

280293
bool entered_loop = false;
281294
for (auto i : empty) {
282-
CONSTEXPR_EXPECT_EQ(i, expected_value);
295+
constexpr_expect_eq(i, expected_value);
283296
entered_loop = true;
284297
}
285-
CONSTEXPR_EXPECT_TRUE(entered_loop);
298+
constexpr_expect_eq(entered_loop, true);
299+
return true;
286300
};
287-
static_assert((lambda(), true));
288-
lambda();
301+
EXPECT_TRUE(constify(lambda()));
302+
EXPECT_TRUE(lambda());
289303
}
290304

291305
TEST(RangeSupportTest, LoopOptionalAccess) {
292306
auto lambda = [&] {
293307
// Example from P3168R2: should access the value from an optional object.
294308
const int expected_value = 0xCAFEBABE;
295309
const auto get_optional = [&]() -> beman::optional::optional<int> { return expected_value; };
296-
CONSTEXPR_ASSERT_TRUE(get_optional().has_value());
310+
constexpr_assert(get_optional().has_value());
297311

298312
for (auto&& opt : get_optional()) {
299-
CONSTEXPR_EXPECT_EQ(opt, expected_value); // usage of opt here is safe
313+
constexpr_expect_eq(opt, expected_value); // usage of opt here is safe
300314
}
315+
return true;
301316
};
302-
static_assert((lambda(), true));
303-
lambda();
317+
EXPECT_TRUE(constify(lambda()));
318+
EXPECT_TRUE(lambda());
304319
}
305320

306321
TEST(RangeSupportTest, LoopOptionalAssignment) {
307-
auto lambda = [&] {
322+
constexpr auto lambda = [] {
308323
// Example from P3168R2: should mutate the value from an optional object.
309-
const int initial_expected_value = 0xCAFEBABE;
310-
const int expected_value = 0xDEADBEEF;
311-
const auto get_optional = [&]() -> beman::optional::optional<int> { return initial_expected_value; };
312-
CONSTEXPR_ASSERT_TRUE(get_optional().has_value());
313-
CONSTEXPR_ASSERT_TRUE(get_optional().value() == initial_expected_value);
324+
constexpr int initial_expected_value = 0xCAFEBABE;
325+
constexpr int expected_value = 0xDEADBEEF;
326+
constexpr auto get_optional = [=]() -> beman::optional::optional<int> { return initial_expected_value; };
327+
constexpr_assert(get_optional().has_value());
328+
constexpr_assert(get_optional().value() == initial_expected_value);
314329

315330
auto opt_int = get_optional();
316331
for (auto&& opt : opt_int) {
317-
CONSTEXPR_EXPECT_EQ(opt, initial_expected_value);
332+
constexpr_expect_eq(opt, initial_expected_value);
318333
opt = expected_value; // usage of opt here is safe
319334
}
320-
CONSTEXPR_ASSERT_TRUE(opt_int.has_value());
321-
CONSTEXPR_EXPECT_EQ(opt_int.value(), expected_value);
335+
constexpr_assert(opt_int.has_value());
336+
constexpr_expect_eq(opt_int.value(), expected_value);
337+
return true;
322338
};
323-
static_assert((lambda(), true));
324-
lambda();
339+
EXPECT_TRUE(constify(lambda()));
340+
EXPECT_TRUE(lambda());
325341
}
326342

327343
TEST(RangeSupportTest, RangeChainExample) {
@@ -407,13 +423,14 @@ TEST(RangeSupportTest, PythagoreanTriples) {
407423
};
408424
constexpr const std::tuple k100th_triple = {
409425
26, 168, 170}; // The 100th Pythagorean triple with x, y, z <= 200.
410-
ASSERT_EQ(bruteforce_generate_nth(100, 200), k100th_triple);
426+
EXPECT_EQ(bruteforce_generate_nth(100, 200), k100th_triple);
411427

412428
// Generate the 100th Pythagorean triple with ranges.
413429
auto&& r = triples | std::views::drop(99) | std::views::take(1);
414430
EXPECT_TRUE(std::ranges::equal(r, std::vector{k100th_triple}));
415431
}
432+
return true;
416433
};
417-
static_assert((lambda(), true));
418-
lambda();
434+
EXPECT_TRUE(constify(lambda()));
435+
EXPECT_TRUE(lambda());
419436
}

0 commit comments

Comments
 (0)