Skip to content

Commit e0db02c

Browse files
authored
Merge pull request #130 from steve-downey/constexpr-20
Fix the constexpr tests to operate under C++20 and move the standard back down to 20 in the toolchain files. TODO: Test with a current compiler with 23 and 26 because untested code should be assumed to be broken.
2 parents 472b83a + 247e43e commit e0db02c

File tree

4 files changed

+92
-83
lines changed

4 files changed

+92
-83
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 & 7 deletions
Original file line numberDiff line numberDiff line change
@@ -3,13 +3,7 @@ include_guard(GLOBAL)
33
set(CMAKE_C_COMPILER gcc)
44
set(CMAKE_CXX_COMPILER g++)
55

6-
set(CMAKE_CXX_FLAGS
7-
"-std=c++20 \
8-
-Wall -Wextra "
9-
CACHE STRING
10-
"CXX_FLAGS"
11-
FORCE
12-
)
6+
set(CMAKE_CXX_FLAGS "-Wall -Wextra " CACHE STRING "CXX_FLAGS" FORCE)
137

148
set(CMAKE_CXX_FLAGS_DEBUG
159
"-O0 -fno-inline -g3"

tests/beman/optional/optional_range_support.t.cpp

Lines changed: 85 additions & 70 deletions
Original file line numberDiff line numberDiff line change
@@ -14,29 +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)
39-
4030
#define CONSTEXPR_EXPECT_TRUE(val) \
4131
if (::std::is_constant_evaluated()) { \
4232
if (!(val)) { \
@@ -45,21 +35,20 @@
4535
} else \
4636
EXPECT_TRUE(val)
4737

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)
38+
template <typename U, typename V>
39+
auto gtest_expect_eq(U&& val1, V&& val2) {
40+
EXPECT_EQ(std::forward<U>(val1), std::forward<V>(val2));
41+
}
5542

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)
43+
template <typename U, typename V>
44+
constexpr auto constexpr_expect_eq(U&& val1, V&& val2) {
45+
if (::std::is_constant_evaluated()) {
46+
if (!(val1 == val2))
47+
std::abort();
48+
} else {
49+
gtest_expect_eq(std::forward<U>(val1), std::forward<V>(val2));
50+
}
51+
}
6352

6453
using namespace beman::optional::tests;
6554

@@ -132,10 +121,10 @@ TEST(RangeSupportTest, BeginOnEmptyOptional) {
132121
// e.g. const_iterator = optional<T>::const_iterator if opt is optional<T>
133122
using const_iterator = typename std::remove_reference_t<decltype(opt)>::const_iterator;
134123

135-
CONSTEXPR_EXPECT_EQ(opt.begin(), iterator());
124+
constexpr_expect_eq(iterator(), opt.begin());
136125

137126
const auto& const_opt = opt;
138-
CONSTEXPR_EXPECT_EQ(const_opt.begin(), const_iterator());
127+
constexpr_expect_eq(const_iterator(), const_opt.begin());
139128
};
140129

141130
test(beman::optional::optional<int>{});
@@ -144,9 +133,11 @@ TEST(RangeSupportTest, BeginOnEmptyOptional) {
144133
test(beman::optional::optional<no_default_ctor>{});
145134
test(beman::optional::optional<base>{});
146135
test(beman::optional::optional<derived>{});
136+
return true;
147137
};
148-
static_assert((lambda(), true));
149-
lambda();
138+
using beman::optional::tests::constify;
139+
EXPECT_TRUE(constify(lambda()));
140+
EXPECT_TRUE(lambda());
150141
}
151142

152143
TEST(RangeSupportTest, BeginOnNonEmptyOptional) {
@@ -158,10 +149,10 @@ TEST(RangeSupportTest, BeginOnNonEmptyOptional) {
158149
// e.g. const_iterator = optional<T>::const_iterator if opt is optional<T>
159150
using const_iterator = typename std::remove_reference_t<decltype(opt)>::const_iterator;
160151

161-
CONSTEXPR_EXPECT_EQ(opt.begin(), iterator(&*opt));
152+
constexpr_expect_eq(opt.begin(), iterator(&*opt));
162153

163154
const auto& const_opt = opt;
164-
CONSTEXPR_EXPECT_EQ(const_opt.begin(), const_iterator(&*opt));
155+
constexpr_expect_eq(const_opt.begin(), const_iterator(&*opt));
165156
};
166157

167158
test(beman::optional::optional<int>{26});
@@ -172,9 +163,10 @@ TEST(RangeSupportTest, BeginOnNonEmptyOptional) {
172163
test(beman::optional::optional<no_default_ctor>{no_default_ctor{empty{}}});
173164
test(beman::optional::optional<base>{base{}});
174165
test(beman::optional::optional<derived>{derived{}});
166+
return true;
175167
};
176-
static_assert((lambda(), true));
177-
lambda();
168+
EXPECT_TRUE(constify(lambda()));
169+
EXPECT_TRUE(lambda());
178170
}
179171

