Compare commits

...

8 Commits

Author SHA1 Message Date
Martin Hořeňovský
ab6c7375be v3.2.1 2022-12-09 23:10:18 +01:00
Martin Hořeňovský
24607694cb Move Catch::always_false into decomposer.hpp 2022-12-09 16:18:33 +01:00
Martin Hořeňovský
28e651f152 Move SFINAE in decomposer into return type
This is needed so that we can use conjunction and other logical
type traits to workaround issue with older GCC versions (8 and
below), when they run into types that have ambiguous constructor
from `0`, see e.g. #2571.

However, using conjunction and friends in the SFINAE constraint
in the template parameter breaks for C++20 and up, due to the new
comparison operator rewriting rules. With C++20, when the compiler
see `a == b`, it also tries `b == a` and collects overload set
for both of these expressions.

In Catch2, this means that e.g. `REQUIRE( 1 == 2 )` would lead
the compiler to check overloads for both `ExprLhs<int> == int`
and `int == ExprLhs<int>`. Since the overload set and SFINAE
constraints assume that `ExprLhs<T>` is always on the left side,
when the compiler tries to resolve the template parameters, all
hell breaks loose and the compilation fails.

By moving the SFINAE constraints to the return type, the compiler
can discard the switched expression without having to resolve
the complex SFINAE constraints, and thus everything works the
way it is supposed to.

Fixes #2571
2022-12-09 00:40:01 +01:00
Martin Hořeňovský
2d7be1f7de Add Clang-10 and GCC-10 C++20 builds 2022-11-22 16:17:18 +01:00
Martin Hořeňovský
1f3b51e903 Use logo with bit of white background in README
Closes #2573
2022-11-22 16:13:37 +01:00
Martin Hořeňovský
a20200be7e Revert "Fix old GCC + types with ambiguous constructor from 0"
This reverts commit 291c502f66.

The issue is that it breaks under C++20 for some reason.
2022-11-22 15:23:03 +01:00
Martin Hořeňovský
291c502f66 Fix old GCC + types with ambiguous constructor from 0
Closes #2571
2022-11-20 17:07:32 +01:00
Martin Hořeňovský
ae1644e7e9 Add logical trait polyfills 2022-11-20 17:03:29 +01:00
19 changed files with 331 additions and 212 deletions

View File

@@ -65,6 +65,22 @@ jobs:
build_type: Release
std: 17
other_pkgs: clang-10
- cxx: clang++-10
build_type: Debug
std: 20
other_pkgs: clang-10
- cxx: clang++-10
build_type: Release
std: 20
other_pkgs: clang-10
- cxx: g++-10
build_type: Debug
std: 20
other_pkgs: g++-10
- cxx: g++-10
build_type: Release
std: 20
other_pkgs: g++-10
steps:
- uses: actions/checkout@v2

View File

