import std.stdio; // writeln import std.conv; // to! import std.string; // format, wstring enum varType { None, Int, Float } union varData { int asInt; float asFloat; } struct variant { private: varType _type; varData _data; // Getters: T get(T)() const { switch(this._type) { case varType.None: return( T.init ); case varType.Int: return( this.getInt!(T)( ) ); case varType.Float: return( this.getFloat!(T)( ) ); default: throw new Exception("Unknown Type"); } } T getInt(T)() const { return( to!(T)(this._data.asInt) ); } T getFloat(T)() const { return( to!(T)(this._data.asFloat) ); } // Not very useful, but an example of how specialization could be used for // custom conversions. int getFloat(T : int)() const { return( 123 ); } // Setters: void set(T)(T value) { throw new Exception(format("Don't know how to set to type %s.", T.stringof)); } void set(T : int)(int value) { this._type = varType.Int; this._data.asInt = value; } void set(T : float)(float value) { this._type = varType.Float; this._data.asFloat = value; } public: @property { T Value(T)() const { return( this.get!(T)() ); } void Value(T)(T value) { this.set!(T)(value); } } } void main() { variant v; writeln("Empty variant:"); writeln(v.Value!(int)); writeln(v.Value!(float)); writeln(v.Value!(wstring)); v.Value = 42.3f; //v.Value!(float) = 42.3f; // Or explicitly writeln("After setting to float:"); writeln(v.Value!(int)); writeln(v.Value!(float)); writeln(v.Value!(wstring)); // v.Value!(wstring) = "42.3"w; // This will throw an exception. }