* Makefile.am, README, bricks/brick-assert.h, bricks/brick-bitlevel.h, bricks/brick-hash.h, bricks/brick-hashset.h, bricks/brick-shmem.h, bricks/brick-types.h, configure.ac, debian/copyright, debian/libspot-dev.install, m4/bricks.m4, tests/Makefile.am, tests/core/.gitignore, tests/core/bricks.cc: here.
1206 lines
34 KiB
C++
1206 lines
34 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-2014 Vladimír Štill <xstill@fi.muni.cz>
|
|
*/
|
|
|
|
/* Redistribution and use in source and binary forms, with or without
|
|
* modification, are permitted provided that the following conditions are met:
|
|
*
|
|
* 1. Redistributions of source code must retain the above copyright notice,
|
|
* this list of conditions and the following disclaimer.
|
|
*
|
|
* 2. Redistributions in binary form must reproduce the above copyright notice,
|
|
* this list of conditions and the following disclaimer in the documentation
|
|
* and/or other materials provided with the distribution.
|
|
*
|
|
* THIS SOFTWARE IS PROVIDED BY THE COPYRIGHT HOLDERS AND CONTRIBUTORS "AS IS"
|
|
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO, THE
|
|
* IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE
|
|
* ARE DISCLAIMED. IN NO EVENT SHALL THE COPYRIGHT HOLDER OR CONTRIBUTORS BE
|
|
* LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
|
|
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
|
|
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
|
|
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
|
|
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
|
|
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF THE
|
|
* POSSIBILITY OF SUCH DAMAGE. */
|
|
|
|
#include <bricks/brick-assert.h>
|
|
|
|
#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
|
|
#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 ) {} };
|
|
|
|
struct Comparable {
|
|
typedef bool IsComparable;
|
|
};
|
|
|
|
template< typename T >
|
|
typename T::IsComparable operator!=( const T &a, const T &b ) {
|
|
return not( a == b );
|
|
}
|
|
|
|
template< typename T >
|
|
typename T::IsComparable operator==( const T &a, const T &b ) {
|
|
return a <= b && b <= a;
|
|
}
|
|
|
|
template< typename T >
|
|
typename T::IsComparable operator<( const T &a, const T &b ) {
|
|
return a <= b && a != b;
|
|
}
|
|
|
|
template< typename T >
|
|
typename T::IsComparable operator>( const T &a, const T &b ) {
|
|
return b <= a && a != b;
|
|
}
|
|
|
|
template< typename T >
|
|
typename T::IsComparable operator>=( const T &a, const T &b ) {
|
|
return b <= a;
|
|
}
|
|
|
|
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(); }
|
|
|
|
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;
|
|
};
|
|
|
|
// don't catch integral types and classical enum!
|
|
template< typename Self, typename = typename
|
|
std::enable_if< is_enum_class< Self >::value >::type >
|
|
constexpr StrongEnumFlags< Self > operator|( Self a, Self b ) noexcept {
|
|
using Ret = StrongEnumFlags< Self >;
|
|
return Ret( a ) | Ret( b );
|
|
}
|
|
|
|
template< typename Self, typename = typename
|
|
std::enable_if< is_enum_class< Self >::value >::type >
|
|
constexpr StrongEnumFlags< Self > operator&( Self a, Self b ) noexcept {
|
|
using Ret = StrongEnumFlags< Self >;
|
|
return Ret( a ) & Ret( b );
|
|
}
|
|
|
|
/* 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 _T >
|
|
struct Witness { using T = _T; };
|
|
|
|
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;
|
|
}
|
|
|
|
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 ) {
|
|
typename std::aligned_storage< size, algignment >::type tmpStor;
|
|
unsigned char tmpDis;
|
|
|
|
std::memcpy( &tmpStor, &other.storage, size );
|
|
tmpDis = other._discriminator;
|
|
other._discriminator = 0;
|
|
std::memcpy( &other.storage, &storage, size );
|
|
other._discriminator = _discriminator;
|
|
_discriminator = 0;
|
|
std::memcpy( &storage, &tmpStor, size );
|
|
_discriminator = tmpDis;
|
|
}
|
|
|
|
bool empty() {
|
|
return _discriminator == 0;
|
|
}
|
|
|
|
explicit operator bool() {
|
|
return !empty();
|
|
}
|
|
|
|
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 >
|
|
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;
|
|
}
|
|
|
|
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
|
|
&& _compare< std::equal_to >( other );
|
|
}
|
|
|
|
bool operator!=( const Union &other ) const {
|
|
return _discriminator != other._discriminator
|
|
|| _compare< std::not_equal_to >( other );
|
|
}
|
|
|
|
bool operator<( const Union &other ) const {
|
|
return _discriminator < other._discriminator
|
|
|| (_discriminator == other._discriminator
|
|
&& _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 algignment = _impl::MaxAlign< 1, Types... >::value;
|
|
typename std::aligned_storage< size, algignment >::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 & )
|
|
{ ASSERT_UNREACHABLE( "invalid _copyConstruct" ); }
|
|
|
|
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 && )
|
|
{ ASSERT_UNREACHABLE( "invalid _moveConstruct" ); }
|
|
|
|
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 & ) { ASSERT_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 ) { ASSERT_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 && ) { ASSERT_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 { ASSERT_UNREACHABLE( "invalid discriminator" ); }
|
|
|
|
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" );
|
|
ASSERT_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< 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
|
|
|
|
}
|
|
}
|
|
|
|
namespace brick_test {
|
|
namespace types {
|
|
|
|
using namespace ::brick::types;
|
|
|
|
struct Integer : Comparable
|
|
{
|
|
int val;
|
|
public:
|
|
Integer(int val) : val(val) {}
|
|
bool operator<=( const Integer& o ) const { return val <= o.val; }
|
|
};
|
|
|
|
struct Mixins {
|
|
|
|
TEST(comparable) {
|
|
Integer i10(10);
|
|
Integer i10a(10);
|
|
Integer i20(20);
|
|
|
|
ASSERT(i10 <= i10a);
|
|
ASSERT(i10a <= i10);
|
|
ASSERT(i10 <= i20);
|
|
ASSERT(! (i20 <= i10));
|
|
|
|
ASSERT(i10 != i20);
|
|
ASSERT(!(i10 != i10a));
|
|
|
|
ASSERT(i10 == i10a);
|
|
ASSERT(!(i10 == i20));
|
|
|
|
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));
|
|
}
|
|
|
|
};
|
|
|
|
#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 );
|
|
ASSERT( !u.empty() );
|
|
ASSERT( u.is< int >() );
|
|
ASSERT_EQ( u.get< int >(), 1 );
|
|
u = 2; // move
|
|
ASSERT( !!u );
|
|
ASSERT_EQ( u.get< int >(), 2 );
|
|
int i = 3;
|
|
u = i; // copy
|
|
ASSERT( !!u );
|
|
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 );
|
|
ASSERT( !u.is< int >() );
|
|
u = 5;
|
|
ASSERT( u );
|
|
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 );
|
|
ASSERT( wierd.is< long >() );
|
|
ASSERT_EQ( wierd.get< long >(), 2L );
|
|
|
|
wierd = Move();
|
|
ASSERT( !!wierd );
|
|
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 ); };
|
|
|
|
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() );
|
|
}
|
|
|
|
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 );
|
|
}
|
|
};
|
|
|
|
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 );
|
|
|
|
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 );
|
|
}
|
|
|
|
// 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
|