Thread overview
Deduplicating template reflection code
Apr 14, 2017
Johannes Pfau
Apr 14, 2017
Stefan Koch
Apr 14, 2017
Moritz Maxeiner
Apr 14, 2017
Johannes Pfau
Apr 14, 2017
Moritz Maxeiner
Apr 14, 2017
Johannes Pfau
Apr 14, 2017
Moritz Maxeiner
April 14, 2017
I've got this code duplicated in quite some functions:

---------------------
foreach (member; __traits(derivedMembers, API))
{
    // Guards against private members
    static if (__traits(compiles, __traits(getMember, API, member)))
    {
        static if (isSomeFunction!(__traits(getMember, API, member))
                && !hasUDA!(__traits(getMember, API, member), IgnoreUDA)
                && !isSpecialFunction!member)
        {
            alias overloads = MemberFunctionsTuple!(API, member);

            foreach (MethodType; overloads)
            {
		// function dependent code here
            }
        }
    }
}
--------------------

What's the idiomatic way to refactor / reuse this code fragment?

-- Johannes

April 14, 2017
On Friday, 14 April 2017 at 08:24:00 UTC, Johannes Pfau wrote:
> I've got this code duplicated in quite some functions:
>
> ---------------------
> foreach (member; __traits(derivedMembers, API))
> {
>     // Guards against private members
>     static if (__traits(compiles, __traits(getMember, API, member)))
>     {
>         static if (isSomeFunction!(__traits(getMember, API, member))
>                 && !hasUDA!(__traits(getMember, API, member), IgnoreUDA)
>                 && !isSpecialFunction!member)
>         {
>             alias overloads = MemberFunctionsTuple!(API, member);
>
>             foreach (MethodType; overloads)
>             {
> 		// function dependent code here
>             }
>         }
>     }
> }
> --------------------
>
> What's the idiomatic way to refactor / reuse this code fragment?
>
> -- Johannes

The Idiomatic way would be to wrap it inside another template.

In a year or so you won't need to worry about template overhead anymore, (if I succeed that is :) )

April 14, 2017
On Friday, 14 April 2017 at 08:24:00 UTC, Johannes Pfau wrote:
> I've got this code duplicated in quite some functions:
>
> ---------------------
> [...]1
>
>             foreach (MethodType; overloads)
>             {
> 		// function dependent code here
>             }
> [...]2
> --------------------
>
> What's the idiomatic way to refactor / reuse this code fragment?
>
> -- Johannes

Your options are at least the following two (both untested, but should work):

Option 1: Template Mixins

---
mixin template Foo(alias API, Dg)
{
    void foo()
    {
       [...]1
             foreach (MethodType; overloads)
             {
                Dg(MethodType);
             }
       [...]2
    }
}

mixin Foo!(API, (MethodType) {
// function dependent code here
});
foo();
---

Option 2: Code generation using CTFE

---
string genFoo(alias API, string justDoIt)
{
    import std.array : appender;
    auto code = appender!string;
    code.put(`[...]1`);
    code.put(`foreach (MethodType; overloads) {`);
    code.put(justDoIt);
    code put(`}`);
    code.put(`[...]2`);
}

mixin(genFoo!(API, q{
    // function dependent code here
})());
---

Personally, I'd consider the second approach to be idiomatic, but YMMW.
April 14, 2017
Am Fri, 14 Apr 2017 08:55:48 +0000
schrieb Moritz Maxeiner <moritz@ucworks.org>:

> 
> mixin Foo!(API, (MethodType) {
> // function dependent code here
> });
> foo();
> ---
> 
> Option 2: Code generation using CTFE
> 
> ---
> string genFoo(alias API, string justDoIt)
> {
>      import std.array : appender;
>      auto code = appender!string;
>      code.put(`[...]1`);
>      code.put(`foreach (MethodType; overloads) {`);
>      code.put(justDoIt);
>      code put(`}`);
>      code.put(`[...]2`);
> }
> 
> mixin(genFoo!(API, q{
>      // function dependent code here
> })());
> ---
> 
> Personally, I'd consider the second approach to be idiomatic, but YMMW.

I'd prefer the first approach, simply to avoid string mixins. I think these can often get ugly ;-)

Is there some way to wrap the 'type selection'? In pseudo-code something like this:

enum FilteredOverloads(API) = ...

