A pattern I'd like to see more of - Parsing template parameter tuples

rikki cattermole rikki at cattermole.co.nz
Mon May 21 07:10:34 UTC 2018


On 21/05/2018 12:13 PM, Ethan wrote:
> Code for context: 
> https://github.com/GooberMan/binderoo/blob/master/binderoo_client/d/src/binderoo/util/enumoptions.d 
> 
> 
> Something struck me at DConf. I was watching the dxml talk and hearing 
> about all these things that weren't being implemented for one reason or 
> another. And I was thinking, "But what if I want those things?" Being D, 
> it'd be pretty easy to opt in to them with template parameters and 
> static if controlling what code gets executed at runtime.
> 
> But that brings up a bit of an annoying thing. Namely, the old school 
> way of doing such things:
> 
> class SomeObject( bool option1, bool option2, Flags iHateBools = 
> Flags.Default, int ohIDontWantThisToBeDefaultedButRefactoringSucks = -1 )
> {
> }
> 
> Pretty obnoxious design pattern.
> 
> But we're in D. We can do much better. It makes sense to do the following:
> 
> class SomeObject( LooseOptions... )
> {
> }
> 
> Much nicer. But how do we go about dealing with that? Static foreach 
> each time we want something? One time parse and cache the values? Both 
> are laborious in their own way. What we want is some helper objects to 
> make sense of it all.
> 
> This is where my EnumOptions struct comes in. The idea here is that all 
> the options you want as booleans, you put them in an enum like so:
> 
> enum SomeOptions
> {
>    Option1,
>    Option2,
>    Option5,
>    Option3Sir,
>    Option3
> }
> 
> And then instantiate your class like so:
> 
> alias SomeInstantiatedObject = SomeObject!( SomeOptions.Option1, 
> SomeOptions.Option2, SomeOptions.Option3 );
> 
> And inside your class definition, you clean it up automagically with a 
> nice little helper function I made:
> 
> class SomeObject( LooseOptions... )
> {
>    enum Options = OptionsOf( SomeOptions, LooseOptions );
> }
> 
> This resolves to an EnumOptions struct that parses all members of an 
> enumeration, and generates bits in a bitfield for them and wraps it all 
> up with properties. So now the following is possible:
> 
> static if( Options.Option1 )
> {
>    // Do the slow thing that I would like supported
> }
> 
> Now, if you've been able to keep up here, you might have noticed 
> something. Your class has a variable template parameter list. Which 
> means we can throw anything in there. The plot thickens. This means you 
> can go one step further and make your options actually human readable:
> 
> enum ObjectVersion
> {
>    _1_0,
>    _1_1,
>    _2_0,
> }
> 
> enum ObjectEncoding
> {
>    UTF8,
>    UTF16,
>    UTF32,
>    PlainASCII,
>    ExtendedASCII,
> }
> 
> class SomeDocument( Options... )
> {
>    enum Version = OptionsOf( ObjectVersion, Options );
>    enum Encoding = OptionsOf( ObjectVersion, Options );
> }
> 
> alias DocumentType = SomeDocument!( ObjectVersion._1_0, 
> ObjectEncoding.PlainASCII );
> alias DocumentType2 = SomeDocument!( ObjectEncoding.UTF8, 
> ObjectVersion._2_0 );
> 
> Pretty, pretty, pretty good.
> 
> With this in the back of my mind, I've been able to expand Binderoo's 
> module binding to be a bit more user friendly. I've got a new 
> BindModules mixin, which unlike the existing mixins are more of a 
> pull-in system rather than a push-in system. Basically, rather than 
> BindModule at the bottom of each module, you put a single BindModules at 
> the bottom of one module and list every module you want as a parameter 
> to it.
> 
> The mixin needs to do a few things though. The list of modules is one 
> thing. A bunch of behaviour options is another. And, since the mixin 
> adds a static shared this, a list of functions that need to be executed 
> for module initialisation. The first two are pretty easy to deal with:
> 
> enum Modules = ExtractAllOf!( string, Options );
> enum BindOptions = OptionsOf!( BindOption, Options );
> 
> But the functions, they're a bit trickier. So I made a new trait in 
> Binderoo's traits module called ExtractTupleOf. The template prototype 
> is the following:
> 
> template ExtractTupleOf( alias TestTemplate, Symbols... )
> 
> That first parameter is the interesting one. It's essentially an 
> uninstantiated template that doubles as a lambda. The template is 
> expected to be an eponymous template aliasing to a boolean value, and 
> take one parameter (although, theoretically, a CTFE bool function(T)() 
> would also work). ExtractTupleOf will static foreach over each symbol in 
> Symbols, and static if( TestTemplate!Symbol ) each one. If it returns 
> true, then that symbol is extracted and put in a new tuple.
> 
> What does this mean? It means I can do this:
> 
> import std.traits : isSomeFunction;
> mixin BindModuleStaticSetup!( ExtractTupleOf!( isSomeFunction, Options ) );
> 
> All of this is very definitely well in the real of "Let's see you do 
> that in the hour it took me to throw it all together, C++!" territory. 
> And I'd really like to see people pick up this pattern rather than 
> emulate the old ways.

Another option[0] ;)

[0] https://github.com/rikkimax/DIPs/blob/named_args/DIPs/DIP1xxx-RC.md


More information about the Digitalmars-d mailing list