www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Call different member functions on object sequence with a generic handler function?

reply =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes:
I hope this is understandable... I have:

class C {
	void A();
	void B();
	void C();
}

I'm iterating over a set of objects of class C like:

foreach(obj; my_selected_objs){
	...
}

The iteration and code before/afterwards always looks the same, I need 
this iteration for many of the memember functions like C.A() and C.B(), 
etc.

foreach(obj; my_selected_objs){
	...
	obj.A|B|C()
	...
}

So, how can I write a generic handler that does the iteration, where I 
can specify which member function to call?

void do_A() {
	handler(C.A()); ???
}

void do_B() {
	handler(C.B()); ???
}

handler(???){
	foreach(obj: my_selected_objs){
		???
	}
}

Viele Grüsse.

-- 
Robert M. Münch
http://www.saphirion.com
smarter | better | faster
Jun 29 2018
next sibling parent reply =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 06/29/2018 09:44 AM, Robert M. Münch wrote:

 So, how can I write a generic handler that does the iteration, where I 
 can specify which member function to call?
Passing a lambda or a string mixin: import std.stdio; class C { void A() { writeln(__FUNCTION__); } void B() { writeln(__FUNCTION__); } } void handler(alias func)(C[] cs) { foreach (c; cs) { func(c); } } void handler_2(string func)(C[] cs) { foreach (c; cs) { enum expr = "c." ~ func ~ "();"; mixin(expr); } } void main() { auto cs = [ new C(), new C() ]; handler!(o => o.A())(cs); handler!(o => o.B())(cs); handler_2!"A"(cs); handler_2!"B"(cs); } Ali
Jun 29 2018
next sibling parent reply =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes:
On 2018-06-29 18:05:00 +0000, Ali ‡ehreli said:

 On 06/29/2018 09:44 AM, Robert M. Münch wrote:
 
 So, how can I write a generic handler that does the iteration, where I 
 can specify which member function to call?
Passing a lambda or a string mixin:
Hi, that was somehow in my mind but didn't made to get the syntax clear... Thanks a lot.
 import std.stdio;
 
 class C {
      void A() {
          writeln(__FUNCTION__);
      }
 
      void B() {
          writeln(__FUNCTION__);
      }
 }
 
 void handler(alias func)(C[] cs) {
      foreach (c; cs) {
          func(c);
      }
 }
Is it possible to make C[] a template type so that I can use different classes and lambdas?
 void handler_2(string func)(C[] cs) {
      foreach (c; cs) {
          enum expr = "c." ~ func ~ "();";
          mixin(expr);
      }
 }
Ok, the "strange" syntax for me is the handler(alias func) or handler(string func) ... I'm alway thinkin in lines of a parameter... which of course doesn't work. Looking at the D grammer for functions these things should be the FuncDeclaratorSuffix, is this right? And then, which part does this fall into? FuncDeclaratorSuffix: Parameters MemberFunctionAttributesopt TemplateParameters Parameters MemberFunctionAttributesop -- Robert M. Münch http://www.saphirion.com smarter | better | faster
Jun 29 2018
parent reply Timoses <timosesu gmail.com> writes:
On Friday, 29 June 2018 at 20:08:56 UTC, Robert M. Münch wrote:
 On 2018-06-29 18:05:00 +0000, Ali ‡ehreli said:

 On 06/29/2018 09:44 AM, Robert M. Münch wrote:
 void handler(alias func)(C[] cs) {
      foreach (c; cs) {
          func(c);
      }
 }
Is it possible to make C[] a template type so that I can use different classes and lambdas?
My guess would be: void handler(alias func, T)(T[] ts) { .... }
 Ok, the "strange" syntax for me is the handler(alias func) or 
 handler(string func) ... I'm alway thinkin in lines of a 
 parameter... which of course doesn't work.

 Looking at the D grammer for functions these things should be 
 the FuncDeclaratorSuffix, is this right? And then, which part 
 does this fall into?

 FuncDeclaratorSuffix:
    Parameters MemberFunctionAttributesopt
    TemplateParameters Parameters MemberFunctionAttributesop
