## digitalmars.D - Function-pointer valued enums

• H. S. Teoh (84/84) Jan 22 Today, I was writing a program for generating Voronoi diagrams with
• tsbockman (3/5) Jan 22 Cool! Hopefully this doesn't have any weird pitfalls; I'm
• mw (2/7) Jan 23 How about non-static member functions?
• Steven Schveighoffer (4/14) Jan 23 &T.membername works. It's just, to call it you need to pair it with a T
• Araq (5/8) Jan 22 So learn a programming language with an AST based macro system.
• Steven Schveighoffer (13/43) Jan 23 Cool idea! I like the fact that only "approved" functions are available
"H. S. Teoh" <hsteoh quickfur.ath.cx> writes:
```Today, I was writing a program for generating Voronoi diagrams with
different metrics over various topologies, and wanted an easy way to
specify which metric/topology to use.  Traditionally, I'd do something
like this:

enum Metric { euclidean, manhattan, ... }

auto genVoronoi(Metric metric, ...) {
...
final switch (metric) {
case Metric.euclidean:
dist = x*x + y*y;
break;

case Metric.manhattan:
dist = abs(x) + abs(y);
break;
...
}
...
}

void main() {
...
Metric metric = /* from user input */;
genVoronoi(metric, ...);
}

But that's a lot of boilerplate to translate the user's choice of metric
from string into enum, then switch over the enum to various branches of
code.

So I decided to factor out the switch into main(), and pass a function
pointer implementing the metric into genVoronoi().  But that still
entailed enumerating every metric in an enum, then translating the enum
into a bijective set of function pointers.

So I thought, what if I based the enum on the function pointer itself?
And so:

float euclideanMetric(float x, float y) { ... }
float manhattanMetric(float x, float y) { ... }
...
alias MetricImpl = float function(float, float);

enum Metric : MetricImpl {
euclidean = &euclideanMetric,
manhattan = &manhattanMetric,
...
}

auto genVoronoi(MetricImpl metric, ...) {
...
dist = metric(x, y);
...
}

void main() {
...
string metricStr = /* from user input */;

// std.conv.to knows how to convert the string name
// directly into a function pointer!
Metric m = metricStr.to!Metric;

genVoronoi(m, ...);
}

Amazingly, it works!  Did you know that in D, enums can be
function-pointer-valued?  :-D

Now all I have to do to add a new metric is to write the function, then
add the function pointer to the enum, and it automagically learns how to
parse the new option!

One last thing remained: if the user didn't know what metrics were
available, I'd like to supply a list.  Being a lazy D metaprogrammer,
I'm certainly not gonna manually type out the list and then have to
maintain it every time I change the list of metrics.  So compile-time
introspection comes to the rescue:

enum ident(alias Memb) = __traits(identifier, Memb);

void showHelp() {
...
alias T = Metric;
...
alias membs = EnumMembers!T;
immutable string[membs.length] membNames = [
staticMap!(ident, membs)
];
stderr.writefln("Available metrics: %-(%s, %)", membNames);
...
}

And with that, the act of adding a new function pointer to Metric
automatically adds it to the displayed list of available metrics with no
extra effort from me.

D is the only language I know of that can automate things to this level
of awesomeness.  D rocks!  No, D boulders!! :-D  (Thanks, Adam ;-))

T

--
The early bird gets the worm. Moral: ewww...
```
Jan 22
tsbockman <thomas.bockman gmail.com> writes:
```On Saturday, 23 January 2021 at 00:21:25 UTC, H. S. Teoh wrote:
Amazingly, it works!  Did you know that in D, enums can be
function-pointer-valued?  :-D

Cool! Hopefully this doesn't have any weird pitfalls; I'm
guessing it hasn't been tested much?
```
Jan 22
mw <mingwu gmail.com> writes:
```On Saturday, 23 January 2021 at 06:13:03 UTC, tsbockman wrote:
On Saturday, 23 January 2021 at 00:21:25 UTC, H. S. Teoh wrote:
Amazingly, it works!  Did you know that in D, enums can be
function-pointer-valued?  :-D

Cool! Hopefully this doesn't have any weird pitfalls; I'm
guessing it hasn't been tested much?

```
Jan 23
Steven Schveighoffer <schveiguy gmail.com> writes:
```On 1/23/21 1:15 PM, mw wrote:
On Saturday, 23 January 2021 at 06:13:03 UTC, tsbockman wrote:
On Saturday, 23 January 2021 at 00:21:25 UTC, H. S. Teoh wrote:
Amazingly, it works!  Did you know that in D, enums can be
function-pointer-valued?  :-D

Cool! Hopefully this doesn't have any weird pitfalls; I'm guessing it
hasn't been tested much?

&T.membername works. It's just, to call it you need to pair it with a T
instance (which is possible).

-Steve
```
Jan 23
Araq <rumpf_a web.de> writes:
```On Saturday, 23 January 2021 at 00:21:25 UTC, H. S. Teoh wrote:
D is the only language I know of that can automate things to
this level
of awesomeness.

So learn a programming language with an AST based macro system.
There are a couple of these around these days and some of them
(Rust, Nim, Elixir ...) can generate a 'switch' statement at
compile-time...
```
Jan 22
Steven Schveighoffer <schveiguy gmail.com> writes:
```On 1/22/21 7:21 PM, H. S. Teoh wrote:

Amazingly, it works!  Did you know that in D, enums can be
function-pointer-valued?  :-D

Now all I have to do to add a new metric is to write the function, then
add the function pointer to the enum, and it automagically learns how to
parse the new option!

One last thing remained: if the user didn't know what metrics were
available, I'd like to supply a list.  Being a lazy D metaprogrammer,
I'm certainly not gonna manually type out the list and then have to
maintain it every time I change the list of metrics.  So compile-time
introspection comes to the rescue:

enum ident(alias Memb) = __traits(identifier, Memb);

void showHelp() {
...
alias T = Metric;
...
alias membs = EnumMembers!T;
immutable string[membs.length] membNames = [
staticMap!(ident, membs)
];
stderr.writefln("Available metrics: %-(%s, %)", membNames);
...
}

And with that, the act of adding a new function pointer to Metric
automatically adds it to the displayed list of available metrics with no
extra effort from me.

Cool idea! I like the fact that only "approved" functions are available

I think that any type that can implicitly be used as an integer can be
used as an switch type (apparently including pointers).

And then there's strings, which generate a specialized translation into
an integer (and this requires compiler help, because the strings are
sorted by length first). Then the switch is run on that integer.

Potentially, one could instrument ANY type with compiler help to be
usable in switch with something like opSwitchSort (run at CTFE to
generate the case orderings), and opSwitchTranslate (run when you switch
on a value).

-Steve
```
Jan 23