Skip to content

Commit 8b8b5bc

Browse files
authored
Merge pull request #481 from boostorg/mod_master
Modified Merge to Master
2 parents 0cba5d6 + 67add6b commit 8b8b5bc

5 files changed

Lines changed: 168 additions & 10 deletions

File tree

include/boost/test/impl/unit_test_main.ipp

Lines changed: 3 additions & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -212,7 +212,9 @@ unit_test_main( init_unit_test_func init_func, int argc, char* argv[] )
212212

213213
// getchar is defined as a macro in uClibc. Use parenthesis to fix
214214
// gcc bug 58952 for gcc <= 4.8.2.
215-
(std::getchar)();
215+
int ch = (std::getchar)();
216+
boost::ignore_unused(ch);
217+
216218
results_reporter::get_stream() << "Continuing..." << std::endl;
217219
}
218220

include/boost/test/tools/assertion.hpp

Lines changed: 140 additions & 9 deletions
Original file line numberDiff line numberDiff line change
@@ -30,6 +30,12 @@
3030
#include <utility>
3131
#endif
3232

33+
// GCC < 10 workaround for std::optional comparison ambiguity (see below)
34+
#if ((defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 10)) || (defined(__clang__) && __clang_major__ < 11)) && \
35+
(__cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L))
36+
#include <optional>
37+
#endif
38+
3339
#include <boost/test/detail/suppress_warnings.hpp>
3440

