Tuesday, August 19, 2014

Better Enumerators To Strings Solution

Several enumerations need to be converted to strings for output during testing.  This was accomplished with an awk script that scanned source files for the enumerations and automatically generated a source file that contained C style string arrays of the enumerator names, which were indexed by an enumerator.  With enumeration classes, the enumerators can not be used as indexes to arrays.  Another solution was to generate an unordered map that can be indexed by the enumerators provided that a generic hash is defined for enumeration classes.

The method of automatically generating source code from source code is kludgy and should be eliminated.  Also, changing a source file read by an awk script forces the entire project to be rebuilt since all the source files are generally dependent on the auto-generated source file.  A different solution was needed.

Another solution is using a function that takes an enumerator value as input and returns the string using the brute force method with switch statement and returning a string for each case, for example:
const char *enumName(Enum value)
{
    switch (value)
    {
    case Enum::Value1:
        return "Value1";
    case Enum::Value2:
        return "Value2":
    ...
    case Enum::ValueN:
        return "ValueN":
    }
}
A good optimizing compiler will convert this switch statement to an array instead of a series of an if-elseif chain, which the GCC compiler does at the higher optimization levels.  However, there are some conditions that are required.  First, the compiler will only generate a look up array if the function is static (local to the source file where used).  Also, the return values need to be relatively simply types.

However, if the return type is QString and each return value is QString("Value1"), the compiler no longer generates an array, but instead an if-elseif chain.  Though note that the actual return value can still be a C style string where the QString constructor is called to create the QString return value.  (This information was acquired by looking at the assembly code generated.)

On the other hand, if the return value is a standard string (STL std::string), then the compiler does generate an array.  This probably has something to do with the fact that the standard string class supports move constructors (a C++11 addition not discussed here).  The QString class does not support these (at least Qt4 classes no not, but Qt5 classes do).  The problem with using standard strings is that the return values of these functions are used with Qt stream classes, which do not support standard strings.  Therefore, C style strings will be used for now.

Functions like the example above will be used.  These functions could be auto-generated, but again this practice will no longer be used.  The nice thing about auto-generation is that it eliminates the mistake of missing an enumerator, because with all warnings enabled (and as errors), if an enumerator is missing, a compiler warning is issued for the switch statement that not values are handled (provided no default case is included).  This is not a perfect solution, but probably the best within the limits of the C++ language.

There is one final issue with the function above.  The compiler sees that there is no return value at the end of the function and issues a warning (error).  The compiler is apparently not smart enough to realize that execution does not get past the switch statement since all cases return.  To silence this warning, a return ""; statement is needed at the end of the function.