Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Way to have a function share an enum classes namespace?

Tags:

c++

c++11

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.


2 Answers

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_string will find KHR_debug::to_string because that namespace contains the argument type.

See it Live on Coliru

Full Sample

#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 :)

like image 120
sehe Avatar answered Feb 06 '26 08:02

sehe


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.

like image 37
Pierre Fourgeaud Avatar answered Feb 06 '26 06:02

Pierre Fourgeaud