* bricks/brick-bitlevel, bricks/brick-hashset, bricks/brick-shmem, bricks/brick-types: here.
1426 lines
41 KiB
C++
1426 lines
41 KiB
C++
// -*- mode: C++; indent-tabs-mode: nil; c-basic-offset: 4 -*-
|
|
|
|
/*
|
|
* Assorted types, mostly for C++11.
|
|
* - Maybe a = Just a | Nothing (w/ a limited variant for C++98)
|
|
* - Unit: single-valued type (empty structure)
|
|
* - Union: discriminated (tagged) union
|
|
* - StrongEnumFlags
|
|
*/
|
|
|
|
/*
|
|
* (c) 2006, 2014 Petr Ročkai <me@mornfall.net>
|
|
* (c) 2013-2015 Vladimír Štill <xstill@fi.muni.cz>
|
|
*
|
|
* Permission to use, copy, modify, and distribute this software for any
|
|
* purpose with or without fee is hereby granted, provided that the above
|
|
* copyright notice and this permission notice appear in all copies.
|
|
*
|
|
* THE SOFTWARE IS PROVIDED "AS IS" AND THE AUTHOR DISCLAIMS ALL WARRANTIES
|
|
* WITH REGARD TO THIS SOFTWARE INCLUDING ALL IMPLIED WARRANTIES OF
|
|
* MERCHANTABILITY AND FITNESS. IN NO EVENT SHALL THE AUTHOR BE LIABLE FOR
|
|
* ANY SPECIAL, DIRECT, INDIRECT, OR CONSEQUENTIAL DAMAGES OR ANY DAMAGES
|
|
* WHATSOEVER RESULTING FROM LOSS OF USE, DATA OR PROFITS, WHETHER IN AN
|
|
* ACTION OF CONTRACT, NEGLIGENCE OR OTHER TORTIOUS ACTION, ARISING OUT OF
|
|
* OR IN CONNECTION WITH THE USE OR PERFORMANCE OF THIS SOFTWARE.
|
|
*/
|
|
|
|
#include "brick-assert"
|
|
|
|
#include <memory>
|
|
#include <cstring>
|
|
#include <type_traits>
|
|
#include <functional>
|
|
|
|
#ifndef BRICK_TYPES_H
|
|
#define BRICK_TYPES_H
|
|
|
|
#if __cplusplus >= 201103L
|
|
#define CONSTEXPR constexpr
|
|
#else
|
|
#define CONSTEXPR
|
|
#endif
|
|
|
|
#if __cplusplus > 201103L && __GNUC__ != 4 && __GNUC_MINOR__ != 9
|
|
#define CPP1Y_CONSTEXPR constexpr // C++1y
|
|
#else
|
|
#define CPP1Y_CONSTEXPR // C++11
|
|
#endif
|
|
|
|
namespace brick {
|
|
namespace types {
|
|
|
|
struct Unit {
|
|
bool operator<( Unit ) const { return false; }
|
|
bool operator==( Unit ) const { return true; }
|
|
};
|
|
|
|
struct Preferred { CONSTEXPR Preferred() { } };
|
|
struct NotPreferred { CONSTEXPR NotPreferred( Preferred ) {} };
|
|
|
|
template< typename _T >
|
|
struct Witness { using T = _T; };
|
|
|
|
struct Eq { typedef bool IsEq; };
|
|
|
|
template< typename T >
|
|
typename T::IsEq operator!=( const T &a, const T &b ) { return !(a == b); }
|
|
|
|
struct Ord : Eq { typedef bool IsOrd; };
|
|
|
|
template< typename T >
|
|
typename T::IsOrd operator<( const T &a, const T &b ) { return !(b <= a); }
|
|
|
|
template< typename T >
|
|
typename T::IsOrd operator>( const T &a, const T &b ) { return !(a <= b); }
|
|
|
|
template< typename T >
|
|
typename T::IsOrd operator>=( const T &a, const T &b ) { return b <= a; }
|
|
|
|
template< typename T >
|
|
typename T::IsOrd operator==( const T &a, const T &b ) {
|
|
return a <= b && b <= a;
|
|
}
|
|
using Comparable = Ord;
|
|
|
|
struct Defer {
|
|
template< typename F >
|
|
Defer( F fn ) : fn( fn ), _deleted( false ) { }
|
|
|
|
void run() {
|
|
if ( !_deleted ) {
|
|
fn();
|
|
_deleted = true;
|
|
}
|
|
}
|
|
|
|
bool deleted() const { return _deleted; }
|
|
void pass() { _deleted = true; }
|
|
~Defer() { run(); }
|
|
private:
|
|
std::function< void() > fn;
|
|
bool _deleted;
|
|
};
|
|
|
|
namespace mixin {
|
|
|
|
#if __cplusplus >= 201103L
|
|
template< typename Self >
|
|
struct LexComparable {
|
|
const Self &lcSelf() const { return *static_cast< const Self * >( this ); }
|
|
|
|
bool operator==( const Self &o ) const {
|
|
return lcSelf().toTuple() == o.toTuple();
|
|
}
|
|
|
|
bool operator!=( const Self &o ) const {
|
|
return lcSelf().toTuple() != o.toTuple();
|
|
}
|
|
|
|
bool operator<( const Self &o ) const {
|
|
return lcSelf().toTuple() < o.toTuple();
|
|
}
|
|
|
|
bool operator<=( const Self &o ) const {
|
|
return lcSelf().toTuple() <= o.toTuple();
|
|
}
|
|
|
|
bool operator>( const Self &o ) const {
|
|
return lcSelf().toTuple() > o.toTuple();
|
|
}
|
|
|
|
bool operator>=( const Self &o ) const {
|
|
return lcSelf().toTuple() >= o.toTuple();
|
|
}
|
|
};
|
|
#endif
|
|
|
|
}
|
|
|
|
#if __cplusplus < 201103L
|
|
|
|
/*
|
|
A Maybe type. Values of type Maybe< T > can be either Just T or Nothing.
|
|
|
|
Maybe< int > foo;
|
|
foo = Maybe::Nothing();
|
|
// or
|
|
foo = Maybe::Just( 5 );
|
|
if ( !foo.nothing() ) {
|
|
int real = foo;
|
|
} else {
|
|
// we haven't got anythig in foo
|
|
}
|
|
|
|
Maybe takes a default value, which is normally T(). That is what you
|
|
get if you try to use Nothing as T.
|
|
*/
|
|
|
|
template <typename T>
|
|
struct Maybe : Comparable {
|
|
bool nothing() const { return m_nothing; }
|
|
bool isNothing() const { return nothing(); }
|
|
T &value() { return m_value; }
|
|
const T &value() const { return m_value; }
|
|
Maybe( bool n, const T &v ) : m_nothing( n ), m_value( v ) {}
|
|
Maybe( const T &df = T() )
|
|
: m_nothing( true ), m_value( df ) {}
|
|
static Maybe Just( const T &t ) { return Maybe( false, t ); }
|
|
static Maybe Nothing( const T &df = T() ) {
|
|
return Maybe( true, df ); }
|
|
operator T() const { return value(); }
|
|
|
|
bool operator <=( const Maybe< T > &o ) const {
|
|
if (o.nothing())
|
|
return true;
|
|
if (nothing())
|
|
return false;
|
|
return value() <= o.value();
|
|
}
|
|
protected:
|
|
bool m_nothing:1;
|
|
T m_value;
|
|
};
|
|
|
|
#else
|
|
|
|
template< typename T >
|
|
struct StorableRef {
|
|
T _t;
|
|
T &t() { return _t; }
|
|
const T &t() const { return _t; }
|
|
StorableRef( T t ) : _t( t ) {}
|
|
};
|
|
|
|
template< typename T >
|
|
struct StorableRef< T & > {
|
|
T *_t;
|
|
T &t() { return *_t; }
|
|
const T &t() const { return *_t; }
|
|
StorableRef( T &t ) : _t( &t ) {}
|
|
};
|
|
|
|
template< typename _T >
|
|
struct Maybe : Comparable
|
|
{
|
|
using T = _T;
|
|
|
|
bool isNothing() const { return _nothing; }
|
|
bool isJust() const { return !_nothing; }
|
|
|
|
T &value() {
|
|
ASSERT( isJust() );
|
|
return _v.t.t();
|
|
}
|
|
|
|
const T &value() const {
|
|
ASSERT( isJust() );
|
|
return _v.t.t();
|
|
}
|
|
|
|
T fromMaybe( T x ) const { return isJust() ? value() : x; }
|
|
|
|
explicit operator bool() const { return isJust() && bool( value() ); }
|
|
|
|
static Maybe Just( const T &t ) { return Maybe( t ); }
|
|
static Maybe Nothing() { return Maybe(); }
|
|
|
|
Maybe( const Maybe &m ) {
|
|
_nothing = m.isNothing();
|
|
if ( !_nothing )
|
|
_v.t = m._v.t;
|
|
}
|
|
|
|
~Maybe() {
|
|
if ( !_nothing )
|
|
_v.t.~StorableRef< T >();
|
|
}
|
|
|
|
bool operator <=( const Maybe< T > &o ) const {
|
|
if (o.isNothing())
|
|
return true;
|
|
if (isNothing())
|
|
return false;
|
|
return value() <= o.value();
|
|
}
|
|
|
|
protected:
|
|
|
|
Maybe( const T &v ) : _v( v ), _nothing( false ) {}
|
|
Maybe() : _nothing( true ) {}
|
|
struct Empty {
|
|
char x[ sizeof( T ) ];
|
|
};
|
|
|
|
union V {
|
|
StorableRef< T > t;
|
|
Empty empty;
|
|
V() : empty() {}
|
|
V( const T &t ) : t( t ) {}
|
|
~V() { } // see dtor of Maybe
|
|
};
|
|
V _v;
|
|
bool _nothing;
|
|
};
|
|
|
|
#endif
|
|
|
|
template<>
|
|
struct Maybe< void > {
|
|
typedef void T;
|
|
static Maybe Just() { return Maybe( false ); }
|
|
static Maybe Nothing() { return Maybe( true ); }
|
|
bool isNothing() { return _nothing; }
|
|
bool isJust() { return !_nothing; }
|
|
private:
|
|
Maybe( bool nothing ) : _nothing( nothing ) {}
|
|
bool _nothing;
|
|
};
|
|
|
|
#if __cplusplus >= 201103L
|
|
|
|
template< typename E >
|
|
using is_enum_class = std::integral_constant< bool,
|
|
std::is_enum< E >::value && !std::is_convertible< E, int >::value >;
|
|
|
|
template< typename Self >
|
|
struct StrongEnumFlags {
|
|
static_assert( is_enum_class< Self >::value, "Not an enum class." );
|
|
using This = StrongEnumFlags< Self >;
|
|
using UnderlyingType = typename std::underlying_type< Self >::type;
|
|
|
|
constexpr StrongEnumFlags() noexcept : store( 0 ) { }
|
|
constexpr StrongEnumFlags( Self flag ) noexcept :
|
|
store( static_cast< UnderlyingType >( flag ) )
|
|
{ }
|
|
explicit constexpr StrongEnumFlags( UnderlyingType st ) noexcept : store( st ) { }
|
|
|
|
constexpr explicit operator UnderlyingType() const noexcept {
|
|
return store;
|
|
}
|
|
|
|
This &operator|=( This o ) noexcept {
|
|
store |= o.store;
|
|
return *this;
|
|
}
|
|
|
|
This &operator&=( This o ) noexcept {
|
|
store &= o.store;
|
|
return *this;
|
|
}
|
|
|
|
This &operator^=( This o ) noexcept {
|
|
store ^= o.store;
|
|
return *this;
|
|
}
|
|
|
|
friend constexpr This operator|( This a, This b ) noexcept {
|
|
return This( a.store | b.store );
|
|
}
|
|
|
|
friend constexpr This operator&( This a, This b ) noexcept {
|
|
return This( a.store & b.store );
|
|
}
|
|
|
|
friend constexpr This operator^( This a, This b ) noexcept {
|
|
return This( a.store ^ b.store );
|
|
}
|
|
|
|
friend constexpr bool operator==( This a, This b ) noexcept {
|
|
return a.store == b.store;
|
|
}
|
|
|
|
friend constexpr bool operator!=( This a, This b ) noexcept {
|
|
return a.store != b.store;
|
|
}
|
|
|
|
constexpr bool has( Self x ) const noexcept {
|
|
return ((*this) & x) == x;
|
|
}
|
|
|
|
This clear( Self x ) noexcept {
|
|
store &= ~UnderlyingType( x );
|
|
return *this;
|
|
}
|
|
|
|
explicit constexpr operator bool() const noexcept {
|
|
return store;
|
|
}
|
|
|
|
private:
|
|
UnderlyingType store;
|
|
};
|
|
|
|
/* implementation of Union */
|
|
|
|
namespace _impl {
|
|
template< size_t val, typename... >
|
|
struct MaxSizeof : std::integral_constant< size_t, val > { };
|
|
|
|
template< size_t val, typename T, typename... Ts >
|
|
struct MaxSizeof< val, T, Ts... > :
|
|
MaxSizeof< ( val > sizeof( T ) ) ? val : sizeof( T ), Ts... >
|
|
{ };
|
|
|
|
template< size_t val, typename... >
|
|
struct MaxAlign : std::integral_constant< size_t, val > { };
|
|
|
|
template< size_t val, typename T, typename... Ts >
|
|
struct MaxAlign< val, T, Ts... > :
|
|
MaxAlign< ( val > std::alignment_of< T >::value )
|
|
? val : std::alignment_of< T >::value, Ts... >
|
|
{ };
|
|
|
|
template< typename... >
|
|
struct AllDistinct : std::true_type { };
|
|
|
|
template< typename, typename... >
|
|
struct In : std::false_type { };
|
|
|
|
template< typename Needle, typename T, typename... Ts >
|
|
struct In< Needle, T, Ts... > : std::integral_constant< bool,
|
|
std::is_same< Needle, T >::value || In< Needle, Ts... >::value >
|
|
{ };
|
|
|
|
template< typename, typename... >
|
|
struct _OneConversion { };
|
|
|
|
template< typename From, typename To, typename... >
|
|
struct NoneConvertible { using T = To; };
|
|
|
|
template< typename From, typename To, typename T, typename... Ts >
|
|
struct NoneConvertible< From, To, T, Ts... > : std::conditional<
|
|
std::is_convertible< From, T >::value,
|
|
Unit,
|
|
NoneConvertible< From, To, Ts... > >::type { };
|
|
|
|
static_assert( std::is_convertible< Witness< int >, Witness< int > >::value, "is_convertible" );
|
|
|
|
template< typename Needle, typename T, typename... Ts >
|
|
struct _OneConversion< Needle, T, Ts... > : std::conditional<
|
|
std::is_convertible< Needle, T >::value,
|
|
NoneConvertible< Needle, T, Ts... >,
|
|
_OneConversion< Needle, Ts... > >::type { };
|
|
|
|
template< typename Needle, typename... Ts >
|
|
struct OneConversion : std::conditional<
|
|
In< Needle, Ts... >::value,
|
|
Witness< Needle >,
|
|
_OneConversion< Needle, Ts... > >::type { };
|
|
|
|
static_assert( std::is_same< OneConversion< int, int >::T, int >::value, "OneConversion" );
|
|
static_assert( std::is_same< OneConversion< long, int >::T, int >::value, "OneConversion" );
|
|
static_assert( std::is_same< OneConversion< long, std::string, int >::T, int >::value, "OneConversion" );
|
|
static_assert( std::is_same< OneConversion< long, int, long, int >::T, long >::value, "OneConversion" );
|
|
|
|
template< typename T, typename... Ts >
|
|
struct AllDistinct< T, Ts... > : std::integral_constant< bool,
|
|
!In< T, Ts... >::value && AllDistinct< Ts... >::value >
|
|
{ };
|
|
|
|
template< typename F, typename T, typename Fallback, typename Check = bool >
|
|
struct _ApplyResult : Fallback {};
|
|
|
|
template< typename F, typename T, typename Fallback >
|
|
struct _ApplyResult< F, T, Fallback, decltype( std::declval< F >()( std::declval< T& >() ), true ) >
|
|
{
|
|
using Parameter = T;
|
|
using Result = decltype( std::declval< F >()( std::declval< T& >() ) );
|
|
};
|
|
|
|
template< typename F, typename... Ts > struct ApplyResult;
|
|
|
|
template< typename F, typename T, typename... Ts >
|
|
struct ApplyResult< F, T, Ts... > : _ApplyResult< F, T, ApplyResult< F, Ts... > > {};
|
|
|
|
template< typename F > struct ApplyResult< F > {};
|
|
|
|
}
|
|
|
|
struct UnionException : std::exception {
|
|
UnionException( std::string msg ) : msg( msg ) { }
|
|
|
|
virtual const char *what() const noexcept override { return msg.c_str(); }
|
|
|
|
std::string msg;
|
|
};
|
|
|
|
template< typename T >
|
|
struct InPlace { };
|
|
|
|
struct NullUnion { };
|
|
|
|
template< typename... Types >
|
|
struct Union : Comparable {
|
|
static_assert( sizeof...( Types ) < 0xff, "Too much unioned types, sorry" );
|
|
static_assert( _impl::AllDistinct< Types... >::value,
|
|
"All types in union must be distinct" );
|
|
|
|
constexpr Union() : _discriminator( 0 ) { }
|
|
constexpr Union( NullUnion ) : _discriminator( 0 ) { }
|
|
|
|
Union( const Union &other ) {
|
|
ASSERT_LEQ( size_t( other._discriminator ), sizeof...( Types ) );
|
|
if ( other._discriminator > 0 )
|
|
_copyConstruct< 1, Types... >( other._discriminator, other );
|
|
_discriminator = other._discriminator;
|
|
}
|
|
|
|
Union( Union &&other ) {
|
|
ASSERT_LEQ( size_t( other._discriminator ), sizeof...( Types ) );
|
|
auto target = other._discriminator;
|
|
other._discriminator = 0;
|
|
if ( target > 0 )
|
|
_moveConstruct< 1, Types... >( target, std::move( other ) );
|
|
_discriminator = target;
|
|
}
|
|
|
|
template< typename T, typename U = typename _impl::OneConversion< T, Types... >::T >
|
|
CPP1Y_CONSTEXPR Union( T val ) {
|
|
new ( &storage ) U( std::move( val ) );
|
|
_discriminator = discriminator< U >();
|
|
}
|
|
|
|
template< typename T, typename... Args >
|
|
Union( InPlace< T >, Args &&... args ) : _discriminator( discriminator< T >() ) {
|
|
new ( &storage ) T( std::forward< Args >( args )... );
|
|
}
|
|
|
|
// use copy and swap
|
|
Union &operator=( Union other ) {
|
|
swap( other );
|
|
return *this;
|
|
}
|
|
|
|
~Union() {
|
|
if ( _discriminator )
|
|
_destruct< 1, Types... >( _discriminator );
|
|
}
|
|
|
|
template< typename T >
|
|
auto operator=( const T &other ) -> typename
|
|
std::enable_if< std::is_lvalue_reference< T & >::value, Union & >::type
|
|
{
|
|
if ( is< T >() )
|
|
unsafeGet< T >() = other;
|
|
else
|
|
_copyAssignDifferent( Union( other ) );
|
|
return *this;
|
|
}
|
|
|
|
template< typename T >
|
|
auto operator=( T &&other ) -> typename
|
|
std::enable_if< std::is_rvalue_reference< T && >::value, Union & >::type
|
|
{
|
|
if ( is< T >() )
|
|
unsafeGet< T >() = std::move( other );
|
|
else
|
|
_moveAssignDifferent( std::move( other ) );
|
|
return *this;
|
|
}
|
|
|
|
void swap( Union &other ) {
|
|
if ( _discriminator == 0 && other._discriminator == 0 )
|
|
return;
|
|
|
|
if ( _discriminator == other._discriminator )
|
|
_swapSame< 1, Types... >( other );
|
|
else
|
|
_swapDifferent< 0, void, Types... >( other );
|
|
}
|
|
|
|
bool empty() const {
|
|
return _discriminator == 0;
|
|
}
|
|
|
|
template< typename T >
|
|
explicit operator bool() const
|
|
{
|
|
auto rv = const_cast< Union* >( this )->apply( []( const T & x ) -> bool { return !!x; } );
|
|
if ( rv.isNothing() )
|
|
return false;
|
|
return true;
|
|
}
|
|
|
|
template< typename T >
|
|
bool is() const {
|
|
return discriminator< T >() == _discriminator;
|
|
}
|
|
|
|
template< typename T >
|
|
explicit operator T() const {
|
|
return convert< T >();
|
|
}
|
|
|
|
template< typename T >
|
|
T &get() {
|
|
ASSERT( is< T >() );
|
|
return unsafeGet< T >();
|
|
}
|
|
|
|
template< typename T >
|
|
const T &get() const {
|
|
return cget< T >();
|
|
}
|
|
|
|
template< typename T >
|
|
const T &cget() const {
|
|
ASSERT( is< T >() );
|
|
return unsafeGet< T >();
|
|
}
|
|
|
|
template< typename T >
|
|
T *asptr() { return is< T >() ? &get< T >() : nullptr; }
|
|
|
|
template< typename T >
|
|
const T *asptr() const { return is< T >() ? &get< T >() : nullptr; }
|
|
|
|
template< typename T >
|
|
const T &getOr( const T &val ) const {
|
|
if ( is< T >() )
|
|
return unsafeGet< T >();
|
|
return val;
|
|
}
|
|
|
|
template< typename T >
|
|
T convert() const { return _convert< T >(); }
|
|
|
|
template< typename T >
|
|
T &unsafeGet() {
|
|
return *reinterpret_cast< T * >( &storage );
|
|
}
|
|
|
|
template< typename T >
|
|
const T &unsafeGet() const {
|
|
return *reinterpret_cast< const T * >( &storage );
|
|
}
|
|
|
|
template< typename T >
|
|
T &&moveOut() {
|
|
ASSERT( is< T >() );
|
|
return unsafeMoveOut< T >();
|
|
}
|
|
|
|
template< typename T >
|
|
T &&unsafeMoveOut() {
|
|
return std::move( *reinterpret_cast< T * >( &storage ) );
|
|
}
|
|
|
|
template< typename F >
|
|
using Applied = Maybe< typename _impl::ApplyResult< F, Types... >::Result >;
|
|
|
|
// invoke `f` on the stored value if the type currently stored in the union
|
|
// can be legally passed to that function as an argument
|
|
template< typename F >
|
|
auto apply( F f ) -> Applied< F > {
|
|
return _apply< F, Types... >( Preferred(), f );
|
|
}
|
|
|
|
template< typename R >
|
|
R _match() { return R::Nothing(); }
|
|
|
|
// invoke the first function that can handle the currently stored value
|
|
// (type-based pattern matching)
|
|
template< typename R, typename F, typename... Args >
|
|
R _match( F f, Args&&... args ) {
|
|
auto x = apply( f );
|
|
if ( x.isNothing() )
|
|
return _match< R >( args... );
|
|
else
|
|
return x;
|
|
}
|
|
|
|
// invoke the first function that can handle the currently stored value
|
|
// (type-based pattern matching)
|
|
// * return value can be extracted from resuling Maybe value
|
|
// * auto lambdas are supported an can be called on any value!
|
|
template< typename F, typename... Args >
|
|
Applied< F > match( F f, Args&&... args ) {
|
|
return _match< Applied< F > >( f, args... );
|
|
}
|
|
|
|
bool operator==( const Union &other ) const {
|
|
return _discriminator == other._discriminator
|
|
&& (_discriminator == 0 || _compare< std::equal_to >( other ));
|
|
}
|
|
|
|
bool operator<( const Union &other ) const {
|
|
return _discriminator < other._discriminator
|
|
|| (_discriminator == other._discriminator
|
|
&& (_discriminator == 0 || _compare< std::less >( other )) );
|
|
}
|
|
|
|
unsigned char discriminator() const { return _discriminator; }
|
|
|
|
template< typename T >
|
|
unsigned char discriminator() const {
|
|
static_assert( _impl::In< T, Types... >::value,
|
|
"Trying to construct Union from value of type not allowed for it." );
|
|
return _discriminatorF< 1, T, Types... >();
|
|
}
|
|
|
|
private:
|
|
static constexpr size_t size = _impl::MaxSizeof< 1, Types... >::value;
|
|
static constexpr size_t alignment = _impl::MaxAlign< 1, Types... >::value;
|
|
typename std::aligned_storage< size, alignment >::type storage;
|
|
unsigned char _discriminator;
|
|
|
|
template< unsigned char i, typename Needle, typename T, typename... Ts >
|
|
constexpr unsigned char _discriminatorF() const {
|
|
return std::is_same< Needle, T >::value
|
|
? i : _discriminatorF< i + 1, Needle, Ts... >();
|
|
}
|
|
|
|
template< unsigned char, typename >
|
|
constexpr unsigned char _discriminatorF() const { return 0; /* cannot happen */ }
|
|
|
|
template< unsigned char i, typename T, typename... Ts >
|
|
void _copyConstruct( unsigned char d, const Union &other ) {
|
|
if ( i == d )
|
|
new ( &storage ) T( other.unsafeGet< T >() );
|
|
else
|
|
_copyConstruct< i + 1, Ts... >( d, other );
|
|
}
|
|
|
|
template< unsigned char >
|
|
unsigned char _copyConstruct( unsigned char, const Union & )
|
|
{ UNREACHABLE( "invalid _copyConstruct" ); return 0; }
|
|
|
|
template< unsigned char i, typename T, typename... Ts >
|
|
void _moveConstruct( unsigned char d, Union &&other ) {
|
|
if ( i == d )
|
|
new ( &storage ) T( other.unsafeMoveOut< T >() );
|
|
else
|
|
_moveConstruct< i + 1, Ts... >( d, std::move( other ) );
|
|
}
|
|
|
|
template< unsigned char >
|
|
unsigned char _moveConstruct( unsigned char, Union && )
|
|
{ UNREACHABLE( "invalid _moveConstruct" ); return 0; }
|
|
|
|
void _copyAssignDifferent( const Union &other ) {
|
|
auto tmp = _discriminator;
|
|
_discriminator = 0;
|
|
if ( tmp )
|
|
_destruct< 1, Types... >( tmp );
|
|
if ( other._discriminator )
|
|
_copyConstruct< 1, Types... >( other._discriminator, other );
|
|
_discriminator = other._discriminator;
|
|
}
|
|
|
|
void _copyAssignSame( const Union &other ) {
|
|
ASSERT_EQ( _discriminator, other._discriminator );
|
|
if ( _discriminator == 0 )
|
|
return;
|
|
_copyAssignSame< 1, Types... >( other );
|
|
}
|
|
|
|
template< unsigned char i, typename T, typename... Ts >
|
|
void _copyAssignSame( const Union &other ) {
|
|
if ( i == _discriminator )
|
|
unsafeGet< T >() = other.unsafeGet< T >();
|
|
else
|
|
_copyAssignSame< i + 1, Ts... >( other );
|
|
}
|
|
|
|
template< unsigned char >
|
|
void _copyAssignSame( const Union & ) { UNREACHABLE( "invalid _copyAssignSame" ); }
|
|
|
|
template< unsigned char i, typename T, typename... Ts >
|
|
void _destruct( unsigned char d ) {
|
|
if ( i == d )
|
|
unsafeGet< T >().~T();
|
|
else
|
|
_destruct< i + 1, Ts... >( d );
|
|
}
|
|
|
|
template< unsigned char >
|
|
void _destruct( unsigned char ) { UNREACHABLE( "invalid _destruct" ); }
|
|
|
|
void _moveAssignSame( Union &&other ) {
|
|
ASSERT_EQ( _discriminator, other._discriminator );
|
|
if ( _discriminator == 0 )
|
|
return;
|
|
_moveAssignSame< 1, Types... >( std::move( other ) );
|
|
}
|
|
|
|
template< unsigned char i, typename T, typename... Ts >
|
|
void _moveAssignSame( Union &&other ) {
|
|
if ( i == _discriminator )
|
|
unsafeGet< T >() = other.unsafeMoveOut< T >();
|
|
else
|
|
_moveAssignSame< i + 1, Ts... >( std::move( other ) );
|
|
}
|
|
|
|
template< unsigned char >
|
|
void _moveAssignSame( Union && ) { UNREACHABLE( "invalid _moveAssignSame" ); }
|
|
|
|
void _moveAssignDifferent( Union &&other ) {
|
|
auto tmp = _discriminator;
|
|
auto target = other._discriminator;
|
|
_discriminator = 0;
|
|
if ( tmp )
|
|
_destruct< 1, Types... >( tmp );
|
|
if ( target )
|
|
_moveConstruct< 1, Types... >( target, std::move( other ) );
|
|
_discriminator = target;
|
|
}
|
|
|
|
template< typename F > Applied< F > _apply( Preferred, F ) { return Applied< F >::Nothing(); }
|
|
|
|
template< typename F, typename T >
|
|
auto fixvoid( F f ) ->
|
|
typename std::enable_if< std::is_void< typename Applied< F >::T >::value, Applied< F > >::type
|
|
{
|
|
f( get< T >() );
|
|
return Maybe< void >::Just();
|
|
}
|
|
|
|
template< typename F, typename T >
|
|
auto fixvoid( F f ) ->
|
|
typename std::enable_if< !std::is_void< typename Applied< F >::T >::value, Applied< F > >::type
|
|
{
|
|
return Applied< F >::Just( f( get< T >() ) );
|
|
}
|
|
|
|
template< typename F, typename T, typename... Args >
|
|
auto _apply( Preferred, F f ) -> Maybe< typename _impl::_ApplyResult< F, T, Unit >::Result >
|
|
{
|
|
if ( !is< T >() )
|
|
return _apply< F, Args... >( Preferred(), f );
|
|
|
|
return fixvoid< F, T >( f );
|
|
}
|
|
|
|
template< typename F, typename T, typename... Args >
|
|
auto _apply( NotPreferred, F f ) -> Applied< F >
|
|
{
|
|
return _apply< F, Args... >( Preferred(), f );
|
|
}
|
|
|
|
template< template< typename > class Compare, int d >
|
|
bool _compare2( const Union & ) const { UNREACHABLE( "invalid discriminator" ); return false;}
|
|
|
|
template< template< typename > class Compare, int d, typename T, typename... Ts >
|
|
bool _compare2( const Union &other ) const {
|
|
return d == _discriminator
|
|
? Compare< T >()( get< T >(), other.template get< T >() )
|
|
: _compare2< Compare, d + 1, Ts... >( other );
|
|
}
|
|
|
|
template< template< typename > class Compare >
|
|
bool _compare( const Union &other ) const {
|
|
return _compare2< Compare, 1, Types... >( other );
|
|
}
|
|
|
|
template< typename Target, bool anyCastPossible, int >
|
|
Target _convert2( Preferred ) const {
|
|
static_assert( anyCastPossible, "Cast of Union can never succeed" );
|
|
UNREACHABLE( "wrong _convert2 in Union" );
|
|
}
|
|
|
|
template< typename Target, bool any, int d, typename, typename... Ts >
|
|
Target _convert2( NotPreferred ) const {
|
|
return _convert2< Target, any, d + 1, Ts... >( Preferred() );
|
|
}
|
|
|
|
template< typename Target, bool any, int d, typename T, typename... Ts >
|
|
auto _convert2( Preferred ) const -> decltype( static_cast< Target >( this->unsafeGet< T >() ) )
|
|
{
|
|
if ( _discriminator == d )
|
|
return static_cast< Target >( unsafeGet< T >() );
|
|
return _convert2< Target, true, d + 1, Ts... >( Preferred() );
|
|
}
|
|
|
|
template< typename Target >
|
|
Target _convert() const {
|
|
return _convert2< Target, false, 1, Types... >( Preferred() );
|
|
}
|
|
|
|
template< unsigned char i, typename T, typename... Ts >
|
|
void _swapSame( Union &other ) {
|
|
if ( _discriminator == i )
|
|
_doSwap< T >( unsafeGet< T >(), other.unsafeGet< T >(), Preferred() );
|
|
else
|
|
_swapSame< i + 1, Ts... >( other );
|
|
}
|
|
|
|
template< unsigned char i >
|
|
void _swapSame( Union & ) { UNREACHABLE( "Invalid _swapSame" ); }
|
|
|
|
template< typename T >
|
|
auto _doSwap( T &a, T &b, Preferred ) -> decltype( a.swap( b ) ) {
|
|
a.swap( b );
|
|
}
|
|
|
|
template< typename T >
|
|
auto _doSwap( T &a, T &b, NotPreferred ) -> decltype( std::swap( a, b ) ) {
|
|
std::swap( a, b );
|
|
}
|
|
|
|
template< unsigned char i, typename T, typename... Ts >
|
|
void _swapDifferent( Union &other ) {
|
|
if ( i == _discriminator )
|
|
_swapDifferent2< i, T, 0, void, Types... >( other );
|
|
else
|
|
_swapDifferent< i + 1, Ts... >( other );
|
|
}
|
|
|
|
template< unsigned char i >
|
|
void _swapDifferent( Union & ) { UNREACHABLE( "Invalid _swapDifferent" ); }
|
|
|
|
template< unsigned char local, typename Local, unsigned char i, typename T, typename... Ts >
|
|
void _swapDifferent2( Union &other ) {
|
|
if ( i == other._discriminator )
|
|
_doSwapDifferent< local, i, Local, T >( other );
|
|
else
|
|
_swapDifferent2< local, Local, i + 1, Ts... >( other );
|
|
}
|
|
|
|
template< unsigned char local, typename Local, unsigned char i >
|
|
void _swapDifferent2( Union & ) { UNREACHABLE( "Invalid _swapDifferent2" ); }
|
|
|
|
template< unsigned char l, unsigned char r, typename L, typename R >
|
|
auto _doSwapDifferent( Union &other ) -> typename std::enable_if< l != 0 && r != 0 >::type {
|
|
L lval( unsafeMoveOut< L >() );
|
|
unsafeGet< L >().~L();
|
|
|
|
new ( &unsafeGet< R >() ) R( other.unsafeMoveOut< R >() );
|
|
other.unsafeGet< R >().~R();
|
|
|
|
new ( &other.unsafeGet< L >() ) L( std::move( lval ) );
|
|
std::swap( _discriminator, other._discriminator );
|
|
}
|
|
|
|
template< unsigned char l, unsigned char r, typename L, typename R >
|
|
auto _doSwapDifferent( Union &other ) -> typename std::enable_if< l == 0 && r != 0 >::type {
|
|
new ( &unsafeGet< R >() ) R( other.unsafeMoveOut< R >() );
|
|
other.unsafeGet< R >().~R();
|
|
std::swap( _discriminator, other._discriminator );
|
|
}
|
|
|
|
template< unsigned char l, unsigned char r, typename L, typename R >
|
|
auto _doSwapDifferent( Union &other ) -> typename std::enable_if< l != 0 && r == 0 >::type {
|
|
new ( &other.unsafeGet< L >() ) L( unsafeMoveOut< L >() );
|
|
unsafeGet< L >().~L();
|
|
std::swap( _discriminator, other._discriminator );
|
|
}
|
|
|
|
template< unsigned char l, unsigned char r, typename L, typename R >
|
|
auto _doSwapDifferent( Union & ) -> typename std::enable_if< l == 0 && r == 0 >::type {
|
|
UNREACHABLE( "Invalid _doSwapDifferent" );
|
|
}
|
|
};
|
|
|
|
template< typename Left, typename Right >
|
|
struct Either : Union< Left, Right > {
|
|
|
|
using Union< Left, Right >::Union;
|
|
|
|
bool isLeft() const { return this->template is< Left >(); }
|
|
bool isRight() const { return this->template is< Right >(); }
|
|
|
|
Left &left() { return this->template get< Left >(); }
|
|
Right &right() { return this->template get< Right >(); }
|
|
|
|
const Left &left() const { return this->template get< Left >(); }
|
|
const Right &right() const { return this->template get< Right >(); }
|
|
};
|
|
|
|
// a pointer-like structure which can, however store values a value or a
|
|
// referrence to type T
|
|
template< typename T >
|
|
struct RefOrVal {
|
|
static_assert( !std::is_reference< T >::value, "T must not be a reference type" );
|
|
|
|
RefOrVal() : _store( InPlace< T >() ) { }
|
|
RefOrVal( T &&val ) : _store( std::forward< T >( val ) ) { }
|
|
RefOrVal( T *ref ) : _store( ref ) { }
|
|
RefOrVal( T &ref ) : _store( &ref ) { }
|
|
|
|
RefOrVal &operator=( const RefOrVal & ) = default;
|
|
RefOrVal &operator=( RefOrVal && ) = default;
|
|
RefOrVal &operator=( T &v ) { _store = v; return *this; }
|
|
RefOrVal &operator=( T &&v ) { _store = std::move( v ); return *this; }
|
|
RefOrVal &operator=( T *ptr ) { _store = ptr; return *this; }
|
|
|
|
T *ptr() {
|
|
ASSERT( !_store.empty() );
|
|
auto *val = _store.template asptr< T >();
|
|
return val ? val : _store.template get< T * >();
|
|
}
|
|
const T *ptr() const {
|
|
ASSERT( !_store.empty() );
|
|
const auto *val = _store.template asptr< T >();
|
|
return val ? val : _store.template get< T * >();
|
|
}
|
|
|
|
T *operator->() { return ptr(); }
|
|
T &operator*() { return *ptr(); }
|
|
const T *operator->() const { return ptr(); }
|
|
const T &operator*() const { return *ptr(); }
|
|
|
|
private:
|
|
Union< T, T * > _store;
|
|
};
|
|
|
|
template< typename Fn, typename R = typename std::result_of< Fn() >::type >
|
|
struct Lazy {
|
|
|
|
Lazy( Fn &&fn ) : _fn( std::forward< Fn >( fn ) ), _val() { }
|
|
|
|
R &get() {
|
|
if ( _val.empty() )
|
|
_val = _fn();
|
|
return _val.template get< R >();
|
|
}
|
|
|
|
R &operator*() { return get(); }
|
|
R *operator->() { return &get(); }
|
|
|
|
private:
|
|
Fn _fn;
|
|
Union< R > _val;
|
|
};
|
|
|
|
template< typename Fn, typename R = typename std::result_of< Fn() >::type >
|
|
Lazy< Fn, R > lazy( Fn &&fn ) { return Lazy< Fn, R >( std::forward< Fn >( fn ) ); }
|
|
|
|
template< template< typename > class C, typename T, typename F >
|
|
using FMap = C< typename std::result_of< F( T ) >::type >;
|
|
|
|
template< typename T >
|
|
struct NewType
|
|
{
|
|
T _value;
|
|
|
|
template< typename X > using FMap = NewType< X >;
|
|
NewType() noexcept {}
|
|
NewType( const T &t ) noexcept : _value( t ) {}
|
|
|
|
T &unwrap() { return _value; }
|
|
const T &unwrap() const { return _value; }
|
|
};
|
|
|
|
template< typename T >
|
|
struct Wrapper : NewType< T >
|
|
{
|
|
Wrapper() = default;
|
|
Wrapper( const T &t ) : NewType< T >( t ) {}
|
|
operator T() { return this->unwrap(); }
|
|
T &value() { return this->unwrap(); }
|
|
T &operator*() { return this->unwrap(); }
|
|
T *operator->() { return &this->unwrap(); }
|
|
};
|
|
|
|
template< template< typename > class C, typename S, typename F >
|
|
auto fmap( F, C< S > n ) -> decltype( FMap< C, S, F >( n.unwrap() ) ) {
|
|
return FMap< C, S, F >( n.unwrap() );
|
|
}
|
|
|
|
template< typename T >
|
|
struct IsUnion : std::false_type { };
|
|
|
|
template< typename... Ts >
|
|
struct IsUnion< Union< Ts... > > : std::true_type { };
|
|
|
|
template< typename A, typename B >
|
|
struct _OneUnion : std::enable_if<
|
|
( IsUnion< A >::value || IsUnion< B >::value )
|
|
&& !(IsUnion< A >::value && IsUnion< B >::value ),
|
|
bool > { };
|
|
|
|
template< typename A, typename B >
|
|
auto operator==( const A &a, const B &b ) ->
|
|
typename std::enable_if< IsUnion< A >::value && !IsUnion< B >::value, bool >::type
|
|
{
|
|
return a.template is< B >() && a.template get< B >() == b;
|
|
}
|
|
|
|
template< typename A, typename B >
|
|
auto operator==( const A &a, const B &b ) ->
|
|
typename std::enable_if< !IsUnion< A >::value && IsUnion< B >::value, bool >::type
|
|
{ return b == a; }
|
|
|
|
|
|
template< typename A, typename B >
|
|
auto operator<( const A &a, const B &b ) ->
|
|
typename std::enable_if< IsUnion< A >::value && !IsUnion< B >::value, bool >::type
|
|
{
|
|
return a.discriminator() < a.template discriminator< B >()
|
|
|| (a.template is< B >() && a.template get< B >() < b);
|
|
}
|
|
|
|
template< typename A, typename B >
|
|
auto operator<( const A &a, const B &b ) ->
|
|
typename std::enable_if< !IsUnion< A >::value && IsUnion< B >::value, bool >::type
|
|
{
|
|
return b.template discriminator< A >() < b.discriminator()
|
|
|| (b.template is< A >() && a < b.template get< A >());
|
|
}
|
|
|
|
template< typename A, typename B >
|
|
auto operator!=( const A &a, const B &b ) -> typename _OneUnion< A, B >::type
|
|
{ return !(a == b); }
|
|
|
|
template< typename A, typename B >
|
|
auto operator<=( const A &a, const B &b ) -> typename _OneUnion< A, B >::type
|
|
{ return a < b || a == b; }
|
|
|
|
template< typename A, typename B >
|
|
auto operator>( const A &a, const B &b ) -> typename _OneUnion< A, B >::type
|
|
{ return b < a; }
|
|
|
|
template< typename A, typename B >
|
|
auto operator>=( const A &a, const B &b ) -> typename _OneUnion< A, B >::type
|
|
{ return b <= a; }
|
|
|
|
#endif // C++11
|
|
|
|
}
|
|
}
|
|
|
|
// don't catch integral types and classical enum!
|
|
template< typename Self, typename = typename
|
|
std::enable_if< brick::types::is_enum_class< Self >::value >::type >
|
|
constexpr brick::types::StrongEnumFlags< Self > operator|( Self a, Self b ) noexcept {
|
|
using Ret = brick::types::StrongEnumFlags< Self >;
|
|
return Ret( a ) | Ret( b );
|
|
}
|
|
|
|
template< typename Self, typename = typename
|
|
std::enable_if< brick::types::is_enum_class< Self >::value >::type >
|
|
constexpr brick::types::StrongEnumFlags< Self > operator&( Self a, Self b ) noexcept {
|
|
using Ret = brick::types::StrongEnumFlags< Self >;
|
|
return Ret( a ) & Ret( b );
|
|
}
|
|
|
|
namespace brick {
|
|
namespace t_types {
|
|
|
|
using namespace types;
|
|
|
|
struct Integer : Comparable
|
|
{
|
|
int val;
|
|
public:
|
|
Integer(int val) : val(val) {}
|
|
bool operator<=( const Integer& o ) const { return val <= o.val; }
|
|
};
|
|
|
|
struct IntegerEq : Eq {
|
|
int val;
|
|
public:
|
|
IntegerEq(int val) : val(val) {}
|
|
bool operator==( const IntegerEq& o ) const { return val == o.val; }
|
|
};
|
|
|
|
struct IntegerEqOrd : Ord {
|
|
int val;
|
|
public:
|
|
IntegerEqOrd(int val) : val(val) {}
|
|
bool operator==( const IntegerEqOrd& o ) const { return val == o.val; }
|
|
bool operator<=( const IntegerEqOrd& o ) const { return val <= o.val; }
|
|
};
|
|
|
|
struct IntegerOrd : Ord {
|
|
int val;
|
|
public:
|
|
IntegerOrd(int val) : val(val) {}
|
|
bool operator<=( const IntegerOrd& o ) const { return val <= o.val; }
|
|
};
|
|
|
|
struct Mixins {
|
|
|
|
template< typename T >
|
|
void eq() {
|
|
T i10(10);
|
|
T i10a(10);
|
|
T i20(20);
|
|
|
|
ASSERT(i10 != i20);
|
|
ASSERT(!(i10 != i10a));
|
|
|
|
ASSERT(i10 == i10a);
|
|
ASSERT(!(i10 == i20));
|
|
}
|
|
|
|
template< typename T >
|
|
void ord() {
|
|
T i10(10);
|
|
T i10a(10);
|
|
T i20(20);
|
|
|
|
ASSERT(i10 <= i10a);
|
|
ASSERT(i10a <= i10);
|
|
ASSERT(i10 <= i20);
|
|
ASSERT(! (i20 <= i10));
|
|
|
|
ASSERT(i10 < i20);
|
|
ASSERT(!(i20 < i10));
|
|
ASSERT(!(i10 < i10a));
|
|
|
|
ASSERT(i20 > i10);
|
|
ASSERT(!(i10 > i20));
|
|
ASSERT(!(i10 > i10a));
|
|
|
|
ASSERT(i10 >= i10a);
|
|
ASSERT(i10a >= i10);
|
|
ASSERT(i20 >= i10);
|
|
ASSERT(! (i10 >= i20));
|
|
}
|
|
|
|
TEST(comparable) {
|
|
eq< Integer >();
|
|
ord< Integer >();
|
|
}
|
|
|
|
TEST(eq) {
|
|
eq< IntegerEq >();
|
|
}
|
|
|
|
TEST(ord) {
|
|
eq< IntegerOrd >();
|
|
ord< IntegerOrd >();
|
|
}
|
|
|
|
TEST(eqord) {
|
|
eq< IntegerEqOrd >();
|
|
ord< IntegerEqOrd >();
|
|
}
|
|
|
|
};
|
|
|
|
#if __cplusplus >= 201103L
|
|
|
|
struct A { };
|
|
struct B { B() { }; ~B() { } };
|
|
struct C { int x; C( int x ) : x( x ) {} C() : x( 0 ) {} };
|
|
|
|
static_assert( _impl::In< int, int >::value, "" );
|
|
static_assert( _impl::In< A, A, B >::value, "" );
|
|
static_assert( _impl::In< A, B, A >::value, "" );
|
|
|
|
// test instances
|
|
struct UnionInstances {
|
|
Union<> a;
|
|
Union< int, long > b;
|
|
Union< int, long, A > c;
|
|
Union< int, long, A, B > d;
|
|
Union< int, long, A, B, std::string > e;
|
|
};
|
|
|
|
struct UnionTest {
|
|
TEST(basic) {
|
|
Union< int > u( 1 );
|
|
ASSERT( !u.empty() );
|
|
ASSERT( u.is< int >() );
|
|
ASSERT_EQ( u.get< int >(), 1 );
|
|
u = 2; // move
|
|
ASSERT( !u.empty() );
|
|
ASSERT_EQ( u.get< int >(), 2 );
|
|
int i = 3;
|
|
u = i; // copy
|
|
ASSERT( !u.empty() );
|
|
ASSERT_EQ( u.get< int >(), 3 );
|
|
u = types::Union< int >( 4 );
|
|
ASSERT( u.is< int >() );
|
|
ASSERT_EQ( u.get< int >(), 4 );
|
|
u = types::Union< int >();
|
|
ASSERT( u.empty() );
|
|
ASSERT( !u.is< int >() );
|
|
u = 5;
|
|
ASSERT( !u.empty() );
|
|
ASSERT( u.is< int >() );
|
|
ASSERT_EQ( u.get< int >(), 5 );
|
|
}
|
|
|
|
TEST(moveNoCopy) {
|
|
// if one of contained structures does not define copy ctor+assignment
|
|
// move should still be available
|
|
struct Move {
|
|
Move() = default;
|
|
Move( const Move & ) = delete;
|
|
Move( Move && ) = default;
|
|
|
|
Move &operator=( Move ) { return *this; }
|
|
};
|
|
Union< long, Move > wierd;
|
|
ASSERT( wierd.empty() );
|
|
|
|
wierd = 2L;
|
|
ASSERT( !wierd.empty() );
|
|
ASSERT( wierd.is< long >() );
|
|
ASSERT_EQ( wierd.get< long >(), 2L );
|
|
|
|
wierd = Move();
|
|
ASSERT( !wierd.empty() );
|
|
ASSERT( wierd.is< Move >() );
|
|
}
|
|
|
|
TEST(ctorCast) {
|
|
ASSERT( ( Union< int, long >{ int( 1 ) }.is< int >() ) );
|
|
ASSERT( ( Union< int, long >{ long( 1 ) }.is< long >() ) );
|
|
|
|
ASSERT( ( Union< long, std::string >{ int( 1 ) }.is< long >() ) );
|
|
|
|
struct A { operator int(){ return 1; } };
|
|
ASSERT( ( Union< int, A >{ A() }.is< A >() ) );
|
|
ASSERT( ( Union< int, std::string >{ A() }.is< int >() ) );
|
|
|
|
struct B { B( int ) { } B() = default; };
|
|
ASSERT( ( Union< int, B >{ B() }.is< B >() ) );
|
|
ASSERT( ( Union< int, B >{ 1 }.is< int >() ) );
|
|
ASSERT( ( Union< B, std::string >{ 1 }.is< B >() ) );
|
|
}
|
|
|
|
static C idC( C c ) { return c; }
|
|
static C constC( B ) { return C( 32 ); }
|
|
static C refC( C &c ) { return c; }
|
|
|
|
TEST(apply) {
|
|
Union< B, C > u;
|
|
u = B();
|
|
|
|
Maybe< C > result = u.match( idC, constC );
|
|
ASSERT( !result.isNothing() );
|
|
ASSERT_EQ( result.value().x, 32 );
|
|
|
|
u = C( 12 );
|
|
result = u.match( idC, constC );
|
|
ASSERT( !result.isNothing() );
|
|
ASSERT_EQ( result.value().x, 12 );
|
|
|
|
result = u.match( constC );
|
|
ASSERT( result.isNothing() );
|
|
|
|
result = u.match( refC );
|
|
ASSERT_EQ( result.value().x, 12 );
|
|
}
|
|
|
|
TEST(eq) {
|
|
Union< int, long > u{ 1 };
|
|
Union< int, long > v{ 2 };
|
|
Union< int, long > w{ 2l };
|
|
|
|
ASSERT( u == u );
|
|
ASSERT( u != v );
|
|
ASSERT( v != w );
|
|
ASSERT( u != w );
|
|
|
|
ASSERT( u == 1 );
|
|
ASSERT( v == 2 );
|
|
ASSERT( w == 2l );
|
|
|
|
ASSERT( u != 1l );
|
|
ASSERT( v != 2l );
|
|
ASSERT( w != 2 );
|
|
}
|
|
|
|
TEST(ord) {
|
|
Union< int, long > u{ 1 };
|
|
Union< int, long > v{ 2 };
|
|
Union< int, long > w{ 2l };
|
|
|
|
ASSERT( u < v );
|
|
ASSERT( !(v < u) );
|
|
ASSERT( u < w );
|
|
ASSERT( !(w < u) );
|
|
ASSERT( v < w );
|
|
ASSERT( !(w < v) );
|
|
|
|
ASSERT( u <= 1 );
|
|
ASSERT( v > 1 );
|
|
ASSERT( w > 1 );
|
|
|
|
ASSERT( u < 1l );
|
|
ASSERT( v < 1l );
|
|
ASSERT( w > 1l );
|
|
|
|
ASSERT( u < 2 );
|
|
ASSERT( v <= 2 );
|
|
ASSERT( w > 2 );
|
|
|
|
ASSERT( u < 2l );
|
|
ASSERT( v < 2l );
|
|
ASSERT( w <= 2l );
|
|
}
|
|
|
|
struct TrackDtor {
|
|
TrackDtor( int *cnt ) : cnt( cnt ) { }
|
|
~TrackDtor() { ++*cnt; }
|
|
int *cnt;
|
|
};
|
|
|
|
TEST(dtor) {
|
|
int cnt = 0;
|
|
{
|
|
Union< int, TrackDtor > u;
|
|
u = TrackDtor( &cnt );
|
|
cnt = 0;
|
|
}
|
|
ASSERT_EQ( cnt, 1 );
|
|
}
|
|
|
|
TEST(assing_dtor) {
|
|
int cnt = 0;
|
|
Union< int, TrackDtor > u;
|
|
u = TrackDtor( &cnt );
|
|
cnt = 0;
|
|
u = 1;
|
|
ASSERT_EQ( cnt, 1 );
|
|
}
|
|
};
|
|
|
|
enum class FA : unsigned char { X = 1, Y = 2, Z = 4 };
|
|
enum class FB : unsigned short { X = 1, Y = 2, Z = 4 };
|
|
enum class FC : unsigned { X = 1, Y = 2, Z = 4 };
|
|
enum class FD : unsigned long { X = 1, Y = 2, Z = 4 };
|
|
|
|
struct StrongEnumFlagsTest {
|
|
template< typename Enum >
|
|
void testEnum() {
|
|
StrongEnumFlags< Enum > e1;
|
|
StrongEnumFlags< Enum > e2( Enum::X );
|
|
|
|
ASSERT( !e1 );
|
|
ASSERT( e2 );
|
|
|
|
#if ((__GNUC__ >= 4 && __GNUC_MINOR__ > 9) || (__clang_major__ == 3 && __clang_minor__ >= 6))
|
|
ASSERT( e1 | e2 );
|
|
ASSERT( Enum::X | Enum::Y );
|
|
ASSERT( e2 | Enum::Z );
|
|
ASSERT( e2.has( Enum::X ) );
|
|
|
|
ASSERT( e2 & Enum::X );
|
|
ASSERT( !( Enum::X & Enum::Y ) );
|
|
|
|
ASSERT( Enum::X | Enum::Y | Enum::Z );
|
|
ASSERT( !( Enum::X & Enum::Y & Enum::Z ) );
|
|
ASSERT( ( Enum::X | Enum::Y | Enum::Z ) & Enum::X );
|
|
#endif
|
|
}
|
|
|
|
// we don't want to break classical enums and ints by out operators
|
|
TEST(regression) {
|
|
enum Classic { C_X = 1, C_Y = 2, C_Z = 4 };
|
|
|
|
ASSERT( C_X | C_Y | C_Z );
|
|
ASSERT( 1 | 2 | 4 );
|
|
ASSERT( C_X & 1 );
|
|
}
|
|
|
|
TEST(enum_uchar) { testEnum< FA >(); }
|
|
TEST(enum_ushort) { testEnum< FB >(); }
|
|
TEST(enum_uint) { testEnum< FC >(); }
|
|
TEST(enum_ulong) { testEnum< FD >(); }
|
|
};
|
|
|
|
#endif
|
|
|
|
}
|
|
}
|
|
|
|
|
|
#endif
|
|
// vim: syntax=cpp tabstop=4 shiftwidth=4 expandtab
|