Function-pointer valued enums
H. S. Teoh
hsteoh at quickfur.ath.cx
Sat Jan 23 00:21:25 UTC 2021
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...
More information about the Digitalmars-d
mailing list