import std.traits; struct None {} // // Possible point of specialization for the future - use null or NaN value // to discern none-ness. // template hasNull( T ) { enum hasNull = __traits( compiles, (T t) => t is null ); } /// /// An optional value. /// struct Option( T ) if ( !hasNull!T ) { private bool none = true; private T payload; this( T t ) { payload = t; none = false; } this( None n ) { none = true; } ref Option opAssign( None n ) { none = true; return this; } ref Option opAssign( T t ) { payload = t; none = false; return this; } ref Option opAssign( Option other ) { none = other.none; if ( none ) { payload = other.payload; } return this; } @property const bool hasValue( ) { return !none; } /// /// Pattern matching. Requires that the programmer handle both the normal and the None case. /// @property auto match( alias fnNone, alias fnPayload )( ) if (is( typeof( fnNone( None( ) ) ) ) && is( typeof( fnPayload( payload ) ) ) ) { if ( none ) { return fnNone( None( ) ); } else { return fnPayload( payload ); } } /// ditto @property auto match( alias fnPayload, alias fnNone )( ) if (is( typeof( fnNone( None( ) ) ) ) && is( typeof( fnPayload( payload ) ) ) ) { if ( none ) { return fnNone( None( ) ); } else { return fnPayload( payload ); } } // Helper template private template isPropertyImpl( T ) { static if ( /*isFunctionPointer!T || isDelegate!T ||*/ is(T == function) ) { enum isPropertyImpl = functionAttributes!T & FunctionAttribute.property; } else { enum isPropertyImpl = true; } } // Helper template private template isProperty( string name ) { enum isProperty = isPropertyImpl!(typeof(__traits( getMember, T, name ))); } // Helper template private template PropertyType( string name ) { static if ( is( typeof( __traits( getMember, T, name ) ) == function ) ) { alias ReturnType!( __traits( getMember, T, name ) ) PropertyType; } else { alias typeof( __traits( getMember, T, name ) ) PropertyType; } } // Helper template private template hasVoidAssignResult( string name ) { alias PropertyType!name V; enum hasVoidAssignResult = is( typeof( __traits( getMember, T, name ) = V.init ) == void); } /// /// Accesses fields and properties of the wrapped type. /// The returned value will be an Option, or void in the case of setters returning void. /// /// @property auto opDispatch( string name, U... )( U args ) if ( isProperty!name && U.length < 2 ) { static if ( U.length == 0 ) { Option!( PropertyType!name ) result; if ( !none ) { mixin( "result = payload." ~ name ~ ";" ); //result = __traits( getMember, payload, name ); } return result; } else { static if ( hasVoidAssignResult!name ) { if ( !none ) { mixin( "payload." ~ name ~ " = args[0];" ); //__traits( getMember, payload, name ) = args[0]; } } else { Option!( PropertyType!name ) result; if ( !none ) { mixin( "result = payload." ~ name ~ " = args[0];" ); //result = __traits( getMember, payload, name ) = args[0]; } return result; } } } /// /// Access member functions of the wrapped type. /// In the case of None, no function call will take place, and the return value /// will be Option!U, with U being the original return type. /// If the function returns void, void will be returned here too. /// auto opDispatch( string name, U... )( U args ) if ( !isProperty!name ){ alias typeof( mixin( "payload." ~ name ~ "( args )" ) ) ReturnType; static if ( is( ReturnType == void ) ) { if ( !none ) { mixin( "payload." ~ name ~ "( args );" ); // __traits( getMember, payload, name )( args ); } } else { Option!ReturnType result; if ( !none ) { mixin( "result = payload." ~ name ~ "( args );" ); // result = __traits( getMember, payload, name )( args ); } return result; } } } version ( unittest ) { struct S { int n; @property float f( ) { return 0.0; } @property float f( float value ) const { return 0.0; } @property double d( ) { return 0.0; } @property void d( double value ) { } void foo( ) { } int bar( ) { return 3; } } unittest { Option!S s; assert( s.isProperty!"n" ); assert( is( s.PropertyType!"n" == int ) ); assert( !s.hasVoidAssignResult!"n" ); assert( s.isProperty!"f" ); assert( is( s.PropertyType!"f" == float ) ); assert( !s.hasVoidAssignResult!"f" ); assert( s.isProperty!"d" ); assert( is( s.PropertyType!"d" == double ) ); assert( s.hasVoidAssignResult!"d" ); assert( !s.isProperty!"foo" ); assert( !s.isProperty!"bar" ); auto f = s.f; assert( is( typeof( f ) == Option!float ) ); f = s.f = 4; s.d = 4; s.foo( ); auto b = s.bar( ); assert( is( typeof( b ) == Option!int ) ); } } unittest { Option!int a; assert( !a.hasValue ); a = 4; assert( a.hasValue ); a = None( ); assert( !a.hasValue ); a = 4; int n = a.match!( (int x) => x, (None n) => 0 ); assert( n == 4 ); a = None( ); n = a.match!( (int x) => x, (None n) => 0 ); assert( n == 0 ); } void main( ) { }