Thread overview
Singleton Object, calling member functions using UFCS (an "ugly?" example) ... better way?
Sep 01, 2021
james.p.leblanc
Sep 01, 2021
user1234
Sep 01, 2021
james.p.leblanc
Sep 01, 2021
user1234
Sep 01, 2021
user1234
Sep 02, 2021
james.p.leblanc
September 01, 2021

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

September 01, 2021

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:

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());
}
September 01, 2021

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:

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

September 01, 2021

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:

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");
}

September 01, 2021

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:

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.

- 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");
}

September 02, 2021

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