www.digitalmars.com         C & C++   DMDScript  

digitalmars.D - Composite Pattern and simplificaton

reply "JS" <js.mdnq gmail.com> writes:
Is there any nifty D features that allow one to simplify the 
composite pattern,

e.g.,

     class A : B
     {
         B b;
     }

Where I would like to have class A's implementation of B be use 
b. This would avoid a lot of boilerplate code if just redirecting 
A's implementation of B to b.

e.g.,

    class A : B(use b)
    {
        B b;
    }


or maybe more D'ish

    class A : B
    {
       B b;
       alias b A:B;
    }

probably some fancy mixin could be used:

    class A : B
    {
       B b;
       mixin simpleComposite(b);   // Just implements B with 
redirection to b
    }
Jul 03 2013
next sibling parent "Dicebot" <public dicebot.lv> writes:
On Wednesday, 3 July 2013 at 10:12:34 UTC, JS wrote:
 ...

class A { B b; alias b this; } ?
Jul 03 2013
prev sibling next sibling parent "JS" <js.mdnq gmail.com> writes:
On Wednesday, 3 July 2013 at 10:41:02 UTC, Dicebot wrote:
 On Wednesday, 3 July 2013 at 10:12:34 UTC, JS wrote:
 ...

class A { B b; alias b this; } ?

Sorry, I left out one important detail, B is an interface so A has to implement B's methods. If B were class then this would not be a problem and alias this would not be required, and I could easily override any implementation details. interface B { void foo(); } class A : B { B b; void foo() { return b.foo(); } } For each method in B, I have to write a duplicate method in A that redirects to b. I do not want to do this and I also want to partially implement B explicitly. I think a mixin and traits could be used, at least to implicitly implement all of B, but I'm not sure about only partially.
Jul 03 2013
prev sibling next sibling parent "JS" <js.mdnq gmail.com> writes:
I'm trying to write a mixin that will solve the problem but since 
I can't seem to build up strings progressively it's a huge pain 
in the ass.


import std.stdio, std.cstream, std.traits, std.conv;

interface A { void myfunc(real, int, string);  property int 
myvalue(); }


template reverseEnum(alias e, alias value)
{
	string eval()
	{
		foreach(v; EnumMembers!e)
		{
			static if (to!int(v) - to!int(value) == 0)
				return to!string(v);
		}
		
		return "";
	}
	enum reverseEnum = '"' ~ eval() ~ '"';
}
template evaluateParameterTypeTuple(string x) { enum 
evaluateParameterTypeTuple = 
"ParameterTypeTuple!("~x~").stringof"; }
template evaluateReturnType(string x) { enum evaluateReturnType = 
"ReturnType!("~x~").stringof"; }
template evaluateAttributes(string x) { enum evaluateAttributes = 
"functionAttributes!("~x~")"; }
template replaceString(string x, string y, string z) { string 
eval() { static if(x == y) return z; return x; } enum 
replaceString = "'~eval()~'"';
}

template implementInterface(alias I, alias i)
{
	
	static string implement()
	{
		//string x;
		foreach(name; __traits(allMembers, I))
		{
			enum qname = I.stringof ~ "." ~ name;
			enum a = 
mixin(replaceString!(" "~mixin(reverseEnum!(FunctionAttribute, 
to!int(mixin(evaluateAttributes!(qname))))) ~ " ", " none ", ""));
			enum bbody = " { return " ~ i.stringof ~ "." ~ name ~ "(); }";
			enum x = a ~ mixin(evaluateReturnType!(qname)) ~ " " ~ name ~ 
mixin(evaluateParameterTypeTuple!(qname)) ~ bbody;
			pragma(msg, ">" ~ x);
		}
		
		return "";
	}
	enum implementInterface = implement();
}

class B : A
{
	A a;
	mixin(implementInterface!(A, a));
}


void main(string[] args)
{
	din.getc();
}



The code produces results like

void myfunc(real, int, string) { return a.myfunc(); } and
 property myvalue() { return a.myvalue(); }

which at some point, when finished, can be inserted into class B 
to implement A.

The problem, is that I can't build up the strings progressively 
in the mixin templates because I get errors that the variable 
can't be read at compile time.

I can do stuff like

enum x = "a" ~ "b" ~ "c";

but not

enum x = "a";
x ~= "b";
x ~= "c";

or whatever...

strings are about useless as they have the same issue.

I understand that compile time evaluation needs to have static 
behavior but I'm not doing anything that can't be done at compile 
time.

I had to write a template just to replace a the "none" attribute 
because I couldn't easily do it directly.  I can't have the 
reverseEnum work with multiple flags because I can't build up the 
attribute string progressively. While I imagine it is possible to 
hack it up by tricking the compiler it starts to feel C'sh with 
something that should be pretty simple...