180172
TEST(RangeSupportTest, EndOnEmptyOptional) {
@@ -186,10 +178,10 @@ TEST(RangeSupportTest, EndOnEmptyOptional) {
186178
// e.g. const_iterator = optional<T>::const_iterator if opt is optional<T>
187179
using const_iterator = typename std::remove_reference_t<decltype(opt)>::const_iterator;
188180

189-
CONSTEXPR_EXPECT_EQ(opt.end(), iterator());
181+
constexpr_expect_eq(opt.end(), iterator());
190182

191183
const auto& const_opt = opt;
192-
CONSTEXPR_EXPECT_EQ(const_opt.end(), const_iterator());
184+
constexpr_expect_eq(const_opt.end(), const_iterator());
193185
};
194186

195187
test(beman::optional::optional<int>{});
@@ -198,9 +190,10 @@ TEST(RangeSupportTest, EndOnEmptyOptional) {
198190
test(beman::optional::optional<no_default_ctor>{});
199191
test(beman::optional::optional<base>{});
200192
test(beman::optional::optional<derived>{});
193+
return true;
201194
};
202-
static_assert((lambda(), true));
203-
lambda();
195+
EXPECT_TRUE(constify(lambda()));
196+
EXPECT_TRUE(lambda());
204197
}
205198

206199
TEST(RangeSupportTest, EndOnNonEmptyOptional) {
@@ -212,10 +205,10 @@ TEST(RangeSupportTest, EndOnNonEmptyOptional) {
212205
// e.g. const_iterator = optional<T>::const_iterator if opt is optional<T>
213206
using const_iterator = typename std::remove_reference_t<decltype(opt)>::const_iterator;
214207

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

217210
const auto& const_opt = opt;
218-
CONSTEXPR_EXPECT_EQ(const_opt.end(), const_iterator(&*opt + 1));
211+
constexpr_expect_eq(const_opt.end(), const_iterator(&*opt + 1));
219212
};
220213

221214
test(beman::optional::optional<int>{26});
@@ -226,12 +219,14 @@ TEST(RangeSupportTest, EndOnNonEmptyOptional) {
226219
test(beman::optional::optional<no_default_ctor>{no_default_ctor{empty{}}});
227220
test(beman::optional::optional<base>{base{}});
228221
test(beman::optional::optional<derived>{derived{}});
222+
return true;
229223
};
230-
static_assert((lambda(), true));
231-
lambda();
224+
EXPECT_TRUE(constify(lambda()));
225+
EXPECT_TRUE(lambda());
232226
}
233227

234-
#if ((__GNUC__ >= 15) && (__GNUC_MINOR__ >= 1) && (__GNUC_PATCHLEVEL__ >= 1)) || ((__GNUC__ >= 16))
228+
#if (__cplusplus >= 202302L) && \
229+
(((__GNUC__ >= 15) && (__GNUC_MINOR__ >= 1) && (__GNUC_PATCHLEVEL__ >= 1)) || ((__GNUC__ >= 16)))
235230
static_assert(std::format_kind<beman::optional::optional<int>> == std::range_format::disabled);
236231
#endif
237232

@@ -258,70 +253,89 @@ TEST(RangeSupportTest, FormatOptionalIsStillDisabled) {
258253
#endif
259254
}
260255

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

266276
for ([[maybe_unused]] auto _ : empty) {
267-
CONSTEXPR_ASSERT_TRUE(false) << "Should not be reached: expected not to loop over empty optional";
277+
constexpr_assert(false); // << "Should not be reached: expected not to loop over empty optional";
268278
}
279+
return true;
269280
};
270-
static_assert((lambda(), true));
271-
lambda();
281+
EXPECT_TRUE(constify(lambda()));
282+
EXPECT_TRUE(lambda());
272283
}
273284

