Skip to content

Instantly share code, notes, and snippets.

@DanielKeep
Created July 25, 2009 09:15
Show Gist options
  • Save DanielKeep/154755 to your computer and use it in GitHub Desktop.
Save DanielKeep/154755 to your computer and use it in GitHub Desktop.
/*
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