@@ -31,7 +31,7 @@ if (CMAKE_BINARY_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR)
endif()
project(Catch2
VERSION 3.2.0 # CML version placeholder, don't delete
VERSION 3.2.1 # CML version placeholder, don't delete
LANGUAGES CXX
# HOMEPAGE_URL is not supported until CMake version 3.12, which
# we do not target yet.

View File

@@ -1,5 +1,5 @@
<a id="top"></a>
![Catch2 logo](data/artwork/catch2-logo-small.png)
![Catch2 logo](data/artwork/catch2-logo-small-with-background.png)
[![Github Releases](https://img.shields.io/github/release/catchorg/catch2.svg)](https://github.com/catchorg/catch2/releases)
[![Linux build status](https://github.com/catchorg/Catch2/actions/workflows/linux-simple-builds.yml/badge.svg)](https://github.com/catchorg/Catch2/actions/workflows/linux-simple-builds.yml)

Binary file not shown.

After

Width:  |  Height:  |  Size: 25 KiB

View File

@@ -2,6 +2,7 @@
# Release notes
**Contents**<br>
[3.2.1](#321)<br>
[3.2.0](#320)<br>
[3.1.1](#311)<br>
[3.1.0](#310)<br>
@@ -53,6 +54,13 @@
## 3.2.1
### Improvements
* Fix the reworked decomposer to work with older (pre 9) GCC versions (#2571)
* **This required more significant changes to properly support C++20, there might be bugs.**
## 3.2.0
### Improvements

View File

@@ -5,8 +5,8 @@
// SPDX-License-Identifier: BSL-1.0
// Catch v3.2.0
// Generated: 2022-11-16 19:30:16.114602
// Catch v3.2.1
// Generated: 2022-12-09 23:01:15.713081
// ----------------------------------------------------------
// This file is an amalgamation of multiple different files.
// You probably shouldn't edit it directly.
@@ -2012,7 +2012,7 @@ namespace Catch {
}
Version const& libraryVersion() {
static Version version( 3, 2, 0, "", 0 );
static Version version( 3, 2, 1, "", 0 );
return version;
}

View File

@@ -5,8 +5,8 @@
// SPDX-License-Identifier: BSL-1.0
// Catch v3.2.0
// Generated: 2022-11-16 19:30:14.116909
// Catch v3.2.1
// Generated: 2022-12-09 23:01:14.526666
// ----------------------------------------------------------
// This file is an amalgamation of multiple different files.
// You probably shouldn't edit it directly.
@@ -1759,10 +1759,9 @@ namespace Catch {
#include <type_traits>
namespace Catch {
template<typename T>
struct always_false : std::false_type {};
template <typename>
struct true_given : std::true_type {};
template <typename> struct true_given : std::true_type {};
struct is_callable_tester {
template <typename Fun, typename... Args>
static true_given<decltype(std::declval<Fun>()(std::declval<Args>()...))> test(int);
@@ -5393,6 +5392,13 @@ namespace Catch {
# pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
#if defined( __clang__ )
# pragma clang diagnostic push
// Did you know that comparing floats with `0` directly
// is super-duper dangerous in unevaluated context?
# pragma clang diagnostic ignored "-Wfloat-equal"
#endif
#define CATCH_DEFINE_COMPARABLE_TRAIT( id, op ) \
template <typename, typename, typename = void> \
struct is_##id##_comparable : std::false_type {}; \
@@ -5421,6 +5427,9 @@ namespace Catch {
#if defined( __GNUC__ ) && !defined( __clang__ )
# pragma GCC diagnostic pop
#endif
#if defined( __clang__ )
# pragma clang diagnostic pop
#endif
@@ -5429,6 +5438,45 @@ namespace Catch {
#endif // CATCH_COMPARE_TRAITS_HPP_INCLUDED
#ifndef CATCH_LOGICAL_TRAITS_HPP_INCLUDED
#define CATCH_LOGICAL_TRAITS_HPP_INCLUDED
#include <type_traits>
namespace Catch {
namespace Detail {
#if defined( __cpp_lib_logical_traits ) && __cpp_lib_logical_traits >= 201510
using std::conjunction;
using std::disjunction;
using std::negation;
#else
template <class...> struct conjunction : std::true_type {};
template <class B1> struct conjunction<B1> : B1 {};
template <class B1, class... Bn>
struct conjunction<B1, Bn...>
: std::conditional_t<bool( B1::value ), conjunction<Bn...>, B1> {};
template <class...> struct disjunction : std::false_type {};
template <class B1> struct disjunction<B1> : B1 {};
template <class B1, class... Bn>
struct disjunction<B1, Bn...>
: std::conditional_t<bool( B1::value ), B1, disjunction<Bn...>> {};
template <class B>
struct negation : std::integral_constant<bool, !bool(B::value)> {};
#endif
} // namespace Detail
} // namespace Catch
#endif // CATCH_LOGICAL_TRAITS_HPP_INCLUDED
#include <type_traits>
#include <iosfwd>
@@ -5451,6 +5499,9 @@ namespace Catch {
namespace Catch {
template <typename T>
struct always_false : std::false_type {};
class ITransientExpression {
bool m_isBinaryExpression;
bool m_result;
@@ -5580,112 +5631,99 @@ namespace Catch {
explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {}
#define CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( id, op ) \
template < \
typename RhsT, \
std::enable_if_t< \
Detail::is_##id##_comparable<LhsT, RhsT>::value && \
!std::is_arithmetic<std::remove_reference_t<RhsT>>::value, \
int> = 0> \
template <typename RhsT> \
friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \
->BinaryExpr<LhsT, RhsT const&> { \
->std::enable_if_t< \
Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \
Detail::negation<std::is_arithmetic< \
std::remove_reference_t<RhsT>>>>::value, \
BinaryExpr<LhsT, RhsT const&>> { \
return { \
static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
} \
template < \
typename RhsT, \
std::enable_if_t<Detail::is_##id##_comparable<LhsT, RhsT>::value && \
std::is_arithmetic<RhsT>::value, \
int> = 0> \
template <typename RhsT> \
friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
->BinaryExpr<LhsT, RhsT> { \
->std::enable_if_t< \
Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \
std::is_arithmetic<RhsT>>::value, \
BinaryExpr<LhsT, RhsT>> { \
return { \
static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
} \
template < \
typename RhsT, \
std::enable_if_t<!Detail::is_##id##_comparable<LhsT, RhsT>::value && \
Detail::is_eq_0_comparable<LhsT>:: \
value && /* We allow long because we want \
`ptr op NULL to be accepted */ \
( std::is_same<RhsT, int>::value || \
std::is_same<RhsT, long>::value ), \
int> = 0> \
template <typename RhsT> \
friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
->BinaryExpr<LhsT, RhsT> { \
if ( rhs != 0 ) { \
throw_test_failure_exception(); \
} \
->std::enable_if_t< \
Detail::conjunction< \
Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \
Detail::is_eq_0_comparable<LhsT>, \
/* We allow long because we want `ptr op NULL` to be accepted */ \
Detail::disjunction<std::is_same<RhsT, int>, \
std::is_same<RhsT, long>>>::value, \
BinaryExpr<LhsT, RhsT>> { \
if ( rhs != 0 ) { throw_test_failure_exception(); } \
return { \
static_cast<bool>( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \
} \
template < \
typename RhsT, \
std::enable_if_t<!Detail::is_##id##_comparable<LhsT, RhsT>::value && \
Detail::is_eq_0_comparable<RhsT>:: \
value && /* We allow long because we want \
`ptr op NULL` to be accepted */ \
( std::is_same<LhsT, int>::value || \
std::is_same<LhsT, long>::value ), \
int> = 0> \
template <typename RhsT> \
friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
->BinaryExpr<LhsT, RhsT> { \
if ( lhs.m_lhs != 0 ) { \
throw_test_failure_exception(); \
} \
->std::enable_if_t< \
Detail::conjunction< \
Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \
Detail::is_eq_0_comparable<RhsT>, \
/* We allow long because we want `ptr op NULL` to be accepted */ \
Detail::disjunction<std::is_same<LhsT, int>, \
std::is_same<LhsT, long>>>::value, \
BinaryExpr<LhsT, RhsT>> { \
if ( lhs.m_lhs != 0 ) { throw_test_failure_exception(); } \
return { static_cast<bool>( 0 op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
}
CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( eq, == )
CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( ne, != )
#undef CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR
#define CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( id, op ) \
template < \
typename RhsT, \
std::enable_if_t< \
Detail::is_##id##_comparable<LhsT, RhsT>::value && \
!std::is_arithmetic<std::remove_reference_t<RhsT>>::value, \
int> = 0> \
#define CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( id, op ) \
template <typename RhsT> \
friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \
->BinaryExpr<LhsT, RhsT const&> { \
->std::enable_if_t< \
Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \
Detail::negation<std::is_arithmetic< \
std::remove_reference_t<RhsT>>>>::value, \
BinaryExpr<LhsT, RhsT const&>> { \
return { \
static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
} \
template < \
typename RhsT, \
std::enable_if_t<Detail::is_##id##_comparable<LhsT, RhsT>::value && \
std::is_arithmetic<RhsT>::value, \
int> = 0> \
template <typename RhsT> \
friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
->BinaryExpr<LhsT, RhsT> { \
->std::enable_if_t< \
Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \
std::is_arithmetic<RhsT>>::value, \
BinaryExpr<LhsT, RhsT>> { \
return { \
static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
} \
template < \
typename RhsT, \
std::enable_if_t<!Detail::is_##id##_comparable<LhsT, RhsT>::value && \
Detail::is_##id##_0_comparable<LhsT>::value && \
std::is_same<RhsT, int>::value, \
int> = 0> \
template <typename RhsT> \
friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
->BinaryExpr<LhsT, RhsT> { \
if ( rhs != 0 ) { \
throw_test_failure_exception(); \
} \
->std::enable_if_t< \
Detail::conjunction< \
Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \
Detail::is_##id##_0_comparable<LhsT>, \
std::is_same<RhsT, int>>::value, \
BinaryExpr<LhsT, RhsT>> { \
if ( rhs != 0 ) { throw_test_failure_exception(); } \
return { \
static_cast<bool>( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \
} \
template < \
typename RhsT, \
std::enable_if_t<!Detail::is_##id##_comparable<LhsT, RhsT>::value && \
Detail::is_##id##_0_comparable<RhsT>::value && \
std::is_same<LhsT, int>::value, \
int> = 0> \
template <typename RhsT> \
friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
->BinaryExpr<LhsT, RhsT> { \
if ( lhs.m_lhs != 0 ) { \
throw_test_failure_exception(); \
} \
->std::enable_if_t< \
Detail::conjunction< \
Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \
Detail::is_##id##_0_comparable<RhsT>, \
std::is_same<LhsT, int>>::value, \
BinaryExpr<LhsT, RhsT>> { \
if ( lhs.m_lhs != 0 ) { throw_test_failure_exception(); } \
return { static_cast<bool>( 0 op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
}
@@ -5697,15 +5735,22 @@ namespace Catch {
#undef CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR
#define CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(op) \
template<typename RhsT, std::enable_if_t<!std::is_arithmetic<std::remove_reference_t<RhsT>>::value, int> = 0> \
friend auto operator op ( ExprLhs && lhs, RhsT && rhs ) -> BinaryExpr<LhsT, RhsT const&> { \
return { static_cast<bool>(lhs.m_lhs op rhs), lhs.m_lhs, #op##_sr, rhs }; \
} \
template<typename RhsT, std::enable_if_t<std::is_arithmetic<RhsT>::value, int> = 0> \
friend auto operator op ( ExprLhs && lhs, RhsT rhs ) -> BinaryExpr<LhsT, RhsT> { \
return { static_cast<bool>(lhs.m_lhs op rhs), lhs.m_lhs, #op##_sr, rhs }; \
}
#define CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR( op ) \
template <typename RhsT> \
friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \
->std::enable_if_t< \
!std::is_arithmetic<std::remove_reference_t<RhsT>>::value, \
BinaryExpr<LhsT, RhsT const&>> { \
return { \
static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
} \
template <typename RhsT> \
friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
->std::enable_if_t<std::is_arithmetic<RhsT>::value, \
BinaryExpr<LhsT, RhsT>> { \
return { \
static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
}
CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(|)
CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(&)
@@ -7313,7 +7358,7 @@ namespace Catch {
#define CATCH_VERSION_MAJOR 3
#define CATCH_VERSION_MINOR 2
#define CATCH_VERSION_PATCH 0
#define CATCH_VERSION_PATCH 1
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED
@@ -10479,20 +10524,6 @@ namespace Matchers {
return arr;
}
#if defined( __cpp_lib_logical_traits ) && __cpp_lib_logical_traits >= 201510
using std::conjunction;
#else // __cpp_lib_logical_traits
template<typename... Cond>
struct conjunction : std::true_type {};
template<typename Cond, typename... Rest>
struct conjunction<Cond, Rest...> : std::integral_constant<bool, Cond::value && conjunction<Rest...>::value> {};
#endif // __cpp_lib_logical_traits
template<typename T>
using is_generic_matcher = std::is_base_of<
Catch::Matchers::MatcherGenericBase,
@@ -10500,7 +10531,7 @@ namespace Matchers {
>;
template<typename... Ts>
using are_generic_matchers = conjunction<is_generic_matcher<Ts>...>;
using are_generic_matchers = Catch::Detail::conjunction<is_generic_matcher<Ts>...>;
template<typename T>
using is_matcher = std::is_base_of<

View File

@@ -8,7 +8,7 @@
project(
'catch2',
'cpp',
version: '3.2.0', # CML version placeholder, don't delete
version: '3.2.1', # CML version placeholder, don't delete
license: 'BSL-1.0',
meson_version: '>=0.50.0',
)

View File

@@ -91,6 +91,7 @@ set(IMPL_HEADERS
${SOURCES_DIR}/internal/catch_lazy_expr.hpp
${SOURCES_DIR}/internal/catch_leak_detector.hpp
${SOURCES_DIR}/internal/catch_list.hpp
${SOURCES_DIR}/internal/catch_logical_traits.hpp
${SOURCES_DIR}/internal/catch_message_info.hpp
${SOURCES_DIR}/internal/catch_meta.hpp
${SOURCES_DIR}/internal/catch_move_and_forward.hpp

View File

@@ -74,6 +74,7 @@
#include <catch2/internal/catch_lazy_expr.hpp>
#include <catch2/internal/catch_leak_detector.hpp>
#include <catch2/internal/catch_list.hpp>
#include <catch2/internal/catch_logical_traits.hpp>
#include <catch2/internal/catch_message_info.hpp>
#include <catch2/internal/catch_meta.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>

View File

@@ -36,7 +36,7 @@ namespace Catch {
}
Version const& libraryVersion() {
static Version version( 3, 2, 0, "", 0 );
static Version version( 3, 2, 1, "", 0 );
return version;
}

View File

@@ -10,6 +10,6 @@
#define CATCH_VERSION_MAJOR 3
#define CATCH_VERSION_MINOR 2
#define CATCH_VERSION_PATCH 0
#define CATCH_VERSION_PATCH 1
#endif // CATCH_VERSION_MACROS_HPP_INCLUDED

View File

@@ -28,6 +28,13 @@ namespace Catch {
# pragma GCC diagnostic ignored "-Wfloat-equal"
#endif
#if defined( __clang__ )
# pragma clang diagnostic push
// Did you know that comparing floats with `0` directly
// is super-duper dangerous in unevaluated context?
# pragma clang diagnostic ignored "-Wfloat-equal"
#endif
#define CATCH_DEFINE_COMPARABLE_TRAIT( id, op ) \
template <typename, typename, typename = void> \
struct is_##id##_comparable : std::false_type {}; \
@@ -56,6 +63,9 @@ namespace Catch {
#if defined( __GNUC__ ) && !defined( __clang__ )
# pragma GCC diagnostic pop
#endif
#if defined( __clang__ )
# pragma clang diagnostic pop
#endif

View File

@@ -10,9 +10,9 @@
#include <catch2/catch_tostring.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_meta.hpp>
#include <catch2/internal/catch_compare_traits.hpp>
#include <catch2/internal/catch_test_failure_exception.hpp>
#include <catch2/internal/catch_logical_traits.hpp>
#include <type_traits>
#include <iosfwd>
@@ -36,6 +36,9 @@
namespace Catch {
template <typename T>
struct always_false : std::false_type {};
class ITransientExpression {
bool m_isBinaryExpression;
bool m_result;
@@ -165,112 +168,99 @@ namespace Catch {
explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {}
#define CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( id, op ) \
template < \
typename RhsT, \
std::enable_if_t< \
Detail::is_##id##_comparable<LhsT, RhsT>::value && \
!std::is_arithmetic<std::remove_reference_t<RhsT>>::value, \
int> = 0> \
template <typename RhsT> \
friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \
->BinaryExpr<LhsT, RhsT const&> { \
->std::enable_if_t< \
Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \
Detail::negation<std::is_arithmetic< \
std::remove_reference_t<RhsT>>>>::value, \
BinaryExpr<LhsT, RhsT const&>> { \
return { \
static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
} \
template < \
typename RhsT, \
std::enable_if_t<Detail::is_##id##_comparable<LhsT, RhsT>::value && \
std::is_arithmetic<RhsT>::value, \
int> = 0> \
template <typename RhsT> \
friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
->BinaryExpr<LhsT, RhsT> { \
->std::enable_if_t< \
Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \
std::is_arithmetic<RhsT>>::value, \
BinaryExpr<LhsT, RhsT>> { \
return { \
static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
} \
template < \
typename RhsT, \
std::enable_if_t<!Detail::is_##id##_comparable<LhsT, RhsT>::value && \
Detail::is_eq_0_comparable<LhsT>:: \
value && /* We allow long because we want \
`ptr op NULL to be accepted */ \
( std::is_same<RhsT, int>::value || \
std::is_same<RhsT, long>::value ), \
int> = 0> \
template <typename RhsT> \
friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
->BinaryExpr<LhsT, RhsT> { \
if ( rhs != 0 ) { \
throw_test_failure_exception(); \
} \
->std::enable_if_t< \
Detail::conjunction< \
Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \
Detail::is_eq_0_comparable<LhsT>, \
/* We allow long because we want `ptr op NULL` to be accepted */ \
Detail::disjunction<std::is_same<RhsT, int>, \
std::is_same<RhsT, long>>>::value, \
BinaryExpr<LhsT, RhsT>> { \
if ( rhs != 0 ) { throw_test_failure_exception(); } \
return { \
static_cast<bool>( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \
} \
template < \
typename RhsT, \
std::enable_if_t<!Detail::is_##id##_comparable<LhsT, RhsT>::value && \
Detail::is_eq_0_comparable<RhsT>:: \
value && /* We allow long because we want \
`ptr op NULL` to be accepted */ \
( std::is_same<LhsT, int>::value || \
std::is_same<LhsT, long>::value ), \
int> = 0> \
template <typename RhsT> \
friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
->BinaryExpr<LhsT, RhsT> { \
if ( lhs.m_lhs != 0 ) { \
throw_test_failure_exception(); \
} \
->std::enable_if_t< \
Detail::conjunction< \
Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \
Detail::is_eq_0_comparable<RhsT>, \
/* We allow long because we want `ptr op NULL` to be accepted */ \
Detail::disjunction<std::is_same<LhsT, int>, \
std::is_same<LhsT, long>>>::value, \
BinaryExpr<LhsT, RhsT>> { \
if ( lhs.m_lhs != 0 ) { throw_test_failure_exception(); } \
return { static_cast<bool>( 0 op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
}
CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( eq, == )
CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( ne, != )
#undef CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR
#define CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( id, op ) \
template < \
typename RhsT, \
std::enable_if_t< \
Detail::is_##id##_comparable<LhsT, RhsT>::value && \
!std::is_arithmetic<std::remove_reference_t<RhsT>>::value, \
int> = 0> \
#define CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( id, op ) \
template <typename RhsT> \
friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \
->BinaryExpr<LhsT, RhsT const&> { \
->std::enable_if_t< \
Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \
Detail::negation<std::is_arithmetic< \
std::remove_reference_t<RhsT>>>>::value, \
BinaryExpr<LhsT, RhsT const&>> { \
return { \
static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
} \
template < \
typename RhsT, \
std::enable_if_t<Detail::is_##id##_comparable<LhsT, RhsT>::value && \
std::is_arithmetic<RhsT>::value, \
int> = 0> \
template <typename RhsT> \
friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
->BinaryExpr<LhsT, RhsT> { \
->std::enable_if_t< \
Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \
std::is_arithmetic<RhsT>>::value, \
BinaryExpr<LhsT, RhsT>> { \
return { \
static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
} \
template < \
typename RhsT, \
std::enable_if_t<!Detail::is_##id##_comparable<LhsT, RhsT>::value && \
Detail::is_##id##_0_comparable<LhsT>::value && \
std::is_same<RhsT, int>::value, \
int> = 0> \
template <typename RhsT> \
friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
->BinaryExpr<LhsT, RhsT> { \
if ( rhs != 0 ) { \
throw_test_failure_exception(); \
} \
->std::enable_if_t< \
Detail::conjunction< \
Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \
Detail::is_##id##_0_comparable<LhsT>, \
std::is_same<RhsT, int>>::value, \
BinaryExpr<LhsT, RhsT>> { \
if ( rhs != 0 ) { throw_test_failure_exception(); } \
return { \
static_cast<bool>( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \
} \
template < \
typename RhsT, \
std::enable_if_t<!Detail::is_##id##_comparable<LhsT, RhsT>::value && \
Detail::is_##id##_0_comparable<RhsT>::value && \
std::is_same<LhsT, int>::value, \
int> = 0> \
template <typename RhsT> \
friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
->BinaryExpr<LhsT, RhsT> { \
if ( lhs.m_lhs != 0 ) { \
throw_test_failure_exception(); \
} \
->std::enable_if_t< \
Detail::conjunction< \
Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \
Detail::is_##id##_0_comparable<RhsT>, \
std::is_same<LhsT, int>>::value, \
BinaryExpr<LhsT, RhsT>> { \
if ( lhs.m_lhs != 0 ) { throw_test_failure_exception(); } \
return { static_cast<bool>( 0 op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
}
@@ -282,15 +272,22 @@ namespace Catch {
#undef CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR
#define CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(op) \
template<typename RhsT, std::enable_if_t<!std::is_arithmetic<std::remove_reference_t<RhsT>>::value, int> = 0> \
friend auto operator op ( ExprLhs && lhs, RhsT && rhs ) -> BinaryExpr<LhsT, RhsT const&> { \
return { static_cast<bool>(lhs.m_lhs op rhs), lhs.m_lhs, #op##_sr, rhs }; \
} \
template<typename RhsT, std::enable_if_t<std::is_arithmetic<RhsT>::value, int> = 0> \
friend auto operator op ( ExprLhs && lhs, RhsT rhs ) -> BinaryExpr<LhsT, RhsT> { \
return { static_cast<bool>(lhs.m_lhs op rhs), lhs.m_lhs, #op##_sr, rhs }; \
}
#define CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR( op ) \
template <typename RhsT> \
friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \
->std::enable_if_t< \
!std::is_arithmetic<std::remove_reference_t<RhsT>>::value, \
BinaryExpr<LhsT, RhsT const&>> { \
return { \
static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
} \
template <typename RhsT> \
friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \
->std::enable_if_t<std::is_arithmetic<RhsT>::value, \
BinaryExpr<LhsT, RhsT>> { \
return { \
static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \
}
CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(|)
CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR(&)

View File

@@ -0,0 +1,44 @@
// Copyright Catch2 Authors
// Distributed under the Boost Software License, Version 1.0.
// (See accompanying file LICENSE.txt or copy at
// https://www.boost.org/LICENSE_1_0.txt)
// SPDX-License-Identifier: BSL-1.0
#ifndef CATCH_LOGICAL_TRAITS_HPP_INCLUDED
#define CATCH_LOGICAL_TRAITS_HPP_INCLUDED
#include <type_traits>
namespace Catch {
namespace Detail {
#if defined( __cpp_lib_logical_traits ) && __cpp_lib_logical_traits >= 201510
using std::conjunction;
using std::disjunction;
using std::negation;
#else
template <class...> struct conjunction : std::true_type {};
template <class B1> struct conjunction<B1> : B1 {};
template <class B1, class... Bn>
struct conjunction<B1, Bn...>
: std::conditional_t<bool( B1::value ), conjunction<Bn...>, B1> {};
template <class...> struct disjunction : std::false_type {};
template <class B1> struct disjunction<B1> : B1 {};
template <class B1, class... Bn>
struct disjunction<B1, Bn...>
: std::conditional_t<bool( B1::value ), B1, disjunction<Bn...>> {};
template <class B>
struct negation : std::integral_constant<bool, !bool(B::value)> {};
#endif
} // namespace Detail
} // namespace Catch
#endif // CATCH_LOGICAL_TRAITS_HPP_INCLUDED

View File

@@ -11,10 +11,9 @@
#include <type_traits>
namespace Catch {
template<typename T>
struct always_false : std::false_type {};
template <typename>
struct true_given : std::true_type {};
template <typename> struct true_given : std::true_type {};
struct is_callable_tester {
template <typename Fun, typename... Args>
static true_given<decltype(std::declval<Fun>()(std::declval<Args>()...))> test(int);

View File

@@ -11,6 +11,7 @@
#include <catch2/matchers/catch_matchers.hpp>
#include <catch2/internal/catch_stringref.hpp>
#include <catch2/internal/catch_move_and_forward.hpp>
#include <catch2/internal/catch_logical_traits.hpp>
#include <array>
#include <algorithm>
@@ -56,20 +57,6 @@ namespace Matchers {
return arr;
}
#if defined( __cpp_lib_logical_traits ) && __cpp_lib_logical_traits >= 201510
using std::conjunction;
#else // __cpp_lib_logical_traits
template<typename... Cond>
struct conjunction : std::true_type {};
template<typename Cond, typename... Rest>
struct conjunction<Cond, Rest...> : std::integral_constant<bool, Cond::value && conjunction<Rest...>::value> {};
#endif // __cpp_lib_logical_traits
template<typename T>
using is_generic_matcher = std::is_base_of<
Catch::Matchers::MatcherGenericBase,
@@ -77,7 +64,7 @@ namespace Matchers {
>;
template<typename... Ts>
using are_generic_matchers = conjunction<is_generic_matcher<Ts>...>;
using are_generic_matchers = Catch::Detail::conjunction<is_generic_matcher<Ts>...>;
template<typename T>
using is_matcher = std::is_base_of<

View File

@@ -96,6 +96,7 @@ internal_headers = [
'internal/catch_lazy_expr.hpp',
'internal/catch_leak_detector.hpp',
'internal/catch_list.hpp',
'internal/catch_logical_traits.hpp',
'internal/catch_message_info.hpp',
'internal/catch_meta.hpp',
'internal/catch_move_and_forward.hpp',

View File

@@ -329,3 +329,27 @@ TEST_CASE( "#2555 - types that can only be compared with 0 literal (not int/long
REQUIRE( TypeWithLit0Comparisons{} != 0 );
REQUIRE_FALSE( 0 != TypeWithLit0Comparisons{} );
}
namespace {
struct MultipleImplicitConstructors {
MultipleImplicitConstructors( double ) {}
MultipleImplicitConstructors( int64_t ) {}
bool operator==( MultipleImplicitConstructors ) const { return true; }
bool operator!=( MultipleImplicitConstructors ) const { return true; }
bool operator<( MultipleImplicitConstructors ) const { return true; }
bool operator<=( MultipleImplicitConstructors ) const { return true; }
bool operator>( MultipleImplicitConstructors ) const { return true; }
bool operator>=( MultipleImplicitConstructors ) const { return true; }
};
}
TEST_CASE("#2571 - tests compile types that have multiple implicit constructors from lit 0",
"[compilation][approvals]") {
MultipleImplicitConstructors mic1( 0.0 );
MultipleImplicitConstructors mic2( 0.0 );
REQUIRE( mic1 == mic2 );
REQUIRE( mic1 != mic2 );
REQUIRE( mic1 < mic2 );
REQUIRE( mic1 <= mic2 );
REQUIRE( mic1 > mic2 );
REQUIRE( mic1 >= mic2 );
}