opDispatch
is a good hook for code generation of all kinds. Here, we'll use it to generate properties to access an associative array of keys in a different format in order to mimic the style
property in the JavaScript DOM, which translates foo.style.backgroundColor
, for example, to the background-color
CSS property.
Let's execute the following steps to use opDispatch
to generate properties:
camelCase
string to a dash-separated string.struct
type with two opDispatch
@property
methods: a getter that takes no runtime arguments and a setter which takes a string runtime argument. Both the opDispatch
methods should take one compile-time string argument.enum
keyword with the transformation function to ensure it is done at compile time.opDispatch
to work only on the properties you want to enable, or if you want it to work on everything, set it minimally to not work on popFront
.DomElement
class with a style
property that returns the Style
struct
and a main
function which tries to use it.The code for the preceding steps is as follows:
// transformation function string unCamelCase(string a) { import std.string; string ret; foreach(c; a) if((c >= 'A' && c <= 'Z')) ret ~= "-" ~ toLower("" ~ c); else ret ~= c; return ret; } private struct Style { // pointer to the associative array we'll modify string[string]* aa; this(string[string]* aa) { this.aa = aa; } // getter @property string opDispatch(string name)() if(name != "popFront") { enum n = unCamelCase(name); // step 3 return (*aa)[n]; } // setter @property string opDispatch(string name)(string value) if(name != "popFront") { enum n = unCamelCase(name); // step 3 return (*aa)[n] = value; } } class DomElement { string[string] styles; // step 6 @property Style style() { return Style(&styles); } } void main() { auto element = new DomElement(); element.style.backgroundColor = "red"; element.style.fontSize = "1em"; import std.stdio; writeln(element.styles); writeln(element.style.backgroundColor); }
By running the preceding program, we will get the following output:
["background-color":"red", "font-size":"1em"] red
This shows that the properties transformed properly for both reading and writing.
opDispatch
is invoked whenever a non-existent member is requested of an aggregate with the dot syntax. It is passed with the name of the requested member as a compile-time parameter. Otherwise, it is just a regular template and may provide code or data.
It is strongly recommended to always put a constraint on opDispatch
, because without it, the compile-time duck typing will be completely thrown off. For example, the isInputRange
function will return true
because the missing methods of empty
, popFront
, and front
will be provided by opDispatch
. This will result in functions such as writeln
trying to iterate over the object as a range and print its contents. However, since they weren't intentionally written, it is unlikely that the object will actually work as a range.
Since the name is passed as a compile-time parameter, it can be used for all kinds of compile-time transformations, including changing the string into a new kind of literal and even mixing it in as a domain-specific language.
Here, we used it to change the format of a string to generate thin accessor properties. The code is very straightforward; a regular function manipulates the string. Then, we use the enum
keyword to force the manipulation to be done at compile time for maximum runtime performance and use it to look up data in the associative array.
We also wanted the generated properties to work on the child property, style
. Since opDispatch
only works with one level at a time, we created a helper object and property function to expose it. The helper object needs a reference to the data it modifies, which we pass in the constructor as a pointer. We could have also passed a reference to the DomElement
class. Even if the Style
struct were nested inside the DomElement
class, it will still be necessary to explicitly pass it a reference to avoid compile errors.
When dot syntax is used on the style
property, the child properties are generated transparently and on demand by opDispatch
.
18.226.88.110