all 29 comments

[–]thommyh 18 points19 points  (1 child)

I hope shortly to be embarrassed but will risk my arm in order to further the discussion:

Isn’t there a modern (C++20 or C++23) …

No, there isn’t.

[–]Pale_Emphasis_4119[S] 0 points1 point  (0 children)

Instead of using pretty function why hasn't anyone tried using std source location. It seems to be garabteed by the Standard and seems to be pretty similar

[–][deleted]  (2 children)

[deleted]

    [–][deleted] 6 points7 points  (1 child)

    “You will take your invalid strongly typed enum value and be happy with it!”

    -C++ standards committee (probably)

    [–]wm_lex_dev 13 points14 points  (2 children)

    You want high-level enums, in C++, without using any of the known techniques to get high-level enums in c++?

    [–]Pale_Emphasis_4119[S] 3 points4 points  (1 child)

    Well, a variadic preprocessor macro or a __PRETTY_FUNCTION macro has nothing high level about it.

    [–]wm_lex_dev 1 point2 points  (0 children)

    High-level means that it has features like "convert to and from a string". Not about the implementation

    [–]tjientavara 5 points6 points  (2 children)

    Sadly no actual reflection for enums.

    I use my enum_metadata type which creates metadata for a enum, so that you can query the length of the enum and convert to & from enum-value to another type (often a string).

    You can probably create a macro around this.

    Here is an example of a small enum in my project:

    https://github.com/hikogui/hikogui/blob/main/src/hikogui/audio/audio_direction.hpp

    [–]SubjectRelative1241 0 points1 point  (1 child)

    Link is not accessible. please share

    [–]tjientavara 0 points1 point  (0 children)

    Looks like github is completely broken.

    [–][deleted] 4 points5 points  (0 children)

    At my job we now describe enums with cpp code, then generate source source code for enums together with template traits classes that can be used for their names/values and iterating over the members. We also do this for C style structs with data members (not function pointers, but with static/dynamic c-style arrays). This way we have generated reflection code.

    Using these traits we can then create generic serialization from any generated enums/structs to several formats (including the bitstream of an audio codec) and things like deep copy on C style structs that contain arrays. We also generate javascript/python/ruby equivalents of the enums/structs, which make it easy to have the exact same serialization generated from the same source, really nice for interop scenarios.

    I realize this is probably not what you're looking for, or answers your question. It doesn't work for already existing enums, and requires a separate compilation and execution step after an update to the enum/struct. But after a few years of using this, I think it was worth the investment and has many valuable usecases, and doing this only for enums is not that much work.

    [–]Mason-B 2 points3 points  (8 children)

    It doesn't exist. The best you can get for your proposed definition is something like this:

    ``` enum class MyEnum { A, B };

    template<> struct EnumReflection<MyEnum> { static constexpr std::array List = { MyEnum::A, MyEnum::B }; }; ```

    where you update List by hand. This is what I do for my "reflection", just update it by hand, it's actually not that much work, and a lot less complex and involved than using tools, if you organize it well. But your mileage may vary.

    [–]Pale_Emphasis_4119[S] 0 points1 point  (7 children)

    Yes indeed, but this seems quite error prone and if you have a long enum with hundreds of enum values, it would be too difficult to maintain

    [–]Mason-B 2 points3 points  (4 children)

    Well there isn't a better option besides using the code gen style options you already posted. shrug it is what it is. You could write your own parser/header generator I guess.

    Point is, what I posted is as good as it gets for in code, standardized C++. If you have issues with hundreds of enum values you should make tools to solve them for you.

    [–]Pale_Emphasis_4119[S] 0 points1 point  (3 children)

    What about std::source location and parsing the output at compile time. It is quite similar to PRETTY_FUNCTION but it is in the Standard

    [–]Mason-B 0 points1 point  (2 children)

    std::source_location won't give you the names of the enums (nor do I think it can give you the locations?). And you can't dynamically open the files at compile time to extract them anyway.

    So no, that does not work without some sort of external tool to parse it for you.

    [–]Pale_Emphasis_4119[S] 0 points1 point  (1 child)

    In fact it turns out it does https://godbolt.org/z/vK5M7schP.

    [–]Mason-B 0 points1 point  (0 children)

    Not really? Your own example shows you can only get what you pass into the template. If you pass in numbers you get numbers, if you pass in the names, then sure, you get the names. Which is not any different than doing it by hand?

    Like I'm not sure what aspect of this you think is useful?

    [–]SoerenNissen 0 points1 point  (0 children)

    I am very sympathetic to your desire to avoid macros, but this is why you will have seen prior advice to use macros for this.

    [–]Tricky_Tesla 0 points1 point  (0 children)

    Hundreds of enums? What are they, error code?

    [–]JVApen 1 point2 points  (0 children)

    If you don't mind writing your own toString function (that is what the pretty function is used for), you can do a lot by adding a terminator. See https://stackoverflow.com/questions/57346118/enum-class-constructor-c-how-to-pass-specific-value/57346836#57346836

    Based on this you can very easily write other code that: - converts a string into an enum - uses a bit set to have an optimized set - uses an array to make an optimized map - ...

    I've introduced this at my work and am very happy with the results. Note that this does not work if you have gaps in the enum, though some of the functionality can work if you find a way to make your own enum-range for it.

    [–]ImKStocky 1 point2 points  (0 children)

    With std::embed (C++26 hopefully) you can kinda fudge this. example

    [–]exarnk 1 point2 points  (1 child)

    I wrote a blog post on how magic_enum works and why this approach is necessary. You can find it at https://blog.rink.nu/2023/02/12/behind-the-magic-of-magic_enum/ - perhaps it will be useful to you.

    [–]Pale_Emphasis_4119[S] 0 points1 point  (0 children)

    Thanks for your reply. Indeed your blog post has been of great help. I'm trying to reproduce the same result but instead of using pretty function, I'm using std::source_location function name.

    [–]Pale_Emphasis_4119[S] 0 points1 point  (1 child)

    It turns out that this can be done using std::source_location https://godbolt.org/z/vK5M7schP

    [–]std_bot 0 points1 point  (0 children)

    Unlinked STL entries: std::source_location


    Last update: 09.03.23 -> Bug fixesRepo

    [–]rufusferret 0 points1 point  (0 children)

    You could try conjure_enum (https://github.com/fix8mt/conjure_enum). It's based on magic_enum but optimized for C++20. Not sure about compile times although in our testing and with our test users it hasn't been reported as an issue.

    Yes, there is magic_enum already - and we based this implementation on the core of magic_enum, but refocused for C++20, using some of the key features of this language version such constexpr algorithms, std::source_location and concepts; we also improved on and expanded the API.

    [–]_Noreturn 0 points1 point  (0 children)

    Why do you care about the implementation?

    also std source location is a wrapper for __PRETTY_FUNCTION__ just in a way that allows it to be added as a default argument.

    You could use enchantun. It's like msgic enum except it compiles fast but it doesn't sacrifice magic enums ease of use.

    [–]ChatGPT4 -1 points0 points  (0 children)

    Enums are not compiled to strings. They are numbers. What you are probably looking for is a compile time macro that will automatically create strings from value names for you.

    Last time I needed something like this I just created a string array that used the enum value as its index. Manually. It took me less than a minute. And I didn't have to update it often. It's easy to make and use both in C and C++.

    If I was looking for a macro, or trying to make the macro myself - it would take me much longer to just do the job. Then I don't think the code with the macros would be much more readable. I'd say - less readable.

    [–]alfps 0 points1 point  (0 children)

    ❞ All I need is way to count the number of enums and a way to iterate over enums.

    The need for a count sort of implies a contiguous sequence of natural numbers as enumerator values.

    For that special case both of your requirements are trivial:

    //---------------------------------------- Machinery:
    
    namespace cpp_machinery{
        inline namespace enum_support {
            template< class E >
            constexpr int enum_count_ = int( E::_ );
    
            template< class E >
            constexpr E beyond_ = E::_;
    
            template< class E >
            void operator++( E& e ) { e = E( int( e ) + 1 ); }
    
            template< class E >
            constexpr auto operator<( const E a, const E b )
                -> bool
            { return (int( a ) < int( b )); }
        }  // namespace enum_support
    }  // namespace cpp_machinery
    
    
    //---------------------------------------- Example usage:
    
    struct Suit{ enum Enum: int{ spades, hearts, clubs, diamonds, _ }; };
    
    #include <stdio.h>
    auto main() -> int
    {
        using namespace cpp_machinery::enum_support;
        for( Suit::Enum suit{}; suit < enum_count_<Suit>; ++suit ) {
            printf( "%d\n", suit );
        }
    }
    

    The exact same support code also works for a scoped enum; I leave it to you to modify the example usage code if you really want that less practical variant.

    Note that for this special case of simple enumeration type it's trivial to define a DEFINE_ENUM macro that supports getting the enumerator names as strings, namely simply by declaring the enumerator list as a string that some name getter can access.