www.digitalmars.com         C & C++   DMDScript  

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

reply Ethan <gooberman gmail.com> writes:
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.
May 20 2018
next sibling parent reply Neia Neutuladh <neia ikeran.org> writes:
On Monday, 21 May 2018 at 00:13:26 UTC, Ethan wrote:
 Code for context: 
 https://github.com/GooberMan/binderoo/blob/master/binderoo_client/d/src/binderoo/util/enumoptions.d
This looks good. One small caveat: alias DocumentType = SomeDocument!(ObjectVersion._1_0, ObjectEncoding.UTF8); alias DocumentType2 = SomeDocument!(ObjectEncoding.UTF8, ObjectVersion._1_0); These are not the same type; they're two identical types with different mangles. You can fix that with a layer of indirection: template SomeDocument(Options...) { alias SomeDocument = SomeDocumentImpl!(OptionsOf!(DocumentParams, Options)); }
May 20 2018
parent reply Manu <turkeyman gmail.com> writes:
I don't really like that SomeObject() will be instantiated a crap load
of times for every possible combination and order of options that a
user might want to supply. How do you control the bloat in a way that
people won't mess up frequently?

On 20 May 2018 at 17:58, Neia Neutuladh via Digitalmars-d
<digitalmars-d puremagic.com> wrote:
 On Monday, 21 May 2018 at 00:13:26 UTC, Ethan wrote:
 Code for context:
 https://github.com/GooberMan/binderoo/blob/master/binderoo_client/d/src/binderoo/util/enumoptions.d
This looks good. One small caveat: alias DocumentType = SomeDocument!(ObjectVersion._1_0, ObjectEncoding.UTF8); alias DocumentType2 = SomeDocument!(ObjectEncoding.UTF8, ObjectVersion._1_0); These are not the same type; they're two identical types with different mangles. You can fix that with a layer of indirection: template SomeDocument(Options...) { alias SomeDocument = SomeDocumentImpl!(OptionsOf!(DocumentParams, Options)); }
May 20 2018
parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
On Monday, 21 May 2018 at 01:53:20 UTC, Manu wrote:
 I don't really like that SomeObject() will be instantiated a 
 crap load of times for every possible combination and order of 
 options that a user might want to supply. How do you control 
 the bloat in a way that people won't mess up frequently?
Just sort types by .stringof in a thin forwarding template, we have sort in std.meta now.
 On 20 May 2018 at 17:58, Neia Neutuladh via Digitalmars-d 
 <digitalmars-d puremagic.com> wrote:
 [...]
May 20 2018
prev sibling next sibling parent reply Paul Backus <snarwin gmail.com> writes:
On Monday, 21 May 2018 at 00:13:26 UTC, Ethan wrote:
 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.
Am I missing something, or is this the same thing as `std.meta: Filter`?
May 20 2018
parent reply Ethan <gooberman gmail.com> writes:
On Monday, 21 May 2018 at 03:30:37 UTC, Paul Backus wrote:
 Am I missing something, or is this the same thing as `std.meta: 
 Filter`?
Nope, I am missing something. I don't find the std library documentation anywhere near as easy to look through as something like cppreference.com, so I only tend to go there when I know what I'm looking for. And in this case, I know the std.meta containers are implemented using recursive templates. Which is not particularly nice on the compiler. The difference with mine (apart from the name mismatch) is that I use string mixins and iterate over items in a for loop, which I've done with quite a few traits to try and get compile times a bit more reasonable on larger codebases. Otherwise, the usage technique is exactly the same.
May 21 2018
parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 5/21/18 5:30 AM, Ethan wrote:
 On Monday, 21 May 2018 at 03:30:37 UTC, Paul Backus wrote:
 Am I missing something, or is this the same thing as `std.meta: Filter`?
