August 02

I like global registration patterns, for some stuff. Things like command line flags in very big projects with many tunable in various subsystems. Or registering things like benchmarks, etc. See for example Abseil flags, Go flags, or google benchmark tests.

But I think in D it is hard to pull off compared to for example C++. (Or some compiler tricks with linker sections).

Lets consider code like this:

// Library code, i.e. flags.d

shared final class Flag {
  this(string name, string help = "") {
    import std.stdio : writefln;
    writefln("Constructing %s", name);

    name_ = name;
    help_ = help;
  }
  const string name_;
  const string help_;
  int val_;

  void print() {
    import std.stdio : writefln;
    writefln("%s=%d  # %s", name_, val_, help_);
  }

  shared static Flag[] flags;
}


string flag(string name, string help = "") {
  import std.conv : text;
  string s;
  s ~= i"shared Flag $(name);\n".text;
  // Alternatively we can do append to `Flag.flags` in `Flag` constructor.
  s ~= i"shared static this() { $(name) = new Flag(\"$(name)\", \"$(help)\"); Flag.flags ~= $(name); }\n".text;
  return s;
}


// User code, i.e. main.d
mixin(flag("foo", "--foo bar baz"));
mixin(flag("x", "--x y z"));

void main() {
  foo.print();
  x.print();
}

I really wish I was able to do something like this instead:

shared final class Flag {
  this(string name, string help = "") {
    import std.stdio : writefln;
    writefln("Constructing %s", name);

    name_ = name;
    help_ = help;

    flags ~= this;
  }
  const string name_;
  const string help_;
  int val_;

  void print() {
    import std.stdio : writefln;
    writefln("%s=%d  # %s", name_, val_, help_);
  }

  shared static Flag[] flags;
}


shared Flag foo = new Flag("foo", "--foo bar baz");
shared Flag x = new Flag("foo", "--x y z");

void main() {
  foo.print();
  x.print();
}

But that does not work afaik.

And also for this to not be legal: Flag foo = new Flag("foo", "--foo bar baz"); (non-shared instantiation as global not allowed, maybe only in unittests allowed).

Am I missing something?

(Another pattern is to make Flag a immutable struct, and annotate it with UDF, then have at the end some mixin that iterates over all flags and initializes / registers them, but that is not pretty either).

@flag("foo")
immutable Flag foo = FlagSpec("--foo bar baz");

mixin(iter_flags_and_register());

or something similar.

There must be some better, easier and less error prone way of doing this.

Cheers.

August 03
I asked Walter to have a look over: https://forum.dlang.org/thread/pilnkcllbpntvysjhkui@forum.dlang.org

Also needs Martin's, and ideally Iain's review.