www.digitalmars.com         C & C++   DMDScript  

digitalmars.D.learn - Singleton Object, calling member functions using UFCS (an "ugly?"

reply james.p.leblanc <james.p.leblanc gmail.com> writes:
Dear D-ers,

For simple plotting using a gnuplot process, I have created a 
singleton object (a stripped
down minimal working example is below.)

In the example, there are two plots calls:

    1) First, call is made using the object member function 
"gp.plot(x, etc.)"
    2) The second uses a wrapper to allow use of the UFCS (uniform 
function call syntax)

The ability to use the UFCS has the usual UFCS advantages, 
additionally the coder
doesn't need to care about the actual name of the object's 
instantiation.

**However, those wrapper functions in the gnuplot_mod module 
looks a bit silly.**

**Is there a more elegant way to do this?**

First, the module:

------------------------------------------------------------------

    module gnuplot_mod;

    import std.stdio;
    import std.process;
    import std.conv : to;

    class Gnuplot {

       // modification of wiki.dlang.org/Low-Lock_Singleton_Pattern
       ProcessPipes pipe;
       private this() {
          this.pipe = pipeProcess("/usr/local/bin/gnuplot") ;
       }

       private static bool instantiated_;
       private __gshared Gnuplot instance_;

       static Gnuplot get() {
          if (!instantiated_) {
             synchronized(Gnuplot.classinfo) {
                if (!instance_) {
                   instance_ = new Gnuplot();
                }
                instantiated_ = true;
             }
          }
          return instance_;
       }

       void cmd(string txt) {
          this.pipe.stdin.writeln(txt);
          this.pipe.stdin.flush();
          return;
       }

       void plot(double[] x, string txt = "ls 21 lw 2"){
          this.pipe.stdin.writeln("plot '-' u 1:2 with lines " ~ 
txt);
          foreach( ind, val ; x) this.pipe.stdin.writeln( 
to!string(ind) ~ " " ~ to!string(val) );
          this.pipe.stdin.writeln("e");
          this.pipe.stdin.flush();
       }
    }

    void cmd(string txt){
       Gnuplot gp;
       gp = gp.get();
       gp.cmd(txt);
    }

    void plot(double[] x, string txt){
       Gnuplot gp;
       gp = gp.get();
       gp.plot(x, txt);
    }

---------------------------------------------------------------
Now, the main ...