Maybe someone has some ideas?
Jul 03 2013
prev sibling next sibling parent Kenji Hara <k.hara.pg gmail.com> writes:
--bcaec53f2e31f425ba04e0a940ee
Content-Type: text/plain; charset=UTF-8

2013/7/4 JS <js.mdnq gmail.com>

 I'm trying to write a mixin that will solve the problem but since I can't
 seem to build up strings progressively it's a huge pain in the ass.

How about this? Unfortunately this code doesn't work with git head, because it requires both one small std.typecons.wrap bug fix and its one small improvement. But I'll make a PR to fix them soon. // ------------- // core side import std.typecons : Proxy; import std.typecons : wrap, unwrap; import std.typecons : WhiteHole, NotImplementedError; import std.exception : enforce; public interface Interface { int foo(); int bar(); } private class Pluggable { Interface impl; mixin Proxy!impl; static Interface defaultImpl; static this() { defaultImpl = new WhiteHole!Interface(); } this() { impl = defaultImpl; } int foo() { return 1; } // pre-defined default behavior } public Interface createPluggable() { return new Pluggable().wrap!Interface; } public Interface setPlugin(Interface i, Interface plugin) { Pluggable p = enforce(i.unwrap!Pluggable); p.impl = plugin ? plugin : Pluggable.defaultImpl; return i; } // ------------- // user side class Plugin : Interface { override int foo() { return 10; } override int bar() { return 20; } } void main() { import std.exception : assertThrown; Interface i = createPluggable(); assert(i.foo() == 1); assertThrown!NotImplementedError(i.bar()); i.setPlugin(new Plugin()); // set plug-in assert(i.foo() == 1); assert(i.bar() == 20); i.setPlugin(null); // remove plug-in assert(i.foo() == 1); assertThrown!NotImplementedError(i.bar()); } Kenji Hara --bcaec53f2e31f425ba04e0a940ee Content-Type: text/html; charset=UTF-8 Content-Transfer-Encoding: quoted-printable <div dir=3D"ltr"><div class=3D"gmail_extra"><div class=3D"gmail_quote">2013= /7/4 JS <span dir=3D"ltr">&lt;<a href=3D"mailto:js.mdnq gmail.com" target= =3D"_blank">js.mdnq gmail.com</a>&gt;</span><br><blockquote class=3D"gmail_= quote" style=3D"margin-top:0px;margin-right:0px;margin-bottom:0px;margin-le= ft:0.8ex;border-left-width:1px;border-left-color:rgb(204,204,204);border-le= ft-style:solid;padding-left:1ex"> I&#39;m trying to write a mixin that will solve the problem but since I can= &#39;t seem to build up strings progressively it&#39;s a huge pain in the a= ss.<br></blockquote><div></div></div><br></div><div class=3D"gmail_extra"> How about this?</div><div class=3D"gmail_extra">Unfortunately this code doe= sn&#39;t work with git head, because it requires both one small std.typecon= s.wrap bug fix and its one small improvement. But I&#39;ll make a PR to fix= them soon.</div> <div class=3D"gmail_extra"><br></div><div class=3D"gmail_extra"><div class= =3D"gmail_extra">// -------------</div><div class=3D"gmail_extra">// core s= ide</div><div class=3D"gmail_extra"><br></div><div class=3D"gmail_extra">im= port std.typecons : Proxy;</div> <div class=3D"gmail_extra">import std.typecons : wrap, unwrap;</div><div cl= ass=3D"gmail_extra">import std.typecons : WhiteHole, NotImplementedError;</= div><div class=3D"gmail_extra">import std.exception : enforce;</div><div cl= ass=3D"gmail_extra"> <br></div><div class=3D"gmail_extra">public interface Interface</div><div c= lass=3D"gmail_extra">{</div><div class=3D"gmail_extra">=C2=A0 =C2=A0 int fo= o();</div><div class=3D"gmail_extra">=C2=A0 =C2=A0 int bar();</div><div cla= ss=3D"gmail_extra">}</div> <div class=3D"gmail_extra"><br></div><div class=3D"gmail_extra">private cla= ss Pluggable</div><div class=3D"gmail_extra">{</div><div class=3D"gmail_ext= ra">=C2=A0 =C2=A0 Interface impl;</div><div class=3D"gmail_extra">=C2=A0 = =C2=A0 mixin Proxy!impl;</div> <div class=3D"gmail_extra"><br></div><div class=3D"gmail_extra">=C2=A0 =C2= =A0 static Interface defaultImpl;</div><div class=3D"gmail_extra">=C2=A0 = =C2=A0 static this() { defaultImpl =3D new WhiteHole!Interface(); }</div><d= iv class=3D"gmail_extra"><br> </div><div class=3D"gmail_extra">=C2=A0 =C2=A0 this() { impl =3D defaultImp= l; }</div><div class=3D"gmail_extra"><br></div><div class=3D"gmail_extra">= =C2=A0 =C2=A0 int foo() { return 1; } =C2=A0 =C2=A0 // pre-defined default = behavior</div><div class=3D"gmail_extra"> }<br></div><div class=3D"gmail_extra"><br></div><div class=3D"gmail_extra">= public Interface createPluggable()</div><div class=3D"gmail_extra">{</div><= div class=3D"gmail_extra">=C2=A0 =C2=A0 return new Pluggable().wrap!Interfa= ce;</div><div class=3D"gmail_extra"> }</div><div class=3D"gmail_extra">public Interface setPlugin(Interface i, I= nterface plugin)</div><div class=3D"gmail_extra">{</div><div class=3D"gmail= _extra">=C2=A0 =C2=A0 Pluggable p =3D enforce(i.unwrap!Pluggable);</div><di= v class=3D"gmail_extra"> =C2=A0 =C2=A0 p.impl =3D plugin ? plugin : Pluggable.defaultImpl;</div><div= class=3D"gmail_extra">=C2=A0 =C2=A0 return i;</div><div class=3D"gmail_ext= ra">}</div><div class=3D"gmail_extra"><br></div><div class=3D"gmail_extra">= // -------------</div><div class=3D"gmail_extra"> // user side</div><div class=3D"gmail_extra"><br></div><div class=3D"gmail_= extra">class Plugin : Interface</div><div class=3D"gmail_extra">{</div><div= class=3D"gmail_extra">=C2=A0 =C2=A0 override int foo() { return 10; }</div=
<div class=3D"gmail_extra">

