The "symbol table" that I was talking about is a table of mappings from the magic numbers in the program (addresses, offsets, enum tag values) back to the actual "symbols" (identifiers) that you used in your source code. It is added to the executable (or object) file and is not part of the program proper. It's used by linkers and debuggers.
As for whether you would use a switch or, perhaps, an array, it's up to you. Usually if there's just a few tags the switch is good enough. Although you could also use an array, if it seems more natural. I've shown that below.
I also used the preprocessor to do a little trick that allows you to print variable names. This is possible since the macro is replaced at compile time when the names are still known. The
#
in front of
var
means to create a string out of it (so if var is
number
, it's turned into
"number"
). This is entirely preprocessor text-replacement magic and is quite at odds with the general philosophy of C++, which is to eschew macros whenever possible.
The SIZE tag at the end of the enum tags provides a tag with the integer value equal to the number of tags (except for the SIZE tag itself). So below, SIZE would have integer value 5, which we use to bounds-check the array index.
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33
|
#include <iostream>
#define dump(var) std::cout << #var ": " << var << '\n';
class Object {
public:
enum class Greek {
Alpha, Beta, Gamma, Delta, Epsilon, SIZE
};
Object() : alphabet(Greek::Delta) {}
const char* to_string() const {
const char* strs[] {"Alpha", "Beta", "Gamma", "Delta", "Epsilon"};
const int Size = int(Greek::SIZE);
int i = int(alphabet);
return i >= 0 && i < Size ? strs[i] : "error";
}
private:
Greek alphabet;
};
std::ostream& operator<<(std::ostream& os, const Object& o) {
return os << o.to_string();
}
int main() {
Object obj;
dump(obj);
int number = 42;
dump(number);
}
|