diff --git a/CMakeLists.txt b/CMakeLists.txt index e551bc6a2c..af3374b4be 100644 --- a/CMakeLists.txt +++ b/CMakeLists.txt @@ -25,7 +25,6 @@ set(_boost_test_dependencies Boost::optional Boost::preprocessor Boost::smart_ptr - Boost::static_assert Boost::type_traits Boost::utility ) diff --git a/build.jam b/build.jam index dd6db1f895..80d7d135af 100644 --- a/build.jam +++ b/build.jam @@ -21,7 +21,6 @@ constant boost_dependencies : /boost/optional//boost_optional /boost/preprocessor//boost_preprocessor /boost/smart_ptr//boost_smart_ptr - /boost/static_assert//boost_static_assert /boost/type_traits//boost_type_traits /boost/utility//boost_utility ; diff --git a/include/boost/test/impl/unit_test_main.ipp b/include/boost/test/impl/unit_test_main.ipp index 2fedecea44..b52f3dda77 100644 --- a/include/boost/test/impl/unit_test_main.ipp +++ b/include/boost/test/impl/unit_test_main.ipp @@ -212,7 +212,9 @@ unit_test_main( init_unit_test_func init_func, int argc, char* argv[] ) // getchar is defined as a macro in uClibc. Use parenthesis to fix // gcc bug 58952 for gcc <= 4.8.2. - (std::getchar)(); + int ch = (std::getchar)(); + boost::ignore_unused(ch); + results_reporter::get_stream() << "Continuing..." << std::endl; } diff --git a/include/boost/test/tools/assertion.hpp b/include/boost/test/tools/assertion.hpp index 39eab3b03b..472b60e221 100644 --- a/include/boost/test/tools/assertion.hpp +++ b/include/boost/test/tools/assertion.hpp @@ -30,6 +30,12 @@ #include #endif +// GCC < 10 workaround for std::optional comparison ambiguity (see below) +#if ((defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 10)) || (defined(__clang__) && __clang_major__ < 11)) && \ + (__cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)) +#include +#endif + #include //____________________________________________________________________________// @@ -38,6 +44,28 @@ namespace boost { namespace test_tools { namespace assertion { +// ************************************************************************** // +// ************** assertion::is_std_optional ************** // +// ************************************************************************** // +// Trait to detect std::optional. Used for GCC < 10 workaround. + +template +struct is_std_optional : std::false_type {}; + +#if ((defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 10)) || (defined(__clang__) && __clang_major__ < 11)) && \ + (__cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)) + +template +struct is_std_optional> : std::true_type {}; +template +struct is_std_optional&> : std::true_type {}; +template +struct is_std_optional const&> : std::true_type {}; +template +struct is_std_optional&&> : std::true_type {}; + +#endif + // ************************************************************************** // // ************** assertion::operators ************** // // ************************************************************************** // @@ -77,7 +105,8 @@ namespace op { #ifndef BOOST_NO_CXX11_DECLTYPE -#define BOOST_TEST_FOR_EACH_CONST_OP(action)\ +// Non-comparison operators (never need SFINAE workaround) +#define BOOST_TEST_FOR_EACH_NONCOMP_OP(action)\ action(->*, MEMP, ->*, MEMP ) \ \ action( * , MUL , * , MUL ) \ @@ -90,15 +119,20 @@ namespace op { action( <<, LSH , << , LSH ) \ action( >>, RSH , >> , RSH ) \ \ - BOOST_TEST_FOR_EACH_COMP_OP(action) \ - \ action( & , BAND, & , BAND ) \ action( ^ , XOR , ^ , XOR ) \ action( | , BOR , | , BOR ) \ /**/ +#define BOOST_TEST_FOR_EACH_CONST_OP(action)\ + BOOST_TEST_FOR_EACH_NONCOMP_OP(action) \ + BOOST_TEST_FOR_EACH_COMP_OP(action) \ +/**/ + #else +#define BOOST_TEST_FOR_EACH_NONCOMP_OP(action) + #define BOOST_TEST_FOR_EACH_CONST_OP(action)\ BOOST_TEST_FOR_EACH_COMP_OP(action) \ /**/ @@ -181,20 +215,87 @@ BOOST_TEST_FOR_EACH_CONST_OP( DEFINE_CONST_OPER ) } // namespace op // ************************************************************************** // -// ************** assertion::expression_base ************** // +// ************** assertion::optional_friends_base ************** // // ************************************************************************** // -// Defines expression operators +// GCC < 10 workaround: Base class that conditionally provides hidden friend +// comparison operators only when ValType is std::optional. Hidden friends are +// found via ADL and as non-templates beat std::optional's template operators. +// See: https://github.com/boostorg/test/issues/475 template class binary_expr; +#if ((defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 10)) || (defined(__clang__) && __clang_major__ < 11)) && \ + (__cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)) + +// Primary template - empty (for non-optional types) +template::value> +struct optional_friends_base {}; + +// Specialization for std::optional types - provides hidden friend operators +template +struct optional_friends_base { + friend binary_expr> + operator==( ExprType lhs, ValType rhs ) + { + return binary_expr>( + std::move(lhs), std::move(rhs)); + } + + friend binary_expr> + operator!=( ExprType lhs, ValType rhs ) + { + return binary_expr>( + std::move(lhs), std::move(rhs)); + } + + friend binary_expr> + operator<( ExprType lhs, ValType rhs ) + { + return binary_expr>( + std::move(lhs), std::move(rhs)); + } + + friend binary_expr> + operator<=( ExprType lhs, ValType rhs ) + { + return binary_expr>( + std::move(lhs), std::move(rhs)); + } + + friend binary_expr> + operator>( ExprType lhs, ValType rhs ) + { + return binary_expr>( + std::move(lhs), std::move(rhs)); + } + + friend binary_expr> + operator>=( ExprType lhs, ValType rhs ) + { + return binary_expr>( + std::move(lhs), std::move(rhs)); + } +}; + +#else // Not GCC < 10 +template +struct optional_friends_base {}; +#endif // GCC < 10 && C++17 + +// ************************************************************************** // +// ************** assertion::expression_base ************** // +// ************************************************************************** // +// Defines expression operators + template -class expression_base { +class expression_base : public optional_friends_base { public: #ifndef BOOST_NO_CXX11_RVALUE_REFERENCES template struct RhsT : remove_const::type> {}; - + +// Regular operator support (non-comparison operators) #define ADD_OP_SUPPORT( oper, name, _, _i ) \ template \ binary_expr(rhs) ); \ } \ /**/ + +// GCC < 10 workaround: comparison operators with SFINAE to exclude std::optional +// when ValType is also std::optional. The hidden friend operators in the base class +// optional_friends_base will handle the optional==optional case instead. +#if ((defined(__GNUC__) && !defined(__clang__) && (__GNUC__ < 10)) || (defined(__clang__) && __clang_major__ < 11)) && \ + (__cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)) +#define ADD_OP_SUPPORT_COMP( oper, name, _, _i ) \ + template::value || \ + !is_std_optional::type>::value, int>::type = 0> \ + binary_expr::type> > \ + operator oper( T&& rhs ) \ + { \ + return binary_expr::type> > \ + ( std::forward( \ + *static_cast(this) ), \ + std::forward(rhs) ); \ + } \ +/**/ #else +#define ADD_OP_SUPPORT_COMP ADD_OP_SUPPORT +#endif + +#else // BOOST_NO_CXX11_RVALUE_REFERENCES #define ADD_OP_SUPPORT( oper, name, _, _i ) \ template \ @@ -222,10 +349,14 @@ class expression_base { rhs ); \ } \ /**/ -#endif +#define ADD_OP_SUPPORT_COMP ADD_OP_SUPPORT + +#endif // BOOST_NO_CXX11_RVALUE_REFERENCES - BOOST_TEST_FOR_EACH_CONST_OP( ADD_OP_SUPPORT ) + BOOST_TEST_FOR_EACH_NONCOMP_OP( ADD_OP_SUPPORT ) + BOOST_TEST_FOR_EACH_COMP_OP( ADD_OP_SUPPORT_COMP ) #undef ADD_OP_SUPPORT + #undef ADD_OP_SUPPORT_COMP #ifndef BOOST_NO_CXX11_AUTO_DECLARATIONS // Disabled operators diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index 33c2bcdfb3..9e8ca58a7c 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -188,6 +188,7 @@ test-suite "writing-test-ts" # [ boost.test-self-test run-fail : writing-test-ts : test-timeout-fail : : : : : : [ requires cxx11_hdr_thread cxx11_hdr_chrono ] ] # [ boost.test-self-test run : writing-test-ts : test-timeout-suite : : : : : : $(requirements_datasets) [ requires cxx11_hdr_thread cxx11_hdr_chrono ] ] # [ boost.test-self-test run-fail : writing-test-ts : test-timeout-suite-fail : : : : : : $(requirements_datasets) [ requires cxx11_hdr_thread cxx11_hdr_chrono ] ] + [ boost.test-self-test run : writing-test-ts : github_issue_475 : : : : : : [ requires cxx17_hdr_optional ] ] ; #_________________________________________________________________________________________________# diff --git a/test/cmake_test/CMakeLists.txt b/test/cmake_test/CMakeLists.txt index 7134060718..a4dfa7e329 100644 --- a/test/cmake_test/CMakeLists.txt +++ b/test/cmake_test/CMakeLists.txt @@ -32,7 +32,6 @@ else() optional preprocessor smart_ptr - static_assert type_traits utility diff --git a/test/writing-test-ts/github_issue_475.cpp b/test/writing-test-ts/github_issue_475.cpp new file mode 100644 index 0000000000..5959db92bd --- /dev/null +++ b/test/writing-test-ts/github_issue_475.cpp @@ -0,0 +1,15 @@ +// Copyright 2025 Matt Borland +// Distributed under the Boost Software License, Version 1.0. +// https://www.boost.org/LICENSE_1_0.txt + +#define BOOST_TEST_MODULE github_issue_475 +#include +#include + +BOOST_TEST_DONT_PRINT_LOG_VALUE(std::optional) + +BOOST_AUTO_TEST_CASE(test1) +{ + std::optional a,b; + BOOST_TEST(a==b); +}