A brief excursion into the land of advanced C++ tricks…
Create an enumTrick sketch with the following contents:
#include "header.h"
static int fun (One x) {
return 10 + x;
}
static int fun (Two x) {
return 20 + x;
}
void setup () {
Serial.begin(57600);
Serial.println("\n[enumTrick]");
Serial.println(fun(A));
Serial.println(fun(B));
Serial.println(fun(C));
Serial.println(fun(D));
Serial.println(fun(E));
Serial.println(fun(F));
}
void loop () {}
Now create a “header.h” file with the following contents:
typedef enum { A, B, C } One;
typedef enum { D, E, F } Two;
The stupid Arduino IDE pre-processing logic makes it necessary to place these definitions in a separate header file, unfortunately.
You can see that there are two definitions of fun (heh).
This code compiles without errors, because C++ supports “function overloading”, a mechanism to disambiguate function calls through the number and type of the arguments. In this case the args differ in type, being different enumeration constants.
Here’s the output:
[enumTrick]
10
11
12
20
21
22
Note that if you were to call fun with an integer value, you’d get an error:
enumTrick.cpp: In function 'void setup()':
enumTrick:17: error: call of overloaded 'fun(int)' is ambiguous
enumTrick.cpp:12: note: candidates are: int fun(One) <near match>
enumTrick.cpp:13: note: int fun(Two) <near match>
Ok, so what’s the point?
Well, this provides a way to create a simpler API for drivers. Say we have a device which can turn the lights on and off, and dim the lights to a certain level (a Dimmer Plug, perhaps, or an X10 interface). We could do this:
typedef enum { ON, OFF } CmdSet1;
typedef enum { DIM } CmdSet2;
void DeviceControl (CmdSet1 cmd) { ... }
void DeviceControl (CmdSet2 cmd, byte level) { ... }
Now the only valid calls are those where ON or OFF is specified and nothing else, or DIM is specified and a single byte value is required. Every other mix generates an error at compile time. And there’s a single “DeviceControl” function you need to deal with and remember.
It’s definitely a contrived example, but I’m currently digging into some stuff which could probably benefit from this. Basically, you get static typing to help simplify more cases at compile time, which I expect will lead to more compact code.
Which – on a low-end 8-bit MPU – can be a big deal!
Doing that sort of trick with C++ can be handy but it’s easy for it all to get out of control. There have been a couple of occasions where I’ve gone through larger C++ programs ripping out “clever” ideas which turned out to make things more complicated overall – usually due to excessive use of inheritance or clever but not quite clever enough memory management. Templates are particularly evil for this. I would regard myself as a fairly experienced programmer but still get caught like this once in a while (though I suppose I do at least have the judgement to know when to back out). Use sparingly.
void DeviceControlOnOff (CmdSet1 cmd) { … } void DeviceControlDimming (CmdSet2 cmd, byte level) { … } is the naming I would go for, to avoid any ambiguity and to have the compiler doing a check at compile time.