module tailconst; import std.traits; import std.range; import std.typetuple; import std.typecons; template StaticFilter( alias pred ) { alias TypeTuple!() StaticFilter; } template StaticFilter( alias pred, alias T, U... ) { static if ( pred!T ) { alias TypeTuple!( T, StaticFilter!( pred, U ) ) StaticFilter; } else { alias StaticFilter!( pred, U ) StaticFilter; } } /** * Check whether T.fld is a field. **/ template has( T ) { template asField( string fld ) { static if ( is( typeof( mixin( "T." ~ fld ) ) ) ) { enum asField = !is( typeof( mixin( "T." ~ fld ) ) == function ); } else { enum asField = false; } } } unittest { struct Test { int n; int foo( ); } assert( has!( Test ).asField!( "n" ) ); assert( !has!( Test ).asField!( "foo" ) ); } /** * Helper struct for comparing type tuples. **/ struct ComparisonTuple( T... ) { alias TypeTuple!T contents; } unittest { assert( is( ComparisonTuple!( int ) == ComparisonTuple!( int ) ) ); assert( is( ComparisonTuple!( "a" ) == ComparisonTuple!( "a" ) ) ); assert( !is( ComparisonTuple!( "a" ) == ComparisonTuple!( "b" ) ) ); assert( !is( ComparisonTuple!( int ) == ComparisonTuple!( "a" ) ) ); } /** * Filters out fields in a class/struct definition, and returns a type tuple of their names. **/ template MemberFieldNames( T ) { alias StaticFilter!( has!T.asField, __traits( allMembers, T ) ) MemberFieldNames; } unittest { struct Test { int n; } assert( is( ComparisonTuple!( MemberFieldNames!Test ) == ComparisonTuple!( "n" ) ) ); } /** * Converts the passed parameter to mutable, const, or immutable. **/ template Mutable( T ) { alias Unqual!T Mutable; } template Const( T ) { alias const( T ) Const; } template Immutable( T ) { alias immutable( T ) Immutable; } unittest { assert( is( Mutable!( const int ) == int) ); assert( is( Const!int == const( int ) ) ); assert( is( Immutable!int == immutable( int ) ) ); } /** * Compare the fields of two types to see if they're the same, disregarding constness. **/ template SameFields( T, U ) { enum SameFields = is( Tuple!( staticMap!( Immutable, FieldTypeTuple!T ) ) == Tuple!( staticMap!( Immutable, FieldTypeTuple!U ) ) ) //&& is( ComparisonTuple!( MemberFieldNames!T ) == ComparisonTuple!( MemberFieldNames!U ) ) // Uncomment this line for more bugs. ; } unittest { struct Test1 { int n; } struct Test2 { const int n; } struct Test3 { const string s; } assert( SameFields!( Test1, Test2 ) ); assert( SameFields!( Test2, Test1 ) ); assert( !SameFields!( Test1, Test3 ) ); assert( !SameFields!( Test3, Test1 ) ); assert( !SameFields!( Test2, Test3 ) ); assert( !SameFields!( Test3, Test2 ) ); } /** * Return the tail-mutable type for a given type **/ template TailMutable( T ) { static if ( is( T U : U[] ) ) { alias Unqual!U[] TailMutable; } else static if ( is( T U : U* ) ) { alias Unqual!U* TailMutable; } else static if ( is( T.TailMutable ) ) { static assert( SameFields!( T, T.TailMutable ), "Normal and tail-mutable versions must have same members." ); alias T.TailMutable TailMutable; } else static if ( ( !is( T == struct ) && !is( T == class ) ) || ( is( T == struct ) && !hasIndirections!T ) ) { alias Unqual!T TailMutable; } } /** * Ditto for tail-const **/ template TailConst( T ) { static if ( is( T U : U[] ) ) { alias const(Unqual!U)[] TailConst; } else static if ( is( T U : U* ) ) { alias const(Unqual!U)* TailConst; } else static if ( is( T.TailConst ) ) { static assert( SameFields!( T, T.TailConst ), "Normal and tail-const versions must have same members." ); alias T.TailConst TailConst; } else static if ( ( !is( T == struct ) && !is( T == class ) ) || ( is( T == struct ) && !hasIndirections!T ) ) { alias Unqual!T TailConst; } } /** * Ditto for tail-immutable **/ template TailImmutable( T ) { static if ( is( T U : U[] ) ) { alias immutable(Unqual!U)[] TailImmutable; } else static if ( is( T U : U* ) ) { alias immutable(Unqual!U)* TailImmutable; } else static if ( is( T.TailImmutable ) ) { static assert( SameFields!( T, T.TailImmutable ), "Normal and tail-immutable versions must have same members." ); alias T.TailImmutable TailImmutable; } else static if ( ( !is( T == struct ) && !is( T == class ) ) || ( is( T == struct ) && !hasIndirections!T ) ) { alias Unqual!T TailImmutable; } } /** * Return the head-mutable type for a given type. * * That is, const(T)[] for const(T[]), T.TailConst for const(T), etc. * **/ template HeadMutable( T ) { static if ( is( T U : U[] ) ) { alias U[] HeadMutable; } else static if ( is( T U : U* ) ) { alias U* HeadMutable; } else static if ( is( T == immutable ) && is( T.TailImmutable ) ) { static assert( SameFields!( T, T.TailImmutable ), "Normal and tail-immutable versions must have same members." ); alias T.TailImmutable HeadMutable; } else static if ( is( T == const ) && is( T.TailConst ) ) { static assert( SameFields!( T, T.TailConst ), "Normal and tail-immutable versions must have same members." ); alias T.TailConst HeadMutable; } else static if ( is( T.TailMutable ) ) { static assert( SameFields!( T, T.TailMutable ), "Normal and tail-immutable versions must have same members." ); alias T.TailMutable HeadMutable; } else static if ( ( !is( T == struct ) && !is( T == class ) ) || ( is( T == struct ) && !hasIndirections!T ) ) { alias Unqual!T HeadMutable; } else { static assert( false, "No head-mutable version of " ~ T.stringof ); } } unittest { struct Test( T ) { T d; alias Test!T TailMutable; alias Test!T TailConst; alias Test!T TailImmutable; } struct S { int a; } struct T { int* p; } assert( is( TailMutable!( int[] ) == int[] ) ); assert( is( TailMutable!( immutable( int[] ) ) == int[] ) ); assert( is( TailMutable!( const(int)[] ) == int[] ) ); assert( is( TailMutable!( Test!int ) == Test!int ) ); assert( is( TailMutable!int == int ) ); assert( is( TailConst!( int[] ) == const(int)[] ) ); assert( is( TailConst!( immutable( int[] ) ) == const(int)[] ) ); assert( is( TailConst!( const(int)[] ) == const(int)[] ) ); assert( is( TailConst!( Test!int ) == Test!int ) ); assert( is( TailConst!int == int ) ); assert( is( TailImmutable!( int[] ) == immutable(int)[] ) ); assert( is( TailImmutable!( immutable( int[] ) ) == immutable(int)[] ) ); assert( is( TailImmutable!( const(int)[] ) == immutable(int)[] ) ); assert( is( TailImmutable!( Test!int ) == Test!int ) ); assert( is( TailImmutable!int == int ) ); assert( is( TailMutable!S == S ) ); assert( is( TailMutable!(const S) == S ) ); assert( is( TailMutable!(immutable S) == S ) ); assert( is( TailConst!S == S ) ); assert( is( TailConst!(const S) == S ) ); assert( is( TailConst!(immutable S) == S ) ); assert( is( TailImmutable!S == S ) ); assert( is( TailImmutable!(const S) == S ) ); assert( is( TailImmutable!(immutable S) == S ) ); assert( !is( TailMutable!T ) ); assert( is( HeadMutable!( const(int[]) ) == const(int)[] ) ); assert( is( HeadMutable!( int[] ) == int[] ) ); assert( is( HeadMutable!( const(int) ) == int ) ); assert( is( HeadMutable!( int ) == int ) ); assert( is( HeadMutable!( string ) == string ) ); } /** * Converts the given parameter to tail mutable **/ TailMutable!T tailmutable( T )( T t ) { TailMutable!T tmp = t; return tmp; } /** * Converts the given parameter to tail const **/ TailConst!T tailconst( T )( T t ) { TailConst!T tmp = t; return tmp; } /** * Converts the given parameter to tail immutable **/ TailImmutable!T tailimmutable( T )( T t ) { TailImmutable!T tmp = t; return tmp; } /** * Converts the given parameter to head mutable **/ HeadMutable!T headmutable( T )( T t ) { HeadMutable!T tmp = t; return tmp; } unittest { struct test( T ) { alias test!(Unqual!T) TailMutable; alias test!(const Unqual!T) TailConst; alias test!(immutable T) TailImmutable; this( test.TailMutable t ) {} this( test.TailConst t ) {} this( test.TailImmutable t ) {} } test!(const int) t; tailmutable( t ); assert( __traits( compiles, { tailconst( [1,2,3] ); } ) ); assert( __traits( compiles, { test!int t; tailconst( t ); } ) ); assert( __traits( compiles, { test!(const int) t; tailconst( t ); } ) ); assert( __traits( compiles, { test!(immutable int) t; tailconst( t ); } ) ); assert( __traits( compiles, { test!int t; tailconst( t ); } ) ); assert( __traits( compiles, { tailmutable( [1,2,3] ); } ) ); assert( __traits( compiles, { test!int t; tailmutable( t ); } ) ); assert( __traits( compiles, { test!(const int) t; tailmutable( t ); } ) ); assert( __traits( compiles, { test!(immutable int) t; tailmutable( t ); } ) ); assert( !__traits( compiles, { tailimmutable( [1,2,3] ); } ) ); assert( __traits( compiles, { test!int t; tailimmutable( t ); } ) ); assert( __traits( compiles, { test!(const int) t; tailimmutable( t ); } ) ); assert( __traits( compiles, { test!(immutable int) t; tailimmutable( t ); } ) ); } /** * Dummy template to wrap a type. **/ struct Tail( T ) {} template fixTail( alias F ) { alias TypeTuple!() fixTail; } /** * Applies the passed template to each element of the passed types that is a Tail!T **/ template fixTail( alias F, T, U... ) { static if ( is( T t : Tail!V, V ) ) { alias TypeTuple!( F!V, fixTail!( F, U ) ) fixTail; } else { alias TypeTuple!( T, fixTail!( F, U ) ) fixTail; } } unittest { assert( is( ComparisonTuple!( fixTail!( Const, int ) ) == ComparisonTuple!( int ) ) ); assert( is( ComparisonTuple!( fixTail!( Const, Tail!int ) ) == ComparisonTuple!( const( int ) ) ) ); } /** * Automagically defines TailMutable, TailConst, and TailImmutable for a type. * * Usage: * struct MyRange( T, int n ) { * T wrapped; * mixin tailConst!( .MyRange, Tail!T, n ); * } * * Note that the template must be specified with a leading '.'. * This is because MyRange would point to the struct Myrange!(T,n), not the template * MyRange. * **/ mixin template tailConst( alias F, U... ) { alias F!( fixTail!( .TailMutable, U ) ) TailMutable; alias F!( fixTail!( .TailConst, U ) ) TailConst; alias F!( fixTail!( .TailImmutable, U ) ) TailImmutable; }