tra">}</div><div class=3D"gmail_extra"><br></div><div class=3D"gmail_extra"=
void main()</div><div class=3D"gmail_extra">{</div><div class=3D"gmail_ext=

<div class=3D"gmail_extra"><br></div><div class=3D"gmail_extra">=C2=A0 =C2= =A0 Interface i =3D createPluggable();</div><div class=3D"gmail_extra"><br>= </div><div class=3D"gmail_extra">=C2=A0 =C2=A0 assert(i.foo() =3D=3D 1);</d= iv><div class=3D"gmail_extra">=C2=A0 =C2=A0 assertThrown!NotImplementedErro= r(i.bar());</div> <div class=3D"gmail_extra"><br></div><div class=3D"gmail_extra">=C2=A0 =C2= =A0 i.setPlugin(new Plugin()); =C2=A0// set plug-in</div><div class=3D"gmai= l_extra"><br></div><div class=3D"gmail_extra">=C2=A0 =C2=A0 assert(i.foo() = =3D=3D 1);</div><div class=3D"gmail_extra"> =C2=A0 =C2=A0 assert(i.bar() =3D=3D 20);</div><div class=3D"gmail_extra"><b= r></div><div class=3D"gmail_extra">=C2=A0 =C2=A0 i.setPlugin(null); =C2=A0 = =C2=A0 =C2=A0 =C2=A0 =C2=A0// remove plug-in</div><div class=3D"gmail_extra= "><br></div><div class=3D"gmail_extra">=C2=A0 =C2=A0 assert(i.foo() =3D=3D = 1);</div> <div class=3D"gmail_extra">=C2=A0 =C2=A0 assertThrown!NotImplementedError(i= .bar());</div><div class=3D"gmail_extra">}</div><div><br></div><div>Kenji H= ara</div></div></div> --bcaec53f2e31f425ba04e0a940ee--
Jul 03 2013
prev sibling next sibling parent "Baz" <burg.basile yahoo.com> writes:
On Wednesday, 3 July 2013 at 10:12:34 UTC, JS wrote:
 Is there any nifty D features that allow one to simplify the 
 composite pattern,

 e.g.,

     class A : B
     {
         B b;
     }

 Where I would like to have class A's implementation of B be use 
 b. This would avoid a lot of boilerplate code if just 
 redirecting A's implementation of B to b.

 e.g.,

    class A : B(use b)
    {
        B b;
    }


 or maybe more D'ish

    class A : B
    {
       B b;
       alias b A:B;
    }

 probably some fancy mixin could be used:

    class A : B
    {
       B b;
       mixin simpleComposite(b);   // Just implements B with 
 redirection to b
    }

You can also try to overload opCast() in the container class, a bit in the same fashion that the 'alias this' stuff (except that alias this will not work with many sub classes). The idea is exposed here: http://dpaste.dzfl.pl/33d1b2c3
Jul 04 2013
prev sibling next sibling parent "JS" <js.mdnq gmail.com> writes:
On Thursday, 4 July 2013 at 07:03:39 UTC, Baz wrote:
 On Wednesday, 3 July 2013 at 10:12:34 UTC, JS wrote:
 Is there any nifty D features that allow one to simplify the 
 composite pattern,

 e.g.,

    class A : B
    {
        B b;
    }

 Where I would like to have class A's implementation of B be 
 use b. This would avoid a lot of boilerplate code if just 
 redirecting A's implementation of B to b.

 e.g.,

   class A : B(use b)
   {
       B b;
   }


 or maybe more D'ish

   class A : B
   {
      B b;
      alias b A:B;
   }

 probably some fancy mixin could be used:

   class A : B
   {
      B b;
      mixin simpleComposite(b);   // Just implements B with 
 redirection to b
   }

You can also try to overload opCast() in the container class, a bit in the same fashion that the 'alias this' stuff (except that alias this will not work with many sub classes). The idea is exposed here: http://dpaste.dzfl.pl/33d1b2c3

This isn't what I'm really after. At some point I might want to implement the interface partially. interface A { ... } class B : A { A a; // implementation of A goes here, but I'd like it to just redirect to a for all that I do not explicitly implement // } There seems to be some cool stuff that Kenji has used that I was not familiar with. I'm not quite sure if it does exactly what I'm asking but it looks close.
Jul 04 2013
prev sibling next sibling parent "JS" <js.mdnq gmail.com> writes:
On Thursday, 4 July 2013 at 05:57:45 UTC, Kenji Hara wrote:
 2013/7/4 JS <js.mdnq gmail.com>

 I'm trying to write a mixin that will solve the problem but 
 since I can't
 seem to build up strings progressively it's a huge pain in the 
 ass.

How about this? Unfortunately this code doesn't work with git head, because it requires both one small std.typecons.wrap bug fix and its one small improvement. But I'll make a PR to fix them soon. // ------------- // core side import std.typecons : Proxy; import std.typecons : wrap, unwrap; import std.typecons : WhiteHole, NotImplementedError; import std.exception : enforce; public interface Interface { int foo(); int bar(); } private class Pluggable { Interface impl; mixin Proxy!impl; static Interface defaultImpl; static this() { defaultImpl = new WhiteHole!Interface(); } this() { impl = defaultImpl; } int foo() { return 1; } // pre-defined default behavior } public Interface createPluggable() { return new Pluggable().wrap!Interface; } public Interface setPlugin(Interface i, Interface plugin) { Pluggable p = enforce(i.unwrap!Pluggable); p.impl = plugin ? plugin : Pluggable.defaultImpl; return i; } // ------------- // user side class Plugin : Interface { override int foo() { return 10; } override int bar() { return 20; } } void main() { import std.exception : assertThrown; Interface i = createPluggable(); assert(i.foo() == 1); assertThrown!NotImplementedError(i.bar()); i.setPlugin(new Plugin()); // set plug-in assert(i.foo() == 1); assert(i.bar() == 20); i.setPlugin(null); // remove plug-in assert(i.foo() == 1); assertThrown!NotImplementedError(i.bar()); } Kenji Hara

Unfortunately I can't test it until head is updated but it looks close to what I'm wanting... not 100% sure though. At the very least wrap is something similar to what I was trying to implement more or less so I can look in that for details if I need to.
Jul 04 2013
prev sibling parent "Baz" <burg.basile yahoo.com> writes:
On Thursday, 4 July 2013 at 09:26:24 UTC, JS wrote:
 On Thursday, 4 July 2013 at 07:03:39 UTC, Baz wrote:
 On Wednesday, 3 July 2013 at 10:12:34 UTC, JS wrote:
 Is there any nifty D features that allow one to simplify the 
 composite pattern,

 e.g.,

   class A : B
   {
       B b;
   }

 Where I would like to have class A's implementation of B be 
 use b. This would avoid a lot of boilerplate code if just 
 redirecting A's implementation of B to b.

 e.g.,

  class A : B(use b)
  {
      B b;
  }


 or maybe more D'ish

  class A : B
  {
     B b;
     alias b A:B;
  }

 probably some fancy mixin could be used:

  class A : B
  {
     B b;
     mixin simpleComposite(b);   // Just implements B with 
 redirection to b
  }

You can also try to overload opCast() in the container class, a bit in the same fashion that the 'alias this' stuff (except that alias this will not work with many sub classes). The idea is exposed here: http://dpaste.dzfl.pl/33d1b2c3

This isn't what I'm really after. At some point I might want to implement the interface partially. interface A { ... } class B : A { A a; // implementation of A goes here, but I'd like it to just redirect to a for all that I do not explicitly implement // } There seems to be some cool stuff that Kenji has used that I was not familiar with. I'm not quite sure if it does exactly what I'm asking but it looks close.

You're right, actually my answer was a bit off-topic since it was mostly related to the first 'Dicebot' answer and the 'alias this' stuff. BTW it's not perfect since using the opCast doesn't allow to 'mirror' several Objects of the same class...
Jul 04 2013