Short version: Is there anyway to nest functions inside enum classes so that as well as EnumClass::EnumLabel you can have useful related functions such as EnumClass::to_string(EnumClass value) instead of having a global function EnumClass_to_string(EnumClass value).
Long version: So I'm playing around with OpenGL which comes with a bunch of integer #defines for various features on the GPU. For C compatibility these defines have no real structure outside of their labels, making it easy to use the incorrect define. To help remedy this source of newbie bugs I've been grouping related defines in to enum classes like the following example for debug message sources:
enum class Source {
API = GL_DEBUG_SOURCE_API,
WINDOW_SYSTEM = GL_DEBUG_SOURCE_WINDOW_SYSTEM,
SHADER_COMPILER = GL_DEBUG_SOURCE_SHADER_COMPILER,
THIRD_PARTY = GL_DEBUG_SOURCE_THIRD_PARTY,
APPLICATION = GL_DEBUG_SOURCE_APPLICATION,
OTHER = GL_DEBUG_SOURCE_OTHER
};
Since these are commonly used in debug output, I decided to make a to_string function to simplify outputting their meanings to logfiles.
std::string source_to_string(KHR_debug::Source source) {
if(source == KHR_debug::Source::API) {
return "GL_DEBUG_SOURCE_API";
} else if(source == KHR_debug::Source::WINDOW_SYSTEM) {
return "GL_DEBUG_SOURCE_WINDOW_SYSTEM";
} else if(source == KHR_debug::Source::SHADER_COMPILER) {
return "GL_DEBUG_SOURCE_SHADER_COMPILER";
} else if(source == KHR_debug::Source::THIRD_PARTY) {
return "GL_DEBUG_SOURCE_THIRD_PARTY";
} else if(source == KHR_debug::Source::APPLICATION) {
return "GL_DEBUG_SOURCE_APPLICATION";
} else if(source == KHR_debug::Source::OTHER) {
return "GL_DEBUG_SOURCE_OTHER";
} else {
return "INVALID_SOURCE_ENUM";
}
}
However, I think it would be much neater if I could nest that helper function inside the enum class itself. So that instead of calling source_to_string(source) you could use Source::to_string(source). I appreciate that this functionality if you use the old-style enum classes where you simply wrap your enum inside a class/struct, however one of the reasons I'm using C++11 enum classes is because of their added type safety.
I tried having a class/namespace alongside the enum class however this appears to overwrite one of the existing definitions.
The idiomatic thing to do here, is use a free function: exactly like you would do
using std::to_string;
int ltuae = 42;
std::cout << "The answer: " << to_string(ltuae) << std::endl;
you can do
KHR_debug::Source s /* = ... */;
std::cout << "The source: " << to_string(s) << std::endl;
Note As written this employs a "hidden" feature of C++: Argument Dependent Lookup. Writing
to_stringwill findKHR_debug::to_stringbecause that namespace contains the argument type.
See it Live on Coliru
#include <iostream>
#include <string>
namespace KHR_debug
{
enum class Source {
API /*= GL_DEBUG_SOURCE_API */,
WINDOW_SYSTEM /*= GL_DEBUG_SOURCE_WINDOW_SYSTEM */,
SHADER_COMPILER /*= GL_DEBUG_SOURCE_SHADER_COMPILER*/,
THIRD_PARTY /*= GL_DEBUG_SOURCE_THIRD_PARTY */,
APPLICATION /*= GL_DEBUG_SOURCE_APPLICATION */,
OTHER /*= GL_DEBUG_SOURCE_OTHER */
};
std::string to_string(Source source) {
switch(source) {
case Source::API: return "GL_DEBUG_SOURCE_API";
case Source::WINDOW_SYSTEM: return "GL_DEBUG_SOURCE_WINDOW_SYSTEM";
case Source::SHADER_COMPILER: return "GL_DEBUG_SOURCE_SHADER_COMPILER";
case Source::THIRD_PARTY: return "GL_DEBUG_SOURCE_THIRD_PARTY";
case Source::APPLICATION: return "GL_DEBUG_SOURCE_APPLICATION";
case Source::OTHER: return "GL_DEBUG_SOURCE_OTHER";
default: return "INVALID_SOURCE_ENUM";
}
}
}
int main()
{
using std::to_string;
int ltuae = 42;
std::cout << "The answer: " << to_string(ltuae) << std::endl;
KHR_debug::Source s = KHR_debug::Source::APPLICATION;
std::cout << "The source: " << to_string(s) << std::endl;
}
Note how I subtly changed your to_string method to use a switch where it should :)
This is impossible directly.
An enum can only be declared : access enum class enumeration-identifier [:underlying-type] { enumerator-list } [var];. They can only contain enumerator-list.
But maybe you can use a Wrapper struct :
#include <string>
#include <iostream>
struct SourceWrapper
{
enum Source {
API = 0,
WINDOW_SYSTEM = 1,
SHADER_COMPILER = 2,
THIRD_PARTY = 3,
APPLICATION = 4,
OTHER = 5,
};
static std::string ToString( Source source ) {
if(source == Source::API) {
return "GL_DEBUG_SOURCE_API";
} else if(source == Source::WINDOW_SYSTEM) {
return "GL_DEBUG_SOURCE_WINDOW_SYSTEM";
} else if(source == Source::SHADER_COMPILER) {
return "GL_DEBUG_SOURCE_SHADER_COMPILER";
} else if(source == Source::THIRD_PARTY) {
return "GL_DEBUG_SOURCE_THIRD_PARTY";
} else if(source == Source::APPLICATION) {
return "GL_DEBUG_SOURCE_APPLICATION";
} else if(source == Source::OTHER) {
return "GL_DEBUG_SOURCE_OTHER";
} else {
return "INVALID_SOURCE_ENUM";
}
}
};
PS: I removed you GL macros for tests purpose.
Live example.
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With