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