foreach(Overload, FilteredOverloads!API)
{
    ....
}
-- Johannes

April 14, 2017
On Friday, 14 April 2017 at 11:29:03 UTC, Johannes Pfau wrote:
>
> Is there some way to wrap the 'type selection'? In pseudo-code something like this:
>
> enum FilteredOverloads(API) = ...
>
> foreach(Overload, FilteredOverloads!API)
> {
>     ....
> }

Sure, but that's a bit more complex:

---
[...] // IgnoreUDA declaration
[...] // isSpecialFunction declaration

///
template FilteredOverloads(API)
{
    import std.traits : hasUDA, isSomeFunction, MemberFunctionsTuple;
    import std.meta : staticMap;
    import std.typetuple : TypeTuple;

    enum derivedMembers = __traits(derivedMembers, API);

    template MemberOverloads(string member)
    {
        static if (__traits(compiles, __traits(getMember, API, member)))
        {
            static if (isSomeFunction!(__traits(getMember, API, member))
                       && !hasUDA!(__traits(getMember, API, member), IgnoreUDA)
                       && !isSpecialFunction!member) {
                alias MemberOverloads = MemberFunctionsTuple!(API, member);
            } else {
                alias MemberOverloads = TypeTuple!();
            }
        } else {
            alias MemberOverloads = TypeTuple!();
        }
    }

    alias FilteredOverloads = staticMap!(MemberOverloads, derivedMembers);
}

//pragma(msg, FilteredOverloads!API);
foreach(Overload; FilteredOverloads!API) {
    // function dependent code here
}
---

Nested templates and std.meta are your best friends if this is the solution you prefer :)
April 14, 2017
Am Fri, 14 Apr 2017 13:41:45 +0000
schrieb Moritz Maxeiner <moritz@ucworks.org>:

> On Friday, 14 April 2017 at 11:29:03 UTC, Johannes Pfau wrote:
> >
> > Is there some way to wrap the 'type selection'? In pseudo-code something like this:
> >
> > enum FilteredOverloads(API) = ...
> >
> > foreach(Overload, FilteredOverloads!API)
> > {
> >     ....
> > }
> 
> Sure, but that's a bit more complex:
> 
> ---
> [...] // IgnoreUDA declaration
> [...] // isSpecialFunction declaration
> 
> ///
> template FilteredOverloads(API)
> {
>      import std.traits : hasUDA, isSomeFunction,
> MemberFunctionsTuple;
>      import std.meta : staticMap;
>      import std.typetuple : TypeTuple;
> 
>      enum derivedMembers = __traits(derivedMembers, API);
> 
>      template MemberOverloads(string member)
>      {
>          static if (__traits(compiles, __traits(getMember, API,
> member)))
>          {
>              static if (isSomeFunction!(__traits(getMember, API,
> member))
>                         && !hasUDA!(__traits(getMember, API,
> member), IgnoreUDA)
>                         && !isSpecialFunction!member) {
>                  alias MemberOverloads =
> MemberFunctionsTuple!(API, member);
>              } else {
>                  alias MemberOverloads = TypeTuple!();
>              }
>          } else {
>              alias MemberOverloads = TypeTuple!();
>          }
>      }
> 
>      alias FilteredOverloads = staticMap!(MemberOverloads,
> derivedMembers);
> }
> 
> //pragma(msg, FilteredOverloads!API);
> foreach(Overload; FilteredOverloads!API) {
>      // function dependent code here
> }
> ---
> 
> Nested templates and std.meta are your best friends if this is the solution you prefer :)

Great, thanks that's exactly the solution I wanted. Figuring this out by myself is a bit above my template skill level ;-)


-- Johannes

April 14, 2017
On Friday, 14 April 2017 at 17:57:49 UTC, Johannes Pfau wrote:
> Am Fri, 14 Apr 2017 13:41:45 +0000
> schrieb Moritz Maxeiner <moritz@ucworks.org>:
>
>> [...]
>
> Great, thanks that's exactly the solution I wanted. Figuring this out by myself is a bit above my template skill level ;-)
>
>
> -- Johannes


No problem, I often enough encounter instances of <strikethrough>THE DAMNED COMPILER JUST NOT DOING WHAT I WANT</strikethrough> being frustrated with templates myself. Usually looking at phobos code helps, though.