Move-enable Catch::optional

This avoids copies in couple places through Catch2, e.g. reporter
spec handling, and moving around `AssertionResult` in `RunContext`.
This commit is contained in:
Martin Hořeňovský 2023-05-06 14:31:29 +02:00
parent d84777c9cb
commit c57b5cdf43
No known key found for this signature in database
GPG Key ID: DE48307B8B0D381A
2 changed files with 76 additions and 18 deletions

View File

@ -8,6 +8,8 @@
#ifndef CATCH_OPTIONAL_HPP_INCLUDED #ifndef CATCH_OPTIONAL_HPP_INCLUDED
#define CATCH_OPTIONAL_HPP_INCLUDED #define CATCH_OPTIONAL_HPP_INCLUDED
#include <catch2/internal/catch_move_and_forward.hpp>
#include <cassert> #include <cassert>
namespace Catch { namespace Catch {
@ -16,35 +18,50 @@ namespace Catch {
template<typename T> template<typename T>
class Optional { class Optional {
public: public:
Optional() : nullableValue( nullptr ) {} Optional(): nullableValue( nullptr ) {}
Optional( T const& _value ) ~Optional() { reset(); }
: nullableValue( new( storage ) T( _value ) )
{}
Optional( Optional const& _other )
: nullableValue( _other ? new( storage ) T( *_other ) : nullptr )
{}
~Optional() { Optional( T const& _value ):
nullableValue( new ( storage ) T( _value ) ) {}
Optional( T&& _value ):
nullableValue( new ( storage ) T( CATCH_MOVE( _value ) ) ) {}
Optional& operator=( T const& _value ) {
reset(); reset();
nullableValue = new ( storage ) T( _value );
return *this;
}
Optional& operator=( T&& _value ) {
reset();
nullableValue = new ( storage ) T( CATCH_MOVE( _value ) );
return *this;
} }
Optional& operator= ( Optional const& _other ) { Optional( Optional const& _other ):
if( &_other != this ) { nullableValue( _other ? new ( storage ) T( *_other ) : nullptr ) {}
Optional( Optional&& _other ):
nullableValue( _other ? new ( storage ) T( CATCH_MOVE( *_other ) )
: nullptr ) {}
Optional& operator=( Optional const& _other ) {
if ( &_other != this ) {
reset(); reset();
if( _other ) if ( _other ) { nullableValue = new ( storage ) T( *_other ); }
nullableValue = new( storage ) T( *_other );
} }
return *this; return *this;
} }
Optional& operator = ( T const& _value ) { Optional& operator=( Optional&& _other ) {
if ( &_other != this ) {
reset(); reset();
nullableValue = new( storage ) T( _value ); if ( _other ) {
nullableValue = new ( storage ) T( CATCH_MOVE( *_other ) );
}
}
return *this; return *this;
} }
void reset() { void reset() {
if( nullableValue ) if ( nullableValue ) { nullableValue->~T(); }
nullableValue->~T();
nullableValue = nullptr; nullableValue = nullptr;
} }
@ -91,7 +108,7 @@ namespace Catch {
} }
private: private:
T *nullableValue; T* nullableValue;
alignas(alignof(T)) char storage[sizeof(T)]; alignas(alignof(T)) char storage[sizeof(T)];
}; };

View File

@ -89,6 +89,47 @@ TEST_CASE("Optional comparison ops", "[optional][approvals]") {
} }
} }
namespace {
struct MoveChecker {
bool has_moved = false;
MoveChecker() = default;
MoveChecker( MoveChecker const& rhs ) = default;
MoveChecker& operator=( MoveChecker const& rhs ) = default;
MoveChecker( MoveChecker&& rhs ) { rhs.has_moved = true; }
MoveChecker& operator=( MoveChecker&& rhs ) {
rhs.has_moved = true;
return *this;
}
};
}
TEST_CASE( "Optional supports move ops", "[optional][approvals]" ) {
using Catch::Optional;
MoveChecker a;
Optional<MoveChecker> opt_A( a );
REQUIRE_FALSE( a.has_moved );
REQUIRE_FALSE( opt_A->has_moved );
SECTION( "Move construction from element" ) {
Optional<MoveChecker> opt_B( CATCH_MOVE( a ) );
REQUIRE( a.has_moved );
}
SECTION( "Move assignment from element" ) {
opt_A = CATCH_MOVE( a );
REQUIRE( a.has_moved );
}
SECTION( "Move construction from optional" ) {
Optional<MoveChecker> opt_B( CATCH_MOVE( opt_A ) );
REQUIRE( opt_A->has_moved );
}
SECTION( "Move assignment from optional" ) {
Optional<MoveChecker> opt_B( opt_A );
REQUIRE_FALSE( opt_A->has_moved );
opt_B = CATCH_MOVE( opt_A );
REQUIRE( opt_A->has_moved );
}
}
TEST_CASE( "Decomposer checks that the argument is 0 when handling " TEST_CASE( "Decomposer checks that the argument is 0 when handling "
"only-0-comparable types", "only-0-comparable types",
"[decomposition][approvals]" ) { "[decomposition][approvals]" ) {