/** Defines tail-const/invariant reference types to work around the limitations of the current const system. Issues: License: $(LICENSE) Copyright: Copyright ©2008 Sönke Ludwig, All rights reserved. Authors: Sönke Ludwig */ module sfc.core.tailconst; import std.traits; import std.metastrings; /** A class reference type which behaves as a tail-const or tail-invariant reference. The reference itself will be mutable (i.e. the instance it points to can be changed to different instances). However, the instance itself cannot be changed. Note that only primitive types my be used as parameters or return types here, since they must be known in the scope of this module. Use TailConstMixin to use externally defined types. Examples: -------------------- class MyClass { private float m_prop; const float prop(){ return m_prop; } float prop( float v ){ return m_prop = v; } } alias TailConst!(const(MyClass)) MyClassRef; void test(){ MyClass inst1 = new MyClass; MyClass inst2 = new MyClass; MyClassRef r = inst1; r = inst2; inst2.prop = 14.4; //r.prop = 12; // not allowed writefln("r.prop: %s", r.prop()); // prints 14.4 } -------------------- */ struct TailConst(C) { // check for valid type of C static if( !is(C == const) && !is(C == invariant) || !is(C == class) ){ static assert(false, "tailconst_ can only be used with const/invariant classes, not with "~C.stringof); } // holds the class reference casted to void* private const(void)* pinst; // constructors and assignment operators static TailConst opCall(C f) { TailConst r = { cast(void*)f }; return r; } static TailConst opCall(in TailConst f) { TailConst r = { f.pinst }; return r; } TailConst opAssign(C f) { pinst = cast(void*)f; return *this; } TailConst opAssign(in TailConst f) { pinst = f.pinst; return *this; } // back-casting to C const C inst(){ return cast(C)pinst; } const C opCast(){ return inst(); } //const C opImplicitCast(){...} // debug output //pragma(msg, "TailConst!("~C.stringof~"):"); //pragma(msg, MemberProxies!(C, 0)()); // insert proxy functions for all methods of C mixin(MemberProxies!(C, 0)); } /** Mixin version of TailConst!(). The tail-const wrapper struct will be defined in the callers scope and thus allows user types to be used. Examples: -------------------- class MyClass { private float m_prop; const float prop(){ return m_prop; } float prop( float v ){ return m_prop = v; } } mixin(TailConstMixin!(const(MyClass), "MyClassRef")); void test(){ MyClass inst1 = new MyClass; MyClass inst2 = new MyClass; MyClassRef r = inst1; r = inst2; inst2.prop = 14.4; //r.prop = 12; // not allowed writefln("r.prop: %s", r.prop()); // prints 14.4 } -------------------- */ string TailConstMixin(C, string name)() { // check for valid type of C static if( !is(C == const) && !is(C == invariant) || !is(C == class) ){ static assert(false, "tailconst_ can only be used with const/invariant classes, not with "~C.stringof); } const string Cs = C.stringof; return `struct `~name~` { // holds the class reference casted to void* private const(void)* pinst; // constructors and assignment operators static `~name~` opCall(`~Cs~` f) { `~name~` r = { cast(void*)f }; return r; } static `~name~` opCall(in `~name~` f) { `~name~` r = { f.pinst }; return r; } `~name~` opAssign(`~Cs~` f) { pinst = cast(void*)f; return *this; } `~name~` opAssign(in `~name~` f) { pinst = f.pinst; return *this; } // back-casting to C const `~Cs~` inst(){ return cast(`~Cs~`)pinst; } const `~Cs~` opCast(){ return inst(); } //const C opImplicitCast(){...} // proxy functions for all methods of C `~MemberProxies!(C, 0)~` } `; } private { string MemberProxies(C, int i)(){ string ret = MethodProxies!(C, __traits(allMembers, C)[i]); static if( i+1 < __traits(allMembers, C).length ){ ret ~= MemberProxies!(C, i+1); } return ret; } string MethodProxies(C, string methodname)(){ string ret = ""; foreach( mtypestr; __traits(getVirtualFunctions, C, methodname) ){ alias typeof(mtypestr) MT; //pragma(msg, "method: "~methodname~" : "~/*MT.stringof*/MutabilityString!(MT)); if( is(MT == const) || is(MT == invariant) ) ret ~= SingleMethodProxy!(methodname, MT); } return ret; } string SingleMethodProxy(string name, MT)(){ alias ReturnType!(MT) RT; alias ParameterTypeTuple!(MT) PT; return " "~RT.stringof~" "~name~"("~TypedParameters!(PT)~")\n" ~ " {\n" //~ " writefln(\"Proxy called for "~name~"\");\n" ~ " " ~ (is(RT==void)?"":"return ") ~ "inst()."~name~"("~Parameters!(PT)~");\n" ~ " }\n"; } string TypedParameters(T...)(){ string ret = ""; foreach( i, U; T ) ret ~= U.stringof ~ " arg" ~ ToString!(i) ~ ", "; if( T.length > 0 ) ret = ret[0..$-2]; return ret; } string Parameters(T...)(){ string ret = ""; foreach( i, U; T ) ret ~= "arg" ~ ToString!(i) ~ ", "; if( T.length > 0 ) ret = ret[0..$-2]; return ret; } string MutabilityString(T)(){ return is(T == invariant) ? "invariant" : is(T == const) ? "const" : "mutable"; } } version(TailConstTest) { import std.stdio; class C { // NOTE: public members break this currently //int var1; //const(int) var2; const float m1(){ writefln("C.m1()"); return 3.1415; } const float m1(const(int)){ return 1.4143; } float m1(float dummytomakem1visible){ return -1; } const int m2(){ return 0; } int m2(int){ return 1; } // NOTE: methods with no mutable overloads are not visible to __traits(allMembers) invariant const(int) m3(string){ return 0; } const int m3(string){ return 0;} string m4(string){ return ""; } } alias TailConst!(const(C)) TailConstCReference; pragma(msg, TailConstMixin!(const(C), "TailConstCReference2")()); mixin(TailConstMixin!(const(C), "TailConstCReference2")); int main() { TailConstCReference c = new C; writefln("m1() returned %s", c.m1()); writefln("m1(1) returned %s", c.m1(1)); writefln("m2() returned %s", c.m2()); TailConstCReference2 d = new C; writefln("m1() returned %s", d.m1()); writefln("m1(1) returned %s", d.m1(1)); writefln("m2() returned %s", d.m2()); return 0; } }