Suppose I have a list of #define
s in a header file for an external library. These #define
s represent error codes returned from functions. I want to write a conversion function that can take as an input an error code and return as an output a string literal representing the actual #define
name.
As an example, if I have
#define NO_ERROR 0
#define ONE_KIND_OF_ERROR 1
#define ANOTHER_KIND_OF_ERROR 2
I would like a function to be able to called like
int errorCode = doSomeLibraryFunction();
if (errorCode)
writeToLog(convertToString(errorCode));
And have convertToString()
be able to auto-convert that error code without being a giant switch-case looking like
const char* convertToString(int errorCode)
{
switch (errorCode)
{
case NO_ERROR:
return "NO_ERROR";
case ONE_KIND_OF_ERROR:
return "ONE_KIND_OF_ERROR";
...
...
...
I have a feeling that if this is possible, it would be possible using templates and metaprogramming, but that would only work the error codes were actually a type and not a bunch of processor macros.
take a look at boost preprocessor. You could create list/array/sequence of code pairs:
relevant link:
http://www.boost.org/doc/libs/1_41_0/libs/preprocessor/doc/ref/seq_for_each.html
http://www.boost.org/doc/libs/1_41_0/libs/preprocessor/doc/ref/tuple_elem.html
#define FOO 1
is handled by the preprocessor as a simple text replacement. If you want to preserve those definitions, you need the giant switch.Another way to do it that's popular in generated code is:
I prefer the switch case way I already mentioned, but depending on how your code is structured it might be easier as part of your build process to generate that array automatically
You are correct. There's no way to recover preprocessor-defined identifiers at runtime (unless you can read the source, but that's cheating). You would be better off creating a constant array of the names and indexing it with the error code (with proper boundary checks of course) - it should be quite easy to write a script to generate it.
One possibility here is to write a little program that parses the .h file that contains the #defines, and emits the corresponding source code for the convertToString() function. Then you can have that program automatically run as part of your build process whenever the .h file is changed. It's a little more work up front, but once it's implemented you'll never again need to manually update your int<->string conversion function.
This is especially useful if you want to support code in multiple languages that uses the same constants. For example, in one of my applications, changing my #defines header file causes the corresponding C++, Python, and Java files to be auto-regenerated. This makes project maintenance much easier and less error-prone.
You can actually have it both ways, ie having two functions that translate from code to error back and forth.
The first thing of course is that
#define
should not be used for constants, an enum would probably be best, however an enum cannot be extended, which requires that all your errors be defined in the same place (ouch, thank you so much for dependencies...)You can do it another may though, using namespaces to isolate the symbols, and preprocessing to handle the whole generation. For the lookup part, we'll use a Bimap.
First we need to define a Handler class (not necessary to inline all that, but it's easier for examples)
Then we just need to provide some syntactic sugar:
We could be a bit more violent in case of a registration of an unknown error (assert for example).
And then we can always wrap that macro into one that can define multiple symbols in one go... but that's left as an exercise.
Usage:
Disclaimer: I am not too sure about the lambda syntax, but it did simplified the writing.