274285
TEST(RangeSupportTest, LoopOverNonEmptyRange) {
275286
auto lambda = [&] {
276287
const int expected_value = 0xCAFEBABE;
277288
beman::optional::optional<int> empty{expected_value};
278-
CONSTEXPR_ASSERT_TRUE(empty.has_value());
289+
constexpr_assert(empty.has_value());
279290

280291
bool entered_loop = false;
281292
for (auto i : empty) {
282-
CONSTEXPR_EXPECT_EQ(i, expected_value);
293+
constexpr_expect_eq(i, expected_value);
283294
entered_loop = true;
284295
}
285-
CONSTEXPR_EXPECT_TRUE(entered_loop);
296+
constexpr_expect_eq(entered_loop, true);
297+
return true;
286298
};
287-
static_assert((lambda(), true));
288-
lambda();
299+
EXPECT_TRUE(constify(lambda()));
300+
EXPECT_TRUE(lambda());
289301
}
290302

291303
TEST(RangeSupportTest, LoopOptionalAccess) {
292304
auto lambda = [&] {
293305
// Example from P3168R2: should access the value from an optional object.
294306
const int expected_value = 0xCAFEBABE;
295307
const auto get_optional = [&]() -> beman::optional::optional<int> { return expected_value; };
296-
CONSTEXPR_ASSERT_TRUE(get_optional().has_value());
308+
constexpr_assert(get_optional().has_value());
297309

298310
for (auto&& opt : get_optional()) {
299-
CONSTEXPR_EXPECT_EQ(opt, expected_value); // usage of opt here is safe
311+
constexpr_expect_eq(opt, expected_value); // usage of opt here is safe
300312
}
313+
return true;
301314
};
302-
static_assert((lambda(), true));
303-
lambda();
315+
EXPECT_TRUE(constify(lambda()));
316+
EXPECT_TRUE(lambda());
304317
}
305318

306319
TEST(RangeSupportTest, LoopOptionalAssignment) {
307-
auto lambda = [&] {
320+
constexpr auto lambda = [] {
308321
// 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);
322+
constexpr int initial_expected_value = 0xCAFEBABE;
323+
constexpr int expected_value = 0xDEADBEEF;
324+
constexpr auto get_optional = [=]() -> beman::optional::optional<int> { return initial_expected_value; };
325+
constexpr_assert(get_optional().has_value());
326+
constexpr_assert(get_optional().value() == initial_expected_value);
314327

315328
auto opt_int = get_optional();
316329
for (auto&& opt : opt_int) {
317-
CONSTEXPR_EXPECT_EQ(opt, initial_expected_value);
330+
constexpr_expect_eq(opt, initial_expected_value);
318331
opt = expected_value; // usage of opt here is safe
319332
}
320-
CONSTEXPR_ASSERT_TRUE(opt_int.has_value());
321-
CONSTEXPR_EXPECT_EQ(opt_int.value(), expected_value);
333+
constexpr_assert(opt_int.has_value());
334+
constexpr_expect_eq(opt_int.value(), expected_value);
335+
return true;
322336
};
323-
static_assert((lambda(), true));
324-
lambda();
337+
EXPECT_TRUE(constify(lambda()));
338+
EXPECT_TRUE(lambda());
325339
}
326340

327341
TEST(RangeSupportTest, RangeChainExample) {
@@ -407,13 +421,14 @@ TEST(RangeSupportTest, PythagoreanTriples) {
407421
};
408422
constexpr const std::tuple k100th_triple = {
409423
26, 168, 170}; // The 100th Pythagorean triple with x, y, z <= 200.
410-
ASSERT_EQ(bruteforce_generate_nth(100, 200), k100th_triple);
424+
EXPECT_EQ(bruteforce_generate_nth(100, 200), k100th_triple);
411425

412426
// Generate the 100th Pythagorean triple with ranges.
413427
auto&& r = triples | std::views::drop(99) | std::views::take(1);
414428
EXPECT_TRUE(std::ranges::equal(r, std::vector{k100th_triple}));
415429
}
430+
return true;
416431
};
417-
static_assert((lambda(), true));
418-
lambda();
432+
EXPECT_TRUE(constify(lambda()));
433+
EXPECT_TRUE(lambda());
419434
}

0 commit comments

Comments
 (0)