www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Eliminating code duplication for static/nonstatic functions

reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
Consider a struct that may or may not have state depending on a type 
parameter:

struct S(T)
{
   enum hasState = FieldTypeTuple!T.length || isNested!T;
   static if (hasState)
     T _theT;
   else
     alias _theT = T;
   ...
}

This is really nice because I don't bloat S unnecessarily and I get to 
use _theT.method() uniformly whether or not it's the type itself or the 
data member.

The duplication problem appears when S itself must define a method that 
should be static or nonstatic depending on the existence of state. Consider:

struct S(T)
{
   ... continued from above ...
   if (hasState)
     int method() { return 1 + _theT.method(); }
   else
     static int method() { return 1 + _theT.method(); }
}

In the general case the body of S!T.method() may be of course larger, 
which makes for a nasty duplication - essentially all but the "static" 
keyword must be duplicated.

Any ideas for a clean solution? I can't get much further than string 
mixins, which wouldn't be clean :o).


Thanks,

Andrei
Sep 19 2013
next sibling parent reply "Andrej Mitrovic" <andrej.mitrovich gmail.com> writes:
On Thursday, 19 September 2013 at 17:10:43 UTC, Andrei 
Alexandrescu wrote:
 Any ideas for a clean solution? I can't get much further than 
 string mixins, which wouldn't be clean :o).
opDispatch comes to mind. You'd only need two of them, one marked static and the other one not.
Sep 19 2013
next sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2013-09-19 19:20, Andrej Mitrovic wrote:

 opDispatch comes to mind. You'd only need two of them, one marked static
 and the other one not.
You cannot overload on "static". -- /Jacob Carlborg
Sep 19 2013
parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 9/19/13, Jacob Carlborg <doob me.com> wrote:
 On 2013-09-19 19:20, Andrej Mitrovic wrote:

 opDispatch comes to mind. You'd only need two of them, one marked static
 and the other one not.
You cannot overload on "static".
Can't you read the damn OP example? He's using a static if to introduce static/non-static functions, do the same for opDispatch and you'll cut down on code.
Sep 19 2013
next sibling parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/19/13 12:42 PM, Andrej Mitrovic wrote:
 On 9/19/13, Jacob Carlborg <doob me.com> wrote:
 On 2013-09-19 19:20, Andrej Mitrovic wrote:

 opDispatch comes to mind. You'd only need two of them, one marked static
 and the other one not.
