May 21, 2018
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
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/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.

But how do you use it?

SomeObject!(Options(true, false, 42, "guess what this does"))

-Steve
May 21, 2018
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
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
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 22, 2018
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);

http://jmdavisprog.com/docs/dxml/0.3.2/dxml_parser.html#.Config http://jmdavisprog.com/docs/dxml/0.3.2/dxml_parser.html#.makeConfig http://jmdavisprog.com/docs/dxml/0.3.2/dxml_parser.html#.parseXML

- Jonathan M Davis

May 22, 2018
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
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
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
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.