Nope, I am missing something. I don't find the std library documentation anywhere near as easy to look through as something like cppreference.com, so I only tend to go there when I know what I'm looking for. And in this case, I know the std.meta containers are implemented using recursive templates. Which is not particularly nice on the compiler. The difference with mine (apart from the name mismatch) is that I use string mixins and iterate over items in a for loop, which I've done with quite a few traits to try and get compile times a bit more reasonable on larger codebases.
Filter was written before static foreach existed. This is a pretty low-hanging fruit if anyone wants to try it out. -Steve
May 21 2018
parent Ethan <gooberman gmail.com> writes:
On Monday, 21 May 2018 at 13:22:33 UTC, Steven Schveighoffer 
wrote:
 Filter was written before static foreach existed. This is a 
 pretty low-hanging fruit if anyone wants to try it out.

 -Steve
I've gone to the effort after all, I might as well just port my code across. I'll look in to it. Probably not this week though, off to Copenhagen/Malmö in a few hours.
May 21 2018
prev sibling next sibling parent reply rikki cattermole <rikki cattermole.co.nz> writes:
On 21/05/2018 12:13 PM, Ethan wrote:
 Code for context: 
 https://github.com/GooberMan/binderoo/blob/master/binderoo_client/d/src/bindero
/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
May 21 2018
parent crimaniak <crimaniak gmail.com> writes:
On Monday, 21 May 2018 at 07:10:34 UTC, rikki cattermole wrote:
 alias DocumentType = SomeDocument!( ObjectVersion._1_0, 
 ObjectEncoding.PlainASCII );
 alias DocumentType2 = SomeDocument!( ObjectEncoding.UTF8, 
 ObjectVersion._2_0 );
typedef basic_string<char> string; typedef basic_string<wchar_t> wstring; So, as I understand, basic idea can be reduced to "Let's use traits for options!", isn't it?
May 21 2018
prev sibling next sibling parent Ethan <gooberman gmail.com> writes:
On Monday, 21 May 2018 at 01:53:20 UTC, Manu wrote:
 I don't really like that SomeObject() will be instantiated a 
 crap load of times for every possible combination and order of 
 options that a user might want to supply. How do you control 
 the bloat in a way that people won't mess up frequently?
On Monday, 21 May 2018 at 00:58:10 UTC, Neia Neutuladh wrote:
 These are not the same type; they're two identical types with 
 different mangles.
Yep, entirely something I missed when I did the code. Classic "Works For Me!(TM)" moment. As mentioned by Neia, the thin wrapper would be my preferred way of approaching that, at which point your implementation class looks like: class Impl( alias Version, alias Encoding ) There's nothing stopping you doing the proposed std.meta.sort either, but that's essentially doubling up on work when you do something like what I illustrated.
May 21 2018
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On Monday, 21 May 2018 at 00:13:26 UTC, 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... )
 {
 }
Unless I'm missing something we can do a lot better in D : struct Options { bool foo; bool bar; int a; string b; } class SomeObject(Options options) { static if (options.foo) { } } No magic templates or anything fancy. -- /Jacob Carlborg
May 21 2018
next sibling parent reply Steven Schveighoffer <schveiguy yahoo.com> writes:
On 5/21/18 10:07 AM, Jacob Carlborg wrote:
 On Monday, 21 May 2018 at 00:13:26 UTC, Ethan wrote:
 Code for context: 
 https://github.com/GooberMan/binderoo/blob/master/binderoo_client/d/src/bindero
/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... )
 {
 }
Unless I'm missing something we can do a lot better in D : struct Options {     bool foo;     bool bar;     int a;     string b; } class SomeObject(Options options) {     static if (options.foo)     {     } } No magic templates or anything fancy.
But how do you use it? SomeObject!(Options(true, false, 42, "guess what this does")) -Steve
May 21 2018
parent reply Jacob Carlborg <doob me.com> writes:
On Monday, 21 May 2018 at 14:23:07 UTC, Steven Schveighoffer 
wrote:

 But how do you use it?

 SomeObject!(Options(true, false, 42, "guess what this does"))
