Support literal-zero detectors using consteval int constructors · catchorg/Catch2@dc51386

5 min read Original article ↗

@@ -13,6 +13,7 @@

1313

#include <catch2/internal/catch_compare_traits.hpp>

1414

#include <catch2/internal/catch_test_failure_exception.hpp>

1515

#include <catch2/internal/catch_logical_traits.hpp>

16+

#include <catch2/internal/catch_compiler_capabilities.hpp>

16171718

#include <type_traits>

1819

#include <iosfwd>

@@ -34,8 +35,33 @@

3435

# pragma GCC diagnostic ignored "-Wsign-compare"

3536

#endif

363738+

#if defined(CATCH_CPP20_OR_GREATER) && __has_include(<compare>)

39+

# include <compare>

40+

# if defined( __cpp_lib_three_way_comparison ) && \

41+

__cpp_lib_three_way_comparison >= 201907L

42+

# define CATCH_CONFIG_CPP20_COMPARE_OVERLOADS

43+

# endif

44+

#endif

45+3746

namespace Catch {

384748+

// Note: There is nothing that stops us from extending this,

49+

// e.g. to `std::is_scalar`, but the more encompassing

50+

// traits are usually also more expensive. For now we

51+

// keep this as it used to be and it can be changed later.

52+

template <typename T>

53+

struct capture_by_value

54+

: std::integral_constant<bool, std::is_arithmetic<T>{}> {};

55+56+

#if defined( CATCH_CONFIG_CPP20_COMPARE_OVERLOADS )

57+

template <>

58+

struct capture_by_value<std::strong_ordering> : std::true_type {};

59+

template <>

60+

struct capture_by_value<std::weak_ordering> : std::true_type {};

61+

template <>

62+

struct capture_by_value<std::partial_ordering> : std::true_type {};

63+

#endif

64+3965

template <typename T>

4066

struct always_false : std::false_type {};

4167

@@ -44,11 +70,12 @@ namespace Catch {

4470

bool m_result;

45714672

public:

47-

auto isBinaryExpression() const -> bool { return m_isBinaryExpression; }

48-

auto getResult() const -> bool { return m_result; }

49-

virtual void streamReconstructedExpression( std::ostream &os ) const = 0;

73+

constexpr auto isBinaryExpression() const -> bool { return m_isBinaryExpression; }

74+

constexpr auto getResult() const -> bool { return m_result; }

75+

//! This function **has** to be overriden by the derived class.

76+

virtual void streamReconstructedExpression( std::ostream& os ) const;

507751-

ITransientExpression( bool isBinaryExpression, bool result )

78+

constexpr ITransientExpression( bool isBinaryExpression, bool result )

5279

: m_isBinaryExpression( isBinaryExpression ),

5380

m_result( result )

5481

{}

@@ -59,7 +86,7 @@ namespace Catch {

59866087

// We don't actually need a virtual destructor, but many static analysers

6188

// complain if it's not here :-(

62-

virtual ~ITransientExpression(); // = default;

89+

virtual ~ITransientExpression() = default;

63906491

friend std::ostream& operator<<(std::ostream& out, ITransientExpression const& expr) {

6592

expr.streamReconstructedExpression(out);

@@ -81,7 +108,7 @@ namespace Catch {

81108

}

8210983110

public:

84-

BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs )

111+

constexpr BinaryExpr( bool comparisonResult, LhsT lhs, StringRef op, RhsT rhs )

85112

: ITransientExpression{ true, comparisonResult },

86113

m_lhs( lhs ),

87114

m_op( op ),

@@ -154,7 +181,7 @@ namespace Catch {

154181

}

155182156183

public:

157-

explicit UnaryExpr( LhsT lhs )

184+

explicit constexpr UnaryExpr( LhsT lhs )

158185

: ITransientExpression{ false, static_cast<bool>(lhs) },

159186

m_lhs( lhs )

160187

{}

@@ -165,30 +192,30 @@ namespace Catch {

165192

class ExprLhs {

166193

LhsT m_lhs;

167194

public:

168-

explicit ExprLhs( LhsT lhs ) : m_lhs( lhs ) {}

195+

explicit constexpr ExprLhs( LhsT lhs ) : m_lhs( lhs ) {}

169196170197

#define CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR( id, op ) \

171198

template <typename RhsT> \

172-

friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \

199+

constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \

173200

->std::enable_if_t< \

174201

Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \

175-

Detail::negation<std::is_arithmetic< \

202+

Detail::negation<capture_by_value< \

176203

std::remove_reference_t<RhsT>>>>::value, \

177204

BinaryExpr<LhsT, RhsT const&>> { \

178205

return { \

179206

static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \

180207

} \

181208

template <typename RhsT> \

182-

friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \

209+

constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \

183210

->std::enable_if_t< \

184211

Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \

185-

std::is_arithmetic<RhsT>>::value, \

212+

capture_by_value<RhsT>>::value, \

186213

BinaryExpr<LhsT, RhsT>> { \

187214

return { \

188215

static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \

189216

} \

190217

template <typename RhsT> \

191-

friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \

218+

constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \

192219

->std::enable_if_t< \

193220

Detail::conjunction< \

194221

Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \

@@ -202,7 +229,7 @@ namespace Catch {

202229

static_cast<bool>( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \

203230

} \

204231

template <typename RhsT> \

205-

friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \

232+

constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \

206233

->std::enable_if_t< \

207234

Detail::conjunction< \

208235

Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \

@@ -220,28 +247,29 @@ namespace Catch {

220247221248

#undef CATCH_INTERNAL_DEFINE_EXPRESSION_EQUALITY_OPERATOR

222249250+223251

#define CATCH_INTERNAL_DEFINE_EXPRESSION_COMPARISON_OPERATOR( id, op ) \

224252

template <typename RhsT> \

225-

friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \

253+

constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \

226254

->std::enable_if_t< \

227255

Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \

228-

Detail::negation<std::is_arithmetic< \

256+

Detail::negation<capture_by_value< \

229257

std::remove_reference_t<RhsT>>>>::value, \

230258

BinaryExpr<LhsT, RhsT const&>> { \

231259

return { \

232260

static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \

233261

} \

234262

template <typename RhsT> \

235-

friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \

263+

constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \

236264

->std::enable_if_t< \

237265

Detail::conjunction<Detail::is_##id##_comparable<LhsT, RhsT>, \

238-

std::is_arithmetic<RhsT>>::value, \

266+

capture_by_value<RhsT>>::value, \

239267

BinaryExpr<LhsT, RhsT>> { \

240268

return { \

241269

static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \

242270

} \

243271

template <typename RhsT> \

244-

friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \

272+

constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \

245273

->std::enable_if_t< \

246274

Detail::conjunction< \

247275

Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \

@@ -253,7 +281,7 @@ namespace Catch {

253281

static_cast<bool>( lhs.m_lhs op 0 ), lhs.m_lhs, #op##_sr, rhs }; \

254282

} \

255283

template <typename RhsT> \

256-

friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \

284+

constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \

257285

->std::enable_if_t< \

258286

Detail::conjunction< \

259287

Detail::negation<Detail::is_##id##_comparable<LhsT, RhsT>>, \

@@ -274,16 +302,16 @@ namespace Catch {

274302275303

#define CATCH_INTERNAL_DEFINE_EXPRESSION_OPERATOR( op ) \

276304

template <typename RhsT> \

277-

friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \

305+

constexpr friend auto operator op( ExprLhs&& lhs, RhsT&& rhs ) \

278306

->std::enable_if_t< \

279-

!std::is_arithmetic<std::remove_reference_t<RhsT>>::value, \

307+

!capture_by_value<std::remove_reference_t<RhsT>>::value, \

280308

BinaryExpr<LhsT, RhsT const&>> { \

281309

return { \

282310

static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \

283311

} \

284312

template <typename RhsT> \

285-

friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \

286-

->std::enable_if_t<std::is_arithmetic<RhsT>::value, \

313+

constexpr friend auto operator op( ExprLhs&& lhs, RhsT rhs ) \

314+

->std::enable_if_t<capture_by_value<RhsT>::value, \

287315

BinaryExpr<LhsT, RhsT>> { \

288316

return { \

289317

static_cast<bool>( lhs.m_lhs op rhs ), lhs.m_lhs, #op##_sr, rhs }; \

@@ -309,19 +337,23 @@ namespace Catch {

309337

"wrap the expression inside parentheses, or decompose it");

310338

}

311339312-

auto makeUnaryExpr() const -> UnaryExpr<LhsT> {

340+

constexpr auto makeUnaryExpr() const -> UnaryExpr<LhsT> {

313341

return UnaryExpr<LhsT>{ m_lhs };

314342

}

315343

};

316344317345

struct Decomposer {

318-

template<typename T, std::enable_if_t<!std::is_arithmetic<std::remove_reference_t<T>>::value, int> = 0>

319-

friend auto operator <= ( Decomposer &&, T && lhs ) -> ExprLhs<T const&> {

346+

template <typename T,

347+

std::enable_if_t<

348+

!capture_by_value<std::remove_reference_t<T>>::value,

349+

int> = 0>

350+

constexpr friend auto operator <= ( Decomposer &&, T && lhs ) -> ExprLhs<T const&> {

320351

return ExprLhs<const T&>{ lhs };

321352

}

322353323-

template<typename T, std::enable_if_t<std::is_arithmetic<T>::value, int> = 0>

324-

friend auto operator <= ( Decomposer &&, T value ) -> ExprLhs<T> {

354+

template <typename T,

355+

std::enable_if_t<capture_by_value<T>::value, int> = 0>

356+

constexpr friend auto operator <= ( Decomposer &&, T value ) -> ExprLhs<T> {

325357

return ExprLhs<T>{ value };

326358

}

327359

};