You cannot overload on "static".
Can't you read the damn OP example? He's using a static if to introduce static/non-static functions, do the same for opDispatch and you'll cut down on code.
I'm not sure I understand how that would work. Andrei
Sep 19 2013
parent reply Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 9/19/13, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 I'm not sure I understand how that would work.
----- module test; import std.traits; struct S(T) { enum hasState = FieldTypeTuple!T.length || isNested!T; static if (hasState) T _theT; else alias _theT = T; private mixin template OpDispatch() { auto opDispatch(string meth, Args...)(Args args) { static if (meth == "method") // specialization ? return 1 + _theT.method(); else return mixin("_theT." ~ meth)(args); } } static if (hasState) mixin OpDispatch!(); else static mixin OpDispatch!(); } struct A { static int method() { return 0; } } struct B { int i; int method() { return i; } } void main() { auto a = S!A(); auto b = S!B(B(1)); assert(a.method == 1); // 1 + A.method() == 1 assert(b.method == 2); // 1 + b.i == 2 assert(S!A.method == 1); // works due to static dispatch // assert(S!B.method == 2); // denied at CT (requires 'this') } -----
Sep 19 2013
parent reply Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/19/13 1:02 PM, Andrej Mitrovic wrote:
 On 9/19/13, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 I'm not sure I understand how that would work.
----- module test;
[snip] Thanks, that's the simplest and most effective. I reduced it to: struct A { enum hasState = false; private mixin template funDef() { void fun(int x) { writeln(x); } } static if (hasState) mixin funDef!(); else static mixin funDef!(); } void main() { A a; a.fun(42); } I see no way to extract the scaffolding into a library. Andrei
Sep 20 2013
next sibling parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
20-Sep-2013 19:20, Andrei Alexandrescu пишет:
 On 9/19/13 1:02 PM, Andrej Mitrovic wrote:
 On 9/19/13, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 I'm not sure I understand how that would work.
----- module test;
[snip] Thanks, that's the simplest and most effective. I reduced it to: struct A { enum hasState = false; private mixin template funDef() { void fun(int x) { writeln(x); } } static if (hasState) mixin funDef!(); else static mixin funDef!(); } void main() { A a; a.fun(42); } I see no way to extract the scaffolding into a library.
Shouldn't it as simple as a mixin-able template that simply forwards all methods of a type T (and if it has no state it does so statically). With __traits(allMembers,...) to extract all publics, it must be doable. The end result should be around the following... struct A{ //aliases type or contains state as required mixin extractState!T; } -- Dmitry Olshansky
Sep 20 2013
prev sibling next sibling parent reply "Simen Kjaeraas" <simen.kjaras gmail.com> writes:
On 2013-09-20, 17:20, Andrei Alexandrescu wrote:

 On 9/19/13 1:02 PM, Andrej Mitrovic wrote:
 On 9/19/13, Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> wrote:
 I'm not sure I understand how that would work.
----- module test;
[snip] Thanks, that's the simplest and most effective. I reduced it to: struct A { enum hasState = false; private mixin template funDef() { void fun(int x) { writeln(x); } } static if (hasState) mixin funDef!(); else static mixin funDef!(); } void main() { A a; a.fun(42); } I see no way to extract the scaffolding into a library.
Like this? import std.stdio; mixin template maybeStatic(bool isStatic, alias funDef, args...) { static if (isStatic) { static mixin funDef!args; } else { mixin funDef!args; } } struct A { enum hasState = false; private mixin template funDef() { void fun(int x) { writeln(x); } } mixin maybeStatic!(!hasState, funDef); } void main() { A a; a.fun(42); } -- Simen
Sep 20 2013
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/20/13 8:52 AM, Simen Kjaeraas wrote:
 I see no way to extract the scaffolding into a library.
Like this? import std.stdio; mixin template maybeStatic(bool isStatic, alias funDef, args...) { static if (isStatic) { static mixin funDef!args; } else { mixin funDef!args; } } struct A { enum hasState = false; private mixin template funDef() { void fun(int x) { writeln(x); } } mixin maybeStatic!(!hasState, funDef); } void main() { A a; a.fun(42); }
This is nice. Works with shared, too. Andrei
Sep 21 2013
prev sibling parent "deadalnix" <deadalnix gmail.com> writes:
On Friday, 20 September 2013 at 15:20:36 UTC, Andrei Alexandrescu 
wrote:
 On 9/19/13 1:02 PM, Andrej Mitrovic wrote:
 On 9/19/13, Andrei Alexandrescu 
 <SeeWebsiteForEmail erdani.org> wrote:
 I'm not sure I understand how that would work.
----- module test;
[snip] Thanks, that's the simplest and most effective. I reduced it to: struct A { enum hasState = false; private mixin template funDef() { void fun(int x) { writeln(x); } } static if (hasState) mixin funDef!(); else static mixin funDef!(); } void main() { A a; a.fun(42); } I see no way to extract the scaffolding into a library. Andrei
You'll be very disapointed when some of your overloads will disapear.
Sep 21 2013
prev sibling parent reply Jacob Carlborg <doob me.com> writes:
On 2013-09-19 21:42, Andrej Mitrovic wrote:

 Can't you read the damn OP example? He's using a static if to
 introduce static/non-static functions, do the same for opDispatch and
 you'll cut down on code.
Right, sorry. -- /Jacob Carlborg
Sep 19 2013
parent Andrej Mitrovic <andrej.mitrovich gmail.com> writes:
On 9/19/13, Jacob Carlborg <doob me.com> wrote:
 On 2013-09-19 21:42, Andrej Mitrovic wrote:

 Can't you read the damn OP example? He's using a static if to
 introduce static/non-static functions, do the same for opDispatch and
 you'll cut down on code.
Right, sorry.
Sorry for the harsh response on my part, I wasn't in the mood 5 minutes ago. I shouldn't take it out on fellow D programmers.
Sep 19 2013
prev sibling parent reply "safety0ff" <safety0ff.dev gmail.com> writes:
On Thursday, 19 September 2013 at 17:20:53 UTC, Andrej Mitrovic 
wrote:
 On Thursday, 19 September 2013 at 17:10:43 UTC, Andrei 
 Alexandrescu wrote:
 Any ideas for a clean solution? I can't get much further than 
 string mixins, which wouldn't be clean :o).
opDispatch comes to mind. You'd only need two of them, one marked static and the other one not.
Hello, I decided to try and solve this as an exercise with the opDispatch hint above. I am not very experienced with D, but I am sharing my solution anyway in case it is of interest: http://dpaste.dzfl.pl/3bde7db1 Enjoy/Destroy :)
Sep 19 2013
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/19/13 9:59 PM, safety0ff wrote:
 On Thursday, 19 September 2013 at 17:20:53 UTC, Andrej Mitrovic wrote:
 On Thursday, 19 September 2013 at 17:10:43 UTC, Andrei Alexandrescu
 wrote:
 Any ideas for a clean solution? I can't get much further than string
 mixins, which wouldn't be clean :o).
opDispatch comes to mind. You'd only need two of them, one marked static and the other one not.
Hello, I decided to try and solve this as an exercise with the opDispatch hint above. I am not very experienced with D, but I am sharing my solution anyway in case it is of interest: http://dpaste.dzfl.pl/3bde7db1 Enjoy/Destroy :)
Nice. If that's what you write while not being very experienced, I'm really looking forward to what you'll write when you'll be! Thanks, Andrei
Sep 20 2013
prev sibling next sibling parent reply Benjamin Thaut <code benjamin-thaut.de> writes:
Am 19.09.2013 19:10, schrieb Andrei Alexandrescu:
 Consider a struct that may or may not have state depending on a type
 parameter:

 struct S(T)
 {
    enum hasState = FieldTypeTuple!T.length || isNested!T;
    static if (hasState)
      T _theT;
    else
      alias _theT = T;
    ...
 }

 This is really nice because I don't bloat S unnecessarily and I get to
 use _theT.method() uniformly whether or not it's the type itself or the
 data member.

 The duplication problem appears when S itself must define a method that
 should be static or nonstatic depending on the existence of state.
 Consider:

 struct S(T)
 {
    ... continued from above ...
    if (hasState)
      int method() { return 1 + _theT.method(); }
    else
      static int method() { return 1 + _theT.method(); }
 }

 In the general case the body of S!T.method() may be of course larger,
 which makes for a nasty duplication - essentially all but the "static"
 keyword must be duplicated.

 Any ideas for a clean solution? I can't get much further than string
 mixins, which wouldn't be clean :o).


 Thanks,

 Andrei
Can't we make the compiler deduce the static attribute? If a template method or a method of a template struct / class does not access any of its members it becomes static automatically?
Sep 19 2013
next sibling parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/19/13 12:44 PM, Benjamin Thaut wrote:
 Can't we make the compiler deduce the static attribute? If a template
 method or a method of a template struct / class does not access any of
 its members it becomes static automatically?
That would be nice for this case, but it's a breaking change and I'm not sure how confusing it would be in general. Allowing static/nonstatic overloading would help because at least I could do this: int method() { return 1 + _theT.method(); } static if (!hasState) static int method() { return S().method(); } which still is quite a bit of work, but less net duplication. Andrei
Sep 19 2013
prev sibling parent reply Walter Bright <newshound2 digitalmars.com> writes:
On 9/19/2013 12:44 PM, Benjamin Thaut wrote:
 Can't we make the compiler deduce the static attribute? If a template method or
 a method of a template struct / class does not access any of its members it
 becomes static automatically?
static/nonstatic member functions have different ABIs, so no, it would not work.
Sep 19 2013
parent "H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
On Thu, Sep 19, 2013 at 01:32:06PM -0700, Walter Bright wrote:
 On 9/19/2013 12:44 PM, Benjamin Thaut wrote:
Can't we make the compiler deduce the static attribute? If a template
method or a method of a template struct / class does not access any
of its members it becomes static automatically?
static/nonstatic member functions have different ABIs, so no, it would not work.
There's also the problem of base class methods turning static where you didn't intend it to, for example: class Base { // This method is intended to be overridden by derived // classes. void method() { // Oops, didn't access 'this', now we're static. assert(0, "Not implemented"); } } class Derived : Base { int x; override void method() { // NG x++; } } T -- Computers aren't intelligent; they only think they are.
Sep 19 2013
prev sibling next sibling parent reply Kenji Hara <k.hara.pg gmail.com> writes:
2013/9/20 Andrei Alexandrescu <SeeWebsiteForEmail erdani.org>

 Consider a struct that may or may not have state depending on a type
 parameter:

 struct S(T)
 {
   enum hasState = FieldTypeTuple!T.length || isNested!T;
   static if (hasState)
     T _theT;
   else
     alias _theT = T;
   ...
 }

 This is really nice because I don't bloat S unnecessarily and I get to use
 _theT.method() uniformly whether or not it's the type itself or the data
 member.

 The duplication problem appears when S itself must define a method that
 should be static or nonstatic depending on the existence of state. Consider:

 struct S(T)
 {
   ... continued from above ...
   if (hasState)
     int method() { return 1 + _theT.method(); }
   else
     static int method() { return 1 + _theT.method(); }
 }

 In the general case the body of S!T.method() may be of course larger,
 which makes for a nasty duplication - essentially all but the "static"
 keyword must be duplicated.

 Any ideas for a clean solution? I can't get much further than string
 mixins, which wouldn't be clean :o).
Just an idea: string mixin + UDA? mixin(hasState ? "" : "static") int method() { return 1 + _theT.method(); } Kenji Hara
Sep 20 2013
parent Andrei Alexandrescu <SeeWebsiteForEmail erdani.org> writes:
On 9/20/13 12:34 AM, Kenji Hara wrote:
 Just an idea: string mixin + UDA?

     mixin(hasState ? "" : "static")
    int method() { return 1 + _theT.method(); }
That would be nice but would be a language change (and a subtle one... now we have mixin which introduces attributes that themselves may or may not start with " "). I decided to go with a slightly different design, but the problem remains - there is no simple way to introduce conditional attributes and qualifiers. Andrei
Sep 20 2013
prev sibling next sibling parent Dmitry Olshansky <dmitry.olsh gmail.com> writes:
19-Sep-2013 21:10, Andrei Alexandrescu пишет:
 Consider a struct that may or may not have state depending on a type
 parameter:
[snip]
 Any ideas for a clean solution? I can't get much further than string
 mixins, which wouldn't be clean :o).
It seems like s/string/template and we are there. This works for me: import std.traits; struct Static{ static int method(){ return 21; } } struct Instance{ int v; //so that it "hasState" int method(){ return 12; } } mixin template Methods() { int method() { return 1 + _theT.method(); } } struct S(T) { enum hasState = FieldTypeTuple!T.length || isNested!T; static if (hasState) T _theT; else alias _theT = T; static if (hasState) mixin Methods!(); else{ static{ mixin Methods!(); } } } void main() { S!Static s1; S!Instance s2; assert(S!Static.method() == 22); assert(s1.method() == 22); assert(s2.method() == 13); assert(s1.sizeof == 1); assert(s2.sizeof == 4); } -- Dmitry Olshansky
Sep 20 2013
prev sibling parent "Vladimir Panteleev" <vladimir thecybershadow.net> writes:
On Thursday, 19 September 2013 at 17:10:43 UTC, Andrei 
Alexandrescu wrote:
 Consider a struct that may or may not have state depending on a 
 type parameter:
I would like to note that this problem is a specific case of the general problem of changing a function's signature based on a compile-time value. The same problem exists with other attributes, and varying the presence of a template or function parameter. One example was that I wanted to define an overload set of function templates, where in some overloads, some parameters were function parameters, and in other overloads, the same parameters were template parameters (there was a significant optimization opportunity when the parameters were known at compile-time).
Sep 21 2013