Yes, or if you want something more readable: enum Options options = { foo: true, bar: false, a: 42, b: "guess what this does" }; SomeObject!options o; -- /Jacob Carlborg
May 21 2018
next sibling parent Sjoerd Nijboer <sjoerdnijboer gmail.com> writes:
On Monday, 21 May 2018 at 14:36:32 UTC, Jacob Carlborg wrote:
 enum Options options = { foo: true, bar: false, a: 42, b:  
 "guess what this does" };
 SomeObject!options o;

 --
 /Jacob Carlborg
I like this especially if you mix it with: enum Options options = { foo: true, bar: false, a: 42, b: "guess what this does" }; SomeObject!options o; class myClass(Options options) { void myFunction() { static if(options.foo) { ... //some code } } } class MyOtherClass(Options options) { void myOtherFunction() { static if(options.foo && options.bar) { ... //some code } } } All of a sudden you encaptulate your template arguments whitout a lot of effort and allow them to be easily re-used, computed and altered. It becomes even more convenient if you're computing the content of `Options` at compile time using some complicated statements.
May 21 2018
prev sibling parent reply Ethan <gooberman gmail.com> writes:
On Monday, 21 May 2018 at 14:36:32 UTC, Jacob Carlborg wrote:
 enum Options options = { foo: true, bar: false, a: 42, b:  
 "guess what this does" };
 SomeObject!options o;
Yeah, so this is one reason why I went the parsing way. enum Options Options1 = { foo: false, a: 5 }; SomeObject!Options1 object1; enum Options Options2 = { foo: true, b: "Totally different" }; SomeObject!Options2 object2; Repeat ad infinitum for each slightly different configuration you want. I always make the point of programmers being lazy by definition, and not being able to do something as simple as declare a type with a single statement is an clear example of reducing usability - which is a critical consideration the lazier a programmer is. (As mentioned earlier in the thread, template parameter naming would make this entire thing irrelevant. As would being able to inline initialise a struct with named variables.) Still, there's a few other things going on with my method. The EnumOptions class turns any enumeration in to a bitfield (regardless of underlying type). My own bitfield object in Binderoo does not have the limitations of std.bitmanip.bitfields as the type it creates internally is a ubyte[Size] aligned and padded out to byte boundaries. An enum declaration of any length is thus kosher. So perhaps EnumBitfield is the better name for the object. Using it in such a way is essentially more used to change default behaviour more than specify behaviour to begin with. It's also an enabler. My Binderoo example at the bottom of my original post has multiple parameters of multiple different types. But what if I need to add more to it? Easy enough to add a variable to an options structure, you might think. But what if I need a tuple of types? Then the options structure becomes templated to hold the tuple. And so on and so forth. Parsing template parameters doesn't lock you in to such unintended design pattern consequences. Parsing parameters also solves a problem I come across a bit too often - the desire to have multiple variable parameter sets as template parameters. Using that light wrapper template method also ensures that I can parse and sort parameters to cut down on unnecessary template reinstantiation.
May 22 2018
parent Sjoerd Nijboer <sjoerdnijboer gmail.com> writes:
On Tuesday, 22 May 2018 at 14:56:52 UTC, Ethan wrote:
 Repeat ad infinitum for each slightly different configuration 
 you want. I always make the point of programmers being lazy by 
 definition, and not being able to do something as simple as 
 declare a type with a single statement is an clear example of 
 reducing usability - which is a critical consideration the 
 lazier a programmer is.
This is the place where an editor could greatly improve productivity. Writing the parameterized instantiation of `enum option = Option(foo: true, bar: 5 ..);` and just being able to press a hotkey in a code editor and have the type automagically generated for you would nullify this benefit and at thesame time giving you all benefits of a struct. It even allows you to later add in some specific kind of initialization of these variables whitout much hastle. This is something i really miss in D. :/
May 22 2018
prev sibling parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Monday, May 21, 2018 14:07:45 Jacob Carlborg via Digitalmars-d wrote:
 On Monday, 21 May 2018 at 00:13:26 UTC, 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... )
 {
 }
Unless I'm missing something we can do a lot better in D : struct Options { bool foo; bool bar; int a; string b; } class SomeObject(Options options) { static if (options.foo) { } } No magic templates or anything fancy. --
That's basically what dxml does except that it takes advantage of the fact that each member is a different type (because each is a differnt instance of std.typecons.Flag) so that it can have a variadic function which takes any of the arguments in any order. e.g. enum config = makeConfig(SkipComments.yes, SplitOnEmpty.yes); auto range = parseXML!config(xml); or auto range = parseXML!(makeConfig(SkipComments.yes, SplitOnEmpty.yes))(xml); - Jonathan M Davis
May 22 2018
parent reply Jacob Carlborg <doob me.com> writes:
On Tuesday, 22 May 2018 at 11:08:13 UTC, Jonathan M Davis wrote:

 That's basically what dxml does except that it takes advantage 
 of the fact that each member is a different type (because each 
 is a differnt instance of std.typecons.Flag) so that it can 
 have a variadic function which takes any of the arguments in 
 any order. e.g.

 enum config = makeConfig(SkipComments.yes, SplitOnEmpty.yes);
 auto range = parseXML!config(xml);

 or

 auto range = parseXML!(makeConfig(SkipComments.yes, 
 SplitOnEmpty.yes))(xml);
I don't see how that is any better. * It requires implementing `makeConfig` * It requires using std.typecons.Flag which I think is an ugly hack due to the lack of named arguments * It only supports boolean types By using a regular struct it's also possible to specify named arguments and in any order: struct Options { bool foo; int value; bool bar; string value2; } Options options = { value2: "asd", bar: true, foo: false }; -- /Jacob Carlborg
May 22 2018
parent reply Jonathan M Davis <newsgroup.d jmdavisprog.com> writes:
On Tuesday, May 22, 2018 14:53:50 Jacob Carlborg via Digitalmars-d wrote:
 On Tuesday, 22 May 2018 at 11:08:13 UTC, Jonathan M Davis wrote:
 That's basically what dxml does except that it takes advantage
 of the fact that each member is a different type (because each
 is a differnt instance of std.typecons.Flag) so that it can
 have a variadic function which takes any of the arguments in
 any order. e.g.

 enum config = makeConfig(SkipComments.yes, SplitOnEmpty.yes);
 auto range = parseXML!config(xml);

 or

 auto range = parseXML!(makeConfig(SkipComments.yes,
 SplitOnEmpty.yes))(xml);
I don't see how that is any better. * It requires implementing `makeConfig` * It requires using std.typecons.Flag which I think is an ugly hack due to the lack of named arguments * It only supports boolean types By using a regular struct it's also possible to specify named arguments and in any order: struct Options { bool foo; int value; bool bar; string value2; } Options options = { value2: "asd", bar: true, foo: false }; --
Honestly, I hate named argumts in general. This situation is one of the few places I've ever run into where I thought that they made any sense. This type of situation is one of the few where having a bunc of parameters and setting only a few makes much sense. So, I tend forget that the syntax that you used here even exists, and I actively avoid it when I do remember that it exists. Regardless, I was just pointing out what I'd done with dxml, since it seems to match the use case here. I'm certainly not arguing that it's always better. I don't think that I'd ever use a solution though that encouraged using the {} syntax for initializing a struct, since I wish that it wasn't even in the language. - Jonathan M Davis
May 22 2018
parent jmh530 <john.michael.hall gmail.com> writes:
On Tuesday, 22 May 2018 at 15:25:47 UTC, Jonathan M Davis wrote:
 Honestly, I hate named argumts in general. This situation is 
 one of the few places I've ever run into where I thought that 
 they made any sense. [snip]
It's quite literally the only reason I ever want named arguments.
May 22 2018