3541
//____________________________________________________________________________//
@@ -38,6 +44,28 @@ namespace boost {
3844
namespace test_tools {
3945
namespace assertion {
4046

47+
// ************************************************************************** //
48+
// ************** assertion::is_std_optional ************** //
49+
// ************************************************************************** //
50+
// Trait to detect std::optional. Used for GCC < 10 workaround.
51+
52+
template<typename T>
53+
struct is_std_optional : std::false_type {};
54+
55+
#if ((defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 10)) || (defined(__clang__) && __clang_major__ < 11)) && \
56+
(__cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L))
57+
58+
template<typename T>
59+
struct is_std_optional<std::optional<T>> : std::true_type {};
60+
template<typename T>
61+
struct is_std_optional<std::optional<T>&> : std::true_type {};
62+
template<typename T>
63+
struct is_std_optional<std::optional<T> const&> : std::true_type {};
64+
template<typename T>
65+
struct is_std_optional<std::optional<T>&&> : std::true_type {};
66+
67+
#endif
68+
4169
// ************************************************************************** //
4270
// ************** assertion::operators ************** //
4371
// ************************************************************************** //
@@ -77,7 +105,8 @@ namespace op {
77105

78106
#ifndef BOOST_NO_CXX11_DECLTYPE
79107

80-
#define BOOST_TEST_FOR_EACH_CONST_OP(action)\
108+
// Non-comparison operators (never need SFINAE workaround)
109+
#define BOOST_TEST_FOR_EACH_NONCOMP_OP(action)\
81110
action(->*, MEMP, ->*, MEMP ) \
82111
\
83112
action( * , MUL , * , MUL ) \
@@ -90,15 +119,20 @@ namespace op {
90119
action( <<, LSH , << , LSH ) \
91120
action( >>, RSH , >> , RSH ) \
92121
\
93-
BOOST_TEST_FOR_EACH_COMP_OP(action) \
94-
\
95122
action( & , BAND, & , BAND ) \
96123
action( ^ , XOR , ^ , XOR ) \
97124
action( | , BOR , | , BOR ) \
98125
/**/
99126

127+
#define BOOST_TEST_FOR_EACH_CONST_OP(action)\
128+
BOOST_TEST_FOR_EACH_NONCOMP_OP(action) \
129+
BOOST_TEST_FOR_EACH_COMP_OP(action) \
130+
/**/
131+
100132
#else
101133

134+
#define BOOST_TEST_FOR_EACH_NONCOMP_OP(action)
135+
102136
#define BOOST_TEST_FOR_EACH_CONST_OP(action)\
103137
BOOST_TEST_FOR_EACH_COMP_OP(action) \
104138
/**/
@@ -181,20 +215,87 @@ BOOST_TEST_FOR_EACH_CONST_OP( DEFINE_CONST_OPER )
181215
} // namespace op
182216

183217
// ************************************************************************** //
184-
// ************** assertion::expression_base ************** //
218+
// ************** assertion::optional_friends_base ************** //
185219
// ************************************************************************** //
186-
// Defines expression operators
220+
// GCC < 10 workaround: Base class that conditionally provides hidden friend
221+
// comparison operators only when ValType is std::optional. Hidden friends are
222+
// found via ADL and as non-templates beat std::optional's template operators.
223+
// See: https://github.com/boostorg/test/issues/475
187224

188225
template<typename Lhs, typename Rhs, typename OP> class binary_expr;
189226

227+
#if ((defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 10)) || (defined(__clang__) && __clang_major__ < 11)) && \
228+
(__cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L))
229+
230+
// Primary template - empty (for non-optional types)
231+
template<typename ExprType, typename ValType, bool IsOptional = is_std_optional<ValType>::value>
232+
struct optional_friends_base {};
233+
234+
// Specialization for std::optional types - provides hidden friend operators
235+
template<typename ExprType, typename ValType>
236+
struct optional_friends_base<ExprType, ValType, true> {
237+
friend binary_expr<ExprType, ValType, op::EQ<ValType, ValType>>
238+
operator==( ExprType lhs, ValType rhs )
239+
{
240+
return binary_expr<ExprType, ValType, op::EQ<ValType, ValType>>(
241+
std::move(lhs), std::move(rhs));
242+
}
243+
244+
friend binary_expr<ExprType, ValType, op::NE<ValType, ValType>>
245+
operator!=( ExprType lhs, ValType rhs )
246+
{
247+
return binary_expr<ExprType, ValType, op::NE<ValType, ValType>>(
248+
std::move(lhs), std::move(rhs));
249+
}
250+
251+
friend binary_expr<ExprType, ValType, op::LT<ValType, ValType>>
252+
operator<( ExprType lhs, ValType rhs )
253+
{
254+
return binary_expr<ExprType, ValType, op::LT<ValType, ValType>>(
255+
std::move(lhs), std::move(rhs));
256+
}
257+
258+
friend binary_expr<ExprType, ValType, op::LE<ValType, ValType>>
259+
operator<=( ExprType lhs, ValType rhs )
260+
{
261+
return binary_expr<ExprType, ValType, op::LE<ValType, ValType>>(
262+
std::move(lhs), std::move(rhs));
263+
}
264+
265+
friend binary_expr<ExprType, ValType, op::GT<ValType, ValType>>
266+
operator>( ExprType lhs, ValType rhs )
267+
{
268+
return binary_expr<ExprType, ValType, op::GT<ValType, ValType>>(
269+
std::move(lhs), std::move(rhs));
270+
}
271+
272+
friend binary_expr<ExprType, ValType, op::GE<ValType, ValType>>
273+
operator>=( ExprType lhs, ValType rhs )
274+
{
275+
return binary_expr<ExprType, ValType, op::GE<ValType, ValType>>(
276+
std::move(lhs), std::move(rhs));
277+
}
278+
};
279+
280+
#else // Not GCC < 10
281+
template<typename ExprType, typename ValType, bool IsOptional = false>
282+
struct optional_friends_base {};
283+
#endif // GCC < 10 && C++17
284+
285+
// ************************************************************************** //
286+
// ************** assertion::expression_base ************** //
287+
// ************************************************************************** //
288+
// Defines expression operators
289+
190290
template<typename ExprType,typename ValType>
191-
class expression_base {
291+
class expression_base : public optional_friends_base<ExprType, ValType> {
192292
public:
193293

194294
#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
195295
template<typename T>
196296
struct RhsT : remove_const<typename remove_reference<T>::type> {};
197-
297+
298+
// Regular operator support (non-comparison operators)
198299
#define ADD_OP_SUPPORT( oper, name, _, _i ) \
199300
template<typename T> \
200301
binary_expr<ExprType,T, \
@@ -208,7 +309,33 @@ class expression_base {
208309
std::forward<T>(rhs) ); \
209310
} \
210311
/**/
312+
313+
// GCC < 10 workaround: comparison operators with SFINAE to exclude std::optional
314+
// when ValType is also std::optional. The hidden friend operators in the base class
315+
// optional_friends_base will handle the optional==optional case instead.
316+
#if ((defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 10)) || (defined(__clang__) && __clang_major__ < 11)) && \
317+
(__cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L))
318+
#define ADD_OP_SUPPORT_COMP( oper, name, _, _i ) \
319+
template<typename T, \
320+
typename std::enable_if< \
321+
!is_std_optional<ValType>::value || \
322+
!is_std_optional<typename RhsT<T>::type>::value, int>::type = 0> \
323+
binary_expr<ExprType,T, \
324+
op::name<ValType,typename RhsT<T>::type> > \
325+
operator oper( T&& rhs ) \
326+
{ \
327+
return binary_expr<ExprType,T, \
328+
op::name<ValType,typename RhsT<T>::type> > \
329+
( std::forward<ExprType>( \
330+
*static_cast<ExprType*>(this) ), \
331+
std::forward<T>(rhs) ); \
332+
} \
333+
/**/
211334
#else
335+
#define ADD_OP_SUPPORT_COMP ADD_OP_SUPPORT
336+
#endif
337+
338+
#else // BOOST_NO_CXX11_RVALUE_REFERENCES
212339

213340
#define ADD_OP_SUPPORT( oper, name, _, _i ) \
214341
template<typename T> \
@@ -222,10 +349,14 @@ class expression_base {
222349
rhs ); \
223350
} \
224351
/**/
225-
#endif
352+
#define ADD_OP_SUPPORT_COMP ADD_OP_SUPPORT
353+
354+
#endif // BOOST_NO_CXX11_RVALUE_REFERENCES
226355

227-
BOOST_TEST_FOR_EACH_CONST_OP( ADD_OP_SUPPORT )
356+
BOOST_TEST_FOR_EACH_NONCOMP_OP( ADD_OP_SUPPORT )
357+
BOOST_TEST_FOR_EACH_COMP_OP( ADD_OP_SUPPORT_COMP )
228358
#undef ADD_OP_SUPPORT
359+
#undef ADD_OP_SUPPORT_COMP
229360

230361
#ifndef BOOST_NO_CXX11_AUTO_DECLARATIONS
231362
// Disabled operators

include/boost/test/unit_test_suite.hpp

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -348,6 +348,11 @@ typedef ::boost::unit_test::ut_detail::nil_t BOOST_AUTO_TEST_CASE_FIXTURE;
348348
// ************** Auto registration facility helper macros ************** //
349349
// ************************************************************************** //
350350

351+
#if defined(__clang__) && __clang_major__ >= 22
352+
# pragma clang diagnostic push
353+
# pragma clang diagnostic ignored "-Wc2y-extensions"
354+
#endif
355+
351356
// Facility for having a unique name based on __LINE__ and __COUNTER__ (later if available)
352357
#if defined(__COUNTER__)
353358
#define BOOST_TEST_INTERNAL_HAS_COUNTER
@@ -364,6 +369,10 @@ typedef ::boost::unit_test::ut_detail::nil_t BOOST_AUTO_TEST_CASE_FIXTURE;
364369
#endif
365370
/**/
366371

372+
#if defined(__clang__) && __clang_major__ >= 22
373+
# pragma clang diagnostic pop
374+
#endif
375+
367376
#define BOOST_AUTO_TU_REGISTRAR( test_name ) \
368377
static boost::unit_test::ut_detail::auto_test_unit_registrar \
369378
BOOST_TEST_APPEND_UNIQUE_ID( BOOST_JOIN( test_name, _registrar ) ) BOOST_ATTRIBUTE_UNUSED \

test/Jamfile.v2

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -188,6 +188,7 @@ test-suite "writing-test-ts"
188188
# [ boost.test-self-test run-fail : writing-test-ts : test-timeout-fail : : : : : : [ requires cxx11_hdr_thread cxx11_hdr_chrono ] ]
189189
# [ boost.test-self-test run : writing-test-ts : test-timeout-suite : : : : : : $(requirements_datasets) [ requires cxx11_hdr_thread cxx11_hdr_chrono ] ]
190190
# [ boost.test-self-test run-fail : writing-test-ts : test-timeout-suite-fail : : : : : : $(requirements_datasets) [ requires cxx11_hdr_thread cxx11_hdr_chrono ] ]
191+
[ boost.test-self-test run : writing-test-ts : github_issue_475 : : : : : : [ requires cxx17_hdr_optional ] ]
191192
;
192193

193194
#_________________________________________________________________________________________________#
Lines changed: 15 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,15 @@
1+
// Copyright 2025 Matt Borland
2+
// Distributed under the Boost Software License, Version 1.0.
3+
// https://www.boost.org/LICENSE_1_0.txt
4+
5+
#define BOOST_TEST_MODULE github_issue_475
6+
#include <optional>
7+
#include <boost/test/unit_test.hpp>
8+
9+
BOOST_TEST_DONT_PRINT_LOG_VALUE(std::optional<int>)
10+
11+
BOOST_AUTO_TEST_CASE(test1)
12+
{
13+
std::optional<int> a,b;
14+
BOOST_TEST(a==b);
15+
}

0 commit comments

Comments
 (0)