---------------------------------------------------------------

    import gnuplot_mod;
    import std.stdio;

    void main(){

       Gnuplot gp;
       gp = gp.get();

       double[] x = [1.1, 0.2, 3.1, 2.2, 3.1, 0.6];
       double[] y = [-1.1, 0.2, -3.1, 2.2, 3.1, -0.6];

       writeln("\nusing singleton's member functions: plot and 
cmd");

       gp.plot(x, "ls 31 lw 3");
       gp.cmd("pause 2");

       writeln("\nusing uniform function call syntax (UFCS): plot 
and cmd");

       y.plot("ls 41 lw 8");
       cmd("pause 2");
    }


Any suggestions on a better way are greatly appreciated.

Best Regards,
James
Sep 01 2021
parent reply user1234 <user1234 12.de> writes:
On Wednesday, 1 September 2021 at 16:02:47 UTC, james.p.leblanc 
wrote:
 Dear D-ers,

 For simple plotting using a gnuplot process, I have created a 
 singleton object (a stripped
 down minimal working example is below.)

 [...]
 
 **However, those wrapper functions in the gnuplot_mod module 
 looks a bit silly.**

 [...]

 Any suggestions on a better way are greatly appreciated.

 Best Regards,
 James
hello, I'd suggest this: ```d shared static this() { get(); // cache instance before main(){} // to get rid of the synchronized() stmt } static Gnuplot get() { __gshared Gnuplot instance; return instance ? instance : (instance = new Gnuplot()); } ```
Sep 01 2021
parent reply james.p.leblanc <james.p.leblanc gmail.com> writes:
On Wednesday, 1 September 2021 at 19:54:14 UTC, user1234 wrote:
 On Wednesday, 1 September 2021 at 16:02:47 UTC, james.p.leblanc 
 wrote:
 Dear D-ers,

 For simple plotting using a gnuplot process, I have created a 
 singleton object (a stripped
 down minimal working example is below.)

 [...]
 
 **However, those wrapper functions in the gnuplot_mod module 
 looks a bit silly.**

 [...]

 Any suggestions on a better way are greatly appreciated.

 Best Regards,
 James
hello, I'd suggest this: ```d shared static this() { get(); // cache instance before main(){} // to get rid of the synchronized() stmt } static Gnuplot get() { __gshared Gnuplot instance; return instance ? instance : (instance = new Gnuplot()); } ```
user1234, Thanks for your reply, I will need to look at it deeper. I also realize that my post was not so clear, to clarify: I needed to implement two wrapper functions ... that are **in** the module, but **outside** of the object, copied below: **void cmd(string txt){** **Gnuplot gp;** **gp = gp.get();** **gp.cmd(txt);** **}** **void plot(double[] x, string txt){** **Gnuplot gp;** **gp = gp.get();** **gp.plot(x, txt);** **}** These enable use of the UFCS in main() (without prefixing with instantiated object's name **gp** ). The gp prefix only needs to be in these wrappers that exist in the module, not in the main(). The question is if there is a way to enable UFCS without such wrapper (see example in main() in original posting). Thanks again, and now I need to play a bit with your suggestion to learn from it. Best Regards, James
Sep 01 2021
parent reply user1234 <user1234 12.de> writes:
On Wednesday, 1 September 2021 at 20:59:15 UTC, james.p.leblanc 
wrote:
 [...]
 The question is if there is a way to enable UFCS without such 
 wrapper
sorry my attention got stuck on the singleton. So my suggestion is rather to only enable the ufcs style. This highlights the fact that the class is useless, you can use a simple getter that initializes a hidden global: ```d module gnuplot_mod; import std.stdio; import std.process; import std.algorithm : each; import std.range : enumerate; ProcessPipes gnuplot () { __gshared ProcessPipes pipe; return pipe.pid ? pipe : (pipe = pipeProcess("/usr/local/bin/gnuplot")); } static void cmd(string txt) { with (gnuplot()) { stdin.writeln(txt); stdin.flush(); } } static void plot(double[] x, string txt = "ls 21 lw 2"){ with (gnuplot()) { stdin.writeln("plot '-' u 1:2 with lines ", txt); enumerate(x).each!((i,v) => stdin.writefln!"%s %f"(i, v))(); stdin.writeln("e"); stdin.flush(); } } void main() { double[] x = [1.1, 0.2, 3.1, 2.2, 3.1, 0.6]; double[] y = [-1.1, 0.2, -3.1, 2.2, 3.1, -0.6]; y.plot("ls 41 lw 8"); cmd("pause 2"); } ```
Sep 01 2021
parent reply user1234 <user1234 12.de> writes:
On Wednesday, 1 September 2021 at 22:01:12 UTC, user1234 wrote:
 On Wednesday, 1 September 2021 at 20:59:15 UTC, james.p.leblanc 
 wrote:
 [...]
 The question is if there is a way to enable UFCS without such 
 wrapper
sorry my attention got stuck on the singleton. So my suggestion is rather to only enable the ufcs style. This highlights the fact that the class is useless, you can use a simple getter that initializes a hidden global: ```d module gnuplot_mod; import std.stdio; import std.process; import std.algorithm : each; import std.range : enumerate; ProcessPipes gnuplot () { __gshared ProcessPipes pipe; return pipe.pid ? pipe : (pipe = pipeProcess("/usr/local/bin/gnuplot")); } static void cmd(string txt) { with (gnuplot()) { stdin.writeln(txt); stdin.flush(); } } static void plot(double[] x, string txt = "ls 21 lw 2"){ with (gnuplot()) { stdin.writeln("plot '-' u 1:2 with lines ", txt); enumerate(x).each!((i,v) => stdin.writefln!"%s %f"(i, v))();
sorry there was a format mistake not detected at compile time. ```diff - enumerate(x).each!((i,v) => stdin.writefln!"%s %f"(i, v))(); + enumerate(x).each!(a => stdin.writefln!"%s %f"(a.index, a.value))(); ```
         stdin.writeln("e");
         stdin.flush();
     }
 }

 void main() {
   double[] x = [1.1, 0.2, 3.1, 2.2, 3.1, 0.6];
   double[] y = [-1.1, 0.2, -3.1, 2.2, 3.1, -0.6];
   y.plot("ls 41 lw 8");
   cmd("pause 2");
 }

 ```
Sep 01 2021
parent james.p.leblanc <james.p.leblanc gmail.com> writes:
On Wednesday, 1 September 2021 at 22:11:29 UTC, user1234 wrote:
 On Wednesday, 1 September 2021 at 22:01:12 UTC, user1234 wrote:
```
 ProcessPipes gnuplot () {
     __gshared ProcessPipes pipe;
     return pipe.pid ? pipe : (pipe = 
 pipeProcess("/usr/local/bin/gnuplot"));
 }
``` user1234, Thanks! This is perfect, getting rid of the class altogether. Yes, as pointed out in your response, that class was rather useless. (For some unknown reason, I had been stuck on creating a singleton to avoid multiple gnuplot processes -- very unnecessary as I now see.). The ternary operator with the pipe id is much cleaner and leaner. Thanks again, for your time, energy and insight. Best Regards, James
Sep 01 2021