www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - How to group similar member functions from different classes?

reply cy <dlang verge.info.tm> writes:
When I define functions like:

class A {
   abstract void format(...) {...}
}

class B : A {
   void format(...) {...}
}

class C : A {
   void format(...) {...}
}

and so on, often these different member functions all share a lot 
in common. Maybe they are the only ones that require formatting 
modules, that do I/O, that do string manipulation, that sort of 
thing.

But if I made a second function, say for instance clone() or 
replaceWithDucks(), it too might import a lot of modules, and 
perform a lot of logic. And there may be many, many different 
types of object here.

In C++ I could forward declare the member functions, then put all 
"::format(...)" member functions together in their own source 
file, making everything pretty neat and tidy. How do I do that in 
D? As near as I can tell, you can only define member functions 
inside the class definition itself, and you can't add to that 
definition piece-wise like

// file 1
class A ... {
   void format(...) { ... }
}
...

// file 2
class A ... {
  void doathing() { ... }
}
...

So how would you do it? Defining A.foo, B.foo, etc in one place, 
and A.bar, B.bar, etc in another?
Jun 18 2016
next sibling parent cy <dlang verge.info.tm> writes:
On Saturday, 18 June 2016 at 07:03:25 UTC, cy wrote:
 So how would you do it? Defining A.foo, B.foo, etc in one 
 place, and A.bar, B.bar, etc in another?
The only thing I've been able to figure is a horrible hack, where your member functions are something like // off in define_foos.d template foo_for(T) { static if(is(T == A)) { enum foo_for = q{ int foo () { return bar+42; } }; } else static if(is(T == B)) { enum foo_for = q{ int foo () { return bar+23; } }; } } // in classes.d import define_foos: foo_for; struct A { int bar; mixin(foo_for!A); } struct B { int bar; mixin(foo_for!B); } // etc void main() { import std.stdio; A a = A(0); B b = B(1); writeln(b.foo()); writeln(a.foo()); }
Jun 18 2016
prev sibling next sibling parent kinke <noone nowhere.com> writes:
On Saturday, 18 June 2016 at 07:03:25 UTC, cy wrote:
 So how would you do it? Defining A.foo, B.foo, etc in one 
 place, and A.bar, B.bar, etc in another?
No such thing in D. But you can always be creative and use an overloaded helper function containing the actual implementation if you want to group the code by functionality and not by type: class A { int foo() { return doFoo(this); } class B : A { int foo() { return doFoo(this); } ... void doFoo(A a) { ... } void doFoo(B b) { ... } If you need full access to the members, the helpers would need to be in the same module, otherwise you can put them into a separate helper.
Jun 18 2016
prev sibling parent reply Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
Untested:

// foo.d
import a, b;
mixin template Foos {
     static if(is(typeof(this) == A))
     void foo() { /* implementation for A */ }
     static if(is(typeof(this) == B))
     void foo() { /* implementation for B */ }
}

// a.d
import foo;
class A {
     mixin Foos;
}

// b.d
import foo;
class B {
     mixin Foos;
}

You must not have static constructors in any of these modules 
because you'd likely get problems with circular initialization.
Jun 20 2016
parent reply cy <dlang verge.info.tm> writes:
On Monday, 20 June 2016 at 16:39:54 UTC, Marc Schütz wrote:
 Untested:
Seems to only work if A and B are both defined in the same file as Foos (defeating the purpose). Putting A and B in a.d and b.d respectively gives me these errors: a.d(2): Error: undefined identifier 'Foos' a.d(2): Error: mixin a.A.Foos!() is not defined b.d(2): Error: undefined identifier 'Foos' b.d(2): Error: mixin b.B.Foos!() is not defined I also tried switching it around, like // b.d import foos Foos; class B { mixin Foos; } but that of course gives the error: foos.d(4): Error: undefined identifier 'A' b.d(3): Error: mixin b.B.Foos!() error instantiating since you can't do a static if(typeof(this) == A) without importing A from a somehow. (you can do else static if(typeof(this) == B) without importing B though, since it does the branch for A first) I think a mixin here is just required, because you can't use an identifier before it's defined, even at compile time. Honestly, I have yet to find a use for mixin templates.
Jul 15 2016
parent Marc =?UTF-8?B?U2Now7x0eg==?= <schuetzm gmx.net> writes:
On Friday, 15 July 2016 at 17:25:23 UTC, cy wrote:
 On Monday, 20 June 2016 at 16:39:54 UTC, Marc Schütz wrote:
 Untested:
Seems to only work if A and B are both defined in the same file as Foos (defeating the purpose). Putting A and B in a.d and b.d respectively gives me these errors: a.d(2): Error: undefined identifier 'Foos' a.d(2): Error: mixin a.A.Foos!() is not defined b.d(2): Error: undefined identifier 'Foos' b.d(2): Error: mixin b.B.Foos!() is not defined I also tried switching it around, like // b.d import foos Foos; class B { mixin Foos; } but that of course gives the error: foos.d(4): Error: undefined identifier 'A' b.d(3): Error: mixin b.B.Foos!() error instantiating since you can't do a static if(typeof(this) == A) without importing A from a somehow. (you can do else static if(typeof(this) == B) without importing B though, since it does the branch for A first) I think a mixin here is just required, because you can't use an identifier before it's defined, even at compile time. Honestly, I have yet to find a use for mixin templates.
It works if you add forward declarations for the classes: import a, b; class A; class B; mixin template Foos() { static if(is(typeof(this) == A)) void foo() { /* implementation for A */ } static if(is(typeof(this) == B)) void foo() { /* implementation for B */ } }
Jul 18 2016