It's definitely the "TemplateParameters" one: void func(<template params>)(<params>) { ... } is the short form of template(<template params>) { void func(<params>) { ... } } https://dlang.org/spec/template.html
Jun 29 2018
parent reply Jerry <labuurii gmail.com> writes:
On Friday, 29 June 2018 at 20:23:47 UTC, Timoses wrote:
 void handler(alias func, T)(T[] ts) {
     ....
 }
Btw this is pretty much std.algorithm.each import std.algorithm; void main() { auto cs = [ new C(), new C() ]; cs.each!(o => o.A()); } https://dlang.org/phobos/std_algorithm_iteration.html#.each
Jun 30 2018
parent reply =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes:
On 2018-06-30 22:53:47 +0000, Jerry said:

 Btw this is pretty much std.algorithm.each
 
 import std.algorithm;
 
 void main() {
    auto cs = [ new C(), new C() ];
    cs.each!(o => o.A());
 }
 
 https://dlang.org/phobos/std_algorithm_iteration.html#.each
The looping needs to be done in the handler because there are two loops running one after the other and the range to loop over is detected in the handler too. Otherwise a lot of code duplication would happen. -- Robert M. Münch http://www.saphirion.com smarter | better | faster
Jun 30 2018
next sibling parent Basile B. <b2.temp gmx.com> writes:
On Sunday, 1 July 2018 at 06:55:35 UTC, Robert M. Münch wrote:
 On 2018-06-30 22:53:47 +0000, Jerry said:

 Btw this is pretty much std.algorithm.each
 
 import std.algorithm;
 
 void main() {
    auto cs = [ new C(), new C() ];
    cs.each!(o => o.A());
 }
 
 https://dlang.org/phobos/std_algorithm_iteration.html#.each
The looping needs to be done in the handler because there are two loops running one after the other
Look at my solution then: https://forum.dlang.org/post/kmkckipiwlvwahifelnc forum.dlang.org
 and the range to loop over is detected in the handler too. 
 Otherwise a lot of code duplication would happen.
Jul 01 2018
prev sibling parent Timoses <timosesu gmail.com> writes:
On Sunday, 1 July 2018 at 06:55:35 UTC, Robert M. Münch wrote:
 The looping needs to be done in the handler because there are 
 two loops running one after the other and the range to loop 
 over is detected in the handler too. Otherwise a lot of code 
 duplication would happen.
Maybe an application for filter? C[] cs = ...; cs.filter!((c) => <predicate to be fulfilled>) .each!((c) => c.a())
Jul 01 2018
prev sibling parent reply =?iso-8859-1?Q?Robert_M._M=FCnch?= <robert.muench saphirion.com> writes:
How does it work if one of the members takes an argument that is 
deduced inside the handler function?


On 2018-06-29 18:05:00 +0000, Ali ‡ehreli said:

 Passing a lambda or a string mixin:
 
 import std.stdio;
 
 class C {
      void A() {
          writeln(__FUNCTION__);
      }
 
      void B() {
          writeln(__FUNCTION__);
      }
void C(bool flag) { writln(__FUNCTION__): }
 }
 
 void handler(alias func)(C[] cs) {
      foreach (c; cs) {
          func(c);
func(c)(flag);
      }
 }
 
 void handler_2(string func)(C[] cs) {
      foreach (c; cs) {
          enum expr = "c." ~ func ~ "();";
          mixin(expr);
      }
 }
 
 void main() {
      auto cs = [ new C(), new C() ];
 
      handler!(o => o.A())(cs);
How to get the parameter spec into the lambda?
      handler!(o => o.B())(cs);
 
      handler_2!"A"(cs);
      handler_2!"B"(cs);
 }
 
 Ali
