Created
July 25, 2009 09:15
-
-
Save DanielKeep/154755 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
/* | |
Demonstration of a library-level Property implementation. | |
*/ | |
import std.stdio; | |
const DefaultGetter = ""; | |
const DefaultSetter = DefaultGetter; | |
private | |
{ | |
enum DEFAULT_GETTER = q{ return storage; }; | |
enum DEFAULT_SETTER = q{ return storage = value; }; | |
string trim(string s) | |
{ | |
while( s.length > 0 | |
&& (s[0] == ' ' || s[0] == '\t' | |
|| s[0] == '\f' || s[0] == '\v' | |
|| s[0] == '\r' || s[0] == '\n') ) | |
s = s[1..$]; | |
while( s.length > 0 | |
&& (s[$-1] == ' ' || s[$-1] == '\t' | |
|| s[$-1] == '\f' || s[$-1] == '\v' | |
|| s[$-1] == '\r' || s[$-1] == '\n') ) | |
s = s[0..$-1]; | |
return s; | |
} | |
size_t indexOf(T)(T haystack, T needle) | |
{ | |
if( needle == "" ) return 0; | |
size_t i = 0; | |
while( haystack.length > 0 ) | |
{ | |
if( haystack.startsWith(needle) ) | |
return i; | |
++i; | |
haystack = haystack[1..$]; | |
} | |
return i; | |
} | |
bool startsWith(T)(T haystack, T needle) | |
{ | |
return haystack.length >= needle.length | |
&& haystack[0..needle.length] == needle; | |
} | |
bool endsWith(T)(T haystack, T needle) | |
{ | |
return haystack.length >= needle.length | |
&& haystack[$-needle.length..$] == needle; | |
} | |
bool contains(T)(T haystack, T needle) | |
{ | |
if( needle.length == 0 ) return true; | |
while( haystack.length >= needle.length ) | |
{ | |
if( haystack.startsWith(needle) ) | |
return true; | |
haystack = haystack[1..$]; | |
} | |
return false; | |
} | |
string propertyName(string name) | |
{ | |
return name[0..name.indexOf("=")]; | |
} | |
string propertyInitialiser(string name) | |
{ | |
size_t i = name.indexOf("="); | |
if( i<name.length ) | |
return name[i+1..$]; | |
else | |
return null; | |
} | |
string protectionHandlerStorage(string prot) | |
{ | |
return " | |
if( (var.startsWith(`"~prot~"`) | |
&& var[`"~prot~"`.length..$].trim().startsWith(`{`) | |
&& var.trim().endsWith(`}`)) ) | |
{ | |
return recurse( | |
var[`"~prot~"`.length..$] | |
.trim()[`{`.length..$][$-`}`.length..$], | |
DEFAULT | |
); | |
} | |
else if( var == `"~prot~"` ) | |
{ | |
return recurse(DEFAULT); | |
} | |
"; | |
} | |
bool usesStorage(string code, string DEFAULT="") | |
{ | |
code = code.trim(); | |
if( code == "" ) | |
return usesStorage(DEFAULT); | |
if( code == "-" ) | |
return false; | |
{ | |
alias usesStorage recurse; | |
alias code var; | |
mixin(protectionHandlerStorage("public")); | |
mixin(protectionHandlerStorage("private")); | |
mixin(protectionHandlerStorage("protected")); | |
mixin(protectionHandlerStorage("package")); | |
} | |
return code.contains("storage"); | |
} | |
string storageName(string name) | |
{ | |
return "__storage_"~name; | |
} | |
string propertyStorage(string T, string name) | |
{ | |
auto initialiser = name.propertyInitialiser(); | |
if( initialiser != "" ) | |
initialiser = " = " ~ initialiser; | |
return "private " ~ T ~ " " ~ storageName(name.propertyName()) | |
~ initialiser ~ ";\n"; | |
} | |
string protectionHandler(string prot) | |
{ | |
return " | |
if( (var.startsWith(`"~prot~"`) | |
&& var[`"~prot~"`.length..$].trim().startsWith(`{`) | |
&& var.trim().endsWith(`}`)) ) | |
{ | |
return `"~prot~" ` ~ recurse( | |
T, | |
name, | |
var[`"~prot~"`.length..$] | |
.trim()[`{`.length..$][$-`}`.length..$] | |
); | |
} | |
else if( var == `"~prot~"` ) | |
{ | |
return `"~prot~" ` ~ recurse(T, name, DEFAULT); | |
} | |
"; | |
} | |
string propertyGetter(string T, string name, string getter) | |
{ | |
getter = getter.trim(); | |
if( getter == "" ) | |
return propertyGetter(T, name, DEFAULT_GETTER); | |
if( getter == "-" ) | |
return ""; | |
{ | |
alias propertyGetter recurse; | |
alias getter var; | |
alias DEFAULT_GETTER DEFAULT; | |
mixin(protectionHandler("public")); | |
mixin(protectionHandler("private")); | |
mixin(protectionHandler("protected")); | |
mixin(protectionHandler("package")); | |
} | |
auto storage = ` | |
static if( is( typeof(`~storageName(name)~`) ) ) | |
{ | |
alias `~storageName(name)~` storage; | |
} | |
`; | |
return | |
T~` `~name~`() | |
{ | |
`~storage~` | |
`~getter~` | |
} | |
`; | |
} | |
string propertySetter(string T, string name, string setter) | |
{ | |
setter = setter.trim(); | |
if( setter == "" ) | |
return propertySetter(T, name, DEFAULT_SETTER); | |
if( setter == "-" ) | |
return ""; | |
{ | |
alias propertySetter recurse; | |
alias setter var; | |
alias DEFAULT_SETTER DEFAULT; | |
mixin(protectionHandler("public")); | |
mixin(protectionHandler("private")); | |
mixin(protectionHandler("protected")); | |
mixin(protectionHandler("package")); | |
} | |
auto storage = ` | |
static if( is( typeof(`~storageName(name)~`) ) ) | |
{ | |
alias `~storageName(name)~` storage; | |
} | |
`; | |
return | |
`auto `~name~`(`~T~` value) | |
{ | |
`~storage~` | |
`~setter~` | |
} | |
`; | |
} | |
} | |
template Property( | |
T, | |
string name, | |
string getter=DefaultGetter, | |
string setter=DefaultSetter) | |
{ | |
/* | |
enum Property = ""; | |
/*/ | |
enum Property = | |
(usesStorage(getter, DEFAULT_GETTER) | |
|| usesStorage(setter, DEFAULT_SETTER) | |
? propertyStorage(T.stringof, name) | |
: "") | |
~ propertyGetter(T.stringof, name.propertyName(), getter) | |
~ propertySetter(T.stringof, name.propertyName(), setter); | |
//*/ | |
} | |
enum CODE = | |
"" | |
~ "\n// bar: default\n" | |
~ Property!(int, "bar") | |
~ "\n// baz: publically read-only\n" | |
~ Property!(int, "baz", null, "private") | |
~ "\n// bop: publically read-only with default\n" | |
~ Property!(int, "bop = 42", null, "private") | |
~ "\n// qux: read-only\n" | |
~ Property!(int, "qux", null, "-") | |
~ "\n// zyz: custom reader and writer\n" | |
~ Property!(int, "zyz", | |
q{ | |
return 7; | |
}, | |
q{ | |
writefln("Tried to set foo to %s!", value); | |
}) | |
; | |
pragma(msg, CODE); | |
struct S | |
{ | |
mixin(CODE); | |
} | |
void main() | |
{ | |
S s; | |
s.bar = 1701; | |
writefln("s.bar = %s", s.bar); | |
writefln("s.baz = %s", s.baz); | |
writefln("s.bop = %s", s.bop); | |
writefln("s.zyz = %s", s.zyz); | |
s.zyz = 1984; | |
} |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment