www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Deduplicating template reflection code

reply Johannes Pfau <nospam example.com> writes:
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
Apr 14
next sibling parent Stefan Koch <uplink.coder googlemail.com> writes:
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 :) )
Apr 14
prev sibling parent reply Moritz Maxeiner <moritz ucworks.org> writes:
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.
Apr 14
parent reply Johannes Pfau <nospam example.com> writes:
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
Apr 14
parent reply Moritz Maxeiner <moritz ucworks.org> writes:
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 :)
Apr 14
parent reply Johannes Pfau <nospam example.com> writes:
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
Apr 14
parent Moritz Maxeiner <moritz ucworks.org> writes:
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.
Apr 14