-- Robert M. Münch http://www.saphirion.com smarter | better | faster
Jun 29 2018
parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 06/29/2018 02:40 PM, Robert M. Münch wrote:
 How does it work if one of the members takes an argument that is deduced
 inside the handler function?


 On 2018-06-29 18:05:00 +0000, Ali ‡ehreli said:

 Passing a lambda or a string mixin:

 import std.stdio;

 class C {
      void A() {
          writeln(__FUNCTION__);
      }

      void B() {
          writeln(__FUNCTION__);
      }
void C(bool flag) { writln(__FUNCTION__): }
So the others still don't have a parameter? It's not clear how the general function should know what member function is being passed. One way is to check the signature of the lambda and decide to pass the flag or not. The other way is to always pass the flag but the caller does not use it. import std.stdio; class C { void A() { writeln(__FUNCTION__); } void B() { writeln(__FUNCTION__); } void C(bool flag) { writeln(__FUNCTION__); } } void handler(alias func)(C[] cs) { bool flag; foreach (c; cs) { func(c, flag); // Always passes the flag } } void main() { auto cs = [ new C(), new C() ]; // flag is used only sometimes handler!((o, flag) => o.A())(cs); handler!((o, flag) => o.B())(cs); handler!((o, flag) => o.C(flag))(cs); } But it started to feel unnatural at this point. What is the exact use case? Perhaps there are better ways... Ali
Jun 29 2018
prev sibling next sibling parent reply Timoses <timosesu gmail.com> writes:
On Friday, 29 June 2018 at 16:44:36 UTC, Robert M. Münch wrote:
 I hope this is understandable... I have:

 class C {
 	void A();
 	void B();
 	void C();
 }

 I'm iterating over a set of objects of class C like:

 foreach(obj; my_selected_objs){
 	...
 }

 The iteration and code before/afterwards always looks the same, 
 I need this iteration for many of the memember functions like 
 C.A() and C.B(), etc.

 foreach(obj; my_selected_objs){
 	...
 	obj.A|B|C()
 	...
 }

 So, how can I write a generic handler that does the iteration, 
 where I can specify which member function to call?

 void do_A() {
 	handler(C.A()); ???
 }

 void do_B() {
 	handler(C.B()); ???
 }

 handler(???){
 	foreach(obj: my_selected_objs){
 		???
 	}
 }

 Viele Grüsse.
Trying to fiddle around a bit with delegates.. But why is the context for delegates not working for classes?? https://run.dlang.io/is/Rxeukg import std.stdio; class Aclass { int i; void foo() { writeln("called ", i); } } struct Bstruct { int i; void foo() { writeln("called ", i); } } template callFoo(T) { alias Dun = void delegate(); void callFoo(T t) { Dun fun; fun.funcptr = &T.foo; fun.ptr = cast(void*)(&t); Dun gun; gun = &t.foo; writeln(fun.ptr, " (fun.ptr of " ~ T.stringof ~ ")"); writeln(gun.ptr, " (gun.ptr of " ~ T.stringof ~ ")"); writeln(&t, " (Address of instance (context))"); fun(); gun(); } } void main() { auto a = new Aclass(); a.i = 5; auto b = Bstruct(); b.i = 7; writeln("---- Doesn't work ----"); callFoo(a); writeln("\n---- Works ----"); callFoo(b); }
Jun 29 2018
parent reply Timoses <timosesu gmail.com> writes:
On Friday, 29 June 2018 at 20:28:55 UTC, Timoses wrote:
 On Friday, 29 June 2018 at 16:44:36 UTC, Robert M. Münch wrote:

 Trying to fiddle around a bit with delegates.. But why is the 
 context for delegates not working for classes??
Aw.. Class = reference type so class A { } struct B { } void delegate() del; A a = new A(); del.ptr = a; // NOT its address, as that would be the address of the reference on the stack B b = B(); del.ptr = &b // value type => address of object in stack ... How would one print the address of the object then though? Since &a is the address of the reference' types stack location. cast(void*)a would be on solution I guess.
Jun 29 2018
parent =?UTF-8?Q?Ali_=c3=87ehreli?= <acehreli yahoo.com> writes:
On 06/29/2018 02:11 PM, Timoses wrote:

 .... How would one print the address of the object then though?
 Since &a is the address of the reference' types stack location.
Casting the reference to void* produces the address of the object: import std.stdio; class C { int i; } void main() { auto r = new C(); writeln("Reference is at ", &r); writeln("Object is at ", cast(void*)r); writeln("Member is at ", &r.i); } Sample output: Reference is at 7FFE735E6698 Object is at 7F78F02B0060 Member is at 7F78F02B0070 Ali
Jun 29 2018
prev sibling parent reply Basile B. <b2.temp gmx.com> writes:
On Friday, 29 June 2018 at 16:44:36 UTC, Robert M. Münch wrote:
 I hope this is understandable... I have:

 class C {
 	void A();
 	void B();
 	void C();
 }

 I'm iterating over a set of objects of class C like:

 foreach(obj; my_selected_objs){
 	...
 }

 The iteration and code before/afterwards always looks the same, 
 I need this iteration for many of the memember functions like 
 C.A() and C.B(), etc.

 foreach(obj; my_selected_objs){
 	...
 	obj.A|B|C()
 	...
 }

 So, how can I write a generic handler that does the iteration, 
 where I can specify which member function to call?

 void do_A() {
 	handler(C.A()); ???
 }

 void do_B() {
 	handler(C.B()); ???
 }

 handler(???){
 	foreach(obj: my_selected_objs){
 		???
 	}
 }

 Viele Grüsse.
Using opDispatch we can manage to get a voldemort able to resolve the member func A, B or C etc. --- import std.stdio; class C { void A(){writeln(__FUNCTION__);} void B(int i){writeln(__FUNCTION__, " ", i);} } auto handler(T)(T t) { struct Handler { auto opDispatch(string member, Args...)(Args args) { import std.algorithm.iteration : each; mixin( `t.each!(a => a.` ~ member ~ `(args));` ); } } Handler h; return h; } void main() { auto cs = [new C(), new C()]; handler(cs).A(); cs.handler.B(42); // UFCS style } --- which results a very natural expression.
Jun 29 2018
parent Basile B. <b2.temp gmx.com> writes:
On Saturday, 30 June 2018 at 00:16:49 UTC, Basile B. wrote:
 On Friday, 29 June 2018 at 16:44:36 UTC, Robert M. Münch wrote:
 I hope this is understandable... I have:

 class C {
 	void A();
 	void B();
 	void C();
 }

 I'm iterating over a set of objects of class C like:

 foreach(obj; my_selected_objs){
 	...
 }

 The iteration and code before/afterwards always looks the 
 same, I need this iteration for many of the memember functions 
 like C.A() and C.B(), etc.

 foreach(obj; my_selected_objs){
 	...
 	obj.A|B|C()
 	...
 }

 So, how can I write a generic handler that does the iteration, 
 where I can specify which member function to call?

 void do_A() {
 	handler(C.A()); ???
 }

 void do_B() {
 	handler(C.B()); ???
 }

 handler(???){
 	foreach(obj: my_selected_objs){
 		???
 	}
 }

 Viele Grüsse.
Using opDispatch we can manage to get a voldemort able to resolve the member func A, B or C etc. --- import std.stdio; class C { void A(){writeln(__FUNCTION__);} void B(int i){writeln(__FUNCTION__, " ", i);} } auto handler(T)(T t) { struct Handler { auto opDispatch(string member, Args...)(Args args) { import std.algorithm.iteration : each; mixin( `t.each!(a => a.` ~ member ~ `(args));` ); } } Handler h; return h; } void main() { auto cs = [new C(), new C()]; handler(cs).A(); cs.handler.B(42); // UFCS style } --- which results a very natural expression.
insert "in" at the right place.
Jun 30 2018