EnumValueToString



  • Kellerautomat schrieb:

    Sowas will doch kein Mensch lesen.

    Muss ja auch keiner.
    Man packt alles bis Zeile 46 in einen Header und gut ist.
    rtrim kann man bestimmt auch durch constexpr Funktionen ersetzen, dann spart man sich das const_string auch. Und SPLIT ist dann auch überflüssig.



  • Ja, der mehrfach-include Trick war vermutlich von mir - zumindest hab' ich das mal irgendwo erwähnt. Wir verwenden das u.A. um konfigurationsabhängige Filenamen für unsere #pragma comment(lib, "filename.ext") Direktiven zusammenzubauen.

    Nathan schrieb:

    Kellerautomat schrieb:

    Sowas will doch kein Mensch lesen.

    Muss ja auch keiner.
    Man packt alles bis Zeile 46 in einen Header und gut ist.

    Arr, zu langsam. Wollte ich auch grad schreiben 🙂



  • Und dann kann ich nur toString-en, buuuh.



  • Kellerautomat schrieb:

    Und dann kann ich nur toString-en, buuuh.

    Naja, find ich angenehmer als extra eine zusätzliche Datei zu erstellen.



  • Naja, man kann das Makro STRING_ENUM ja um weitere Teile - inklusive mehrfache Aufrufe von BOOST_PP_SEQ_ENUM - erweitern.



  • BTW: Ich mach das mit enums in meinen Projekten sehr ... pragmatisch 😃

    Einfach

    enum Foo
    {
       FooA,
       FooB,
       FooC,
    };
    
    ...
    
    #define FOO_TO_STRING_CASE_(code) \
    	case code: \
    		return BOOST_JOIN(L, #code) \
    	// end FOO_TO_STRING_CASE_
    
    std::wstring FooToString(Foo f)
    {
        switch (f)
        {
        FOO_TO_STRING_CASE_(FooA);
        FOO_TO_STRING_CASE_(FooB);
        FOO_TO_STRING_CASE_(FooC);
        default:
            assert(0 && "invalid Foo enum value");
            return L"";
        }
    }
    
    #undef FOO_TO_STRING_CASE_
    

    Klar, ich hab damit die Namen an zwei Stellen stehen.
    Dafür ist es lesbar und Intellisense- und refactoring-freundlich. (Die Bezeichner in den FOO_TO_STRING_CASE_ Aufrufen werden zwar meist nicht mit umbenannt, aber das führt dann ja zu nem Compiler-Fehler => kein Problem.)
    Und es ist immerhin sichergestellt dass das Ergebnis von FooToString() dem Bezeichner entspricht. => Gut genug für mich.

    EDIT: * Renamed FOO_TO_STRING_CASE__ => FOO_TO_STRING_CASE_ to avoid using a reserved identifier.



  • hustbaer schrieb:

    FOO_TO_STRING_CASE__

    Dieser Bezeichner ist reserviert. Ich würde die hinteren beiden Unterstriche weglassen.



  • Ja, hast Recht. Hab's gefixt.
    Ich hatte fälschlicherweise im Kopf dass nur doppelte Underscores am Anfang verboten sind... k.A. warum.


  • Mod

    #include <algorithm>
    #include <stdexcept>
    #include <type_traits>
    
    #include <cstring>
    
    namespace EnumDefaults
    {
    namespace detail
    {
    	struct enum_info
    	{
    		bool increasing;
    		bool continuous;
    	};
    
    	template <typename T>
    	constexpr enum_info information( T const* ptr, std::size_t len, bool increasing = true, bool continuous = true )
    	{
    		return len == 1? enum_info{increasing, continuous} : information(ptr+1, len-1, increasing && ptr[0] < ptr[1], continuous && ptr[0] + 1 == ptr[1] );
    	}
    
    	template <typename T> struct identity {using type = T;};
    }
    
    	template <char const* const* first, std::size_t len, typename underlying_type, underlying_type const* values>
    	std::pair<underlying_type, bool> fromString( char const* str )
    	{
    		auto i = std::find_if(first, first + len, [str] (char const* arg) {return std::strcmp(arg, str) == 0;});
    
    		if (i == first + len)
    			return {values[0], false};
    
    		return {values[i - first], true};
    	}
    }
    
    #include <boost/preprocessor/cat.hpp>
    #include <boost/preprocessor/stringize.hpp>
    
    #include <boost/preprocessor/arithmetic/inc.hpp>
    #include <boost/preprocessor/repetition/enum.hpp>
    
    #include <boost/preprocessor/seq/enum.hpp>
    #include <boost/preprocessor/seq/fold_left.hpp>
    #include <boost/preprocessor/seq/size.hpp>
    #include <boost/preprocessor/seq/subseq.hpp>
    #include <boost/preprocessor/seq/transform.hpp>
    #include <boost/preprocessor/tuple/elem.hpp>
    #include <boost/preprocessor/tuple/size.hpp>
    #include <boost/preprocessor/tuple/to_seq.hpp>
    #include <boost/preprocessor/variadic/to_seq.hpp>
    
    #define STRINGIZE_ENUM(s, data, tuple) BOOST_PP_STRINGIZE(BOOST_PP_TUPLE_ELEM(0, tuple))
    
    #define EXTRACT_VALUE_1(s, data, tuple) data+1
    #define EXTRACT_VALUE_2(s, data, tuple) BOOST_PP_TUPLE_ELEM(1, tuple)
    #define EXTRACT_VALUE(s, data, tuple) BOOST_PP_CAT(EXTRACT_VALUE_, BOOST_PP_TUPLE_SIZE(tuple))(s, data, tuple)
    
    #define EXPAND_ENUMERATOR_1(s, data, tuple) BOOST_PP_TUPLE_ELEM(0, tuple)
    #define EXPAND_ENUMERATOR_2(s, data, tuple) BOOST_PP_TUPLE_ELEM(0, tuple) = BOOST_PP_TUPLE_ELEM(1, tuple)
    #define EXPAND_ENUMERATOR(s, data, tuple) BOOST_PP_CAT(EXPAND_ENUMERATOR_, BOOST_PP_TUPLE_SIZE(tuple))(s, data, tuple)
    
    #define INDEX_MAP(s, N, data) BOOST_PP_SEQ_FOLD_LEFT(EXTRACT_VALUE, BOOST_PP_TUPLE_ELEM(0, data), BOOST_PP_SEQ_SUBSEQ(BOOST_PP_TUPLE_ELEM(1, data), 0, BOOST_PP_INC(N)))
    
    #define TO_STRING_CASE(s, N, data) case BOOST_PP_TUPLE_ELEM(1,data)::BOOST_PP_TUPLE_ELEM(0,BOOST_PP_SEQ_ELEM(N, BOOST_PP_TUPLE_ELEM(0,data))): return strings[N];
    
    #define STRING_ENUM_IMPL(name, namespace_name, seq)                                                                                     \
                                                                                                                                            \
          typedef enum name { BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(EXPAND_ENUMERATOR, 0, seq)) } Deduced##namespace_name##_enum_type;   \
                                                                                                                                            \
          namespace namespace_name                                                                                                          \
          {                                                                                                                                 \
                using enum_type = Deduced##namespace_name##_enum_type;                                                                      \
                constexpr std::size_t enum_size = BOOST_PP_SEQ_SIZE(seq);                                                                   \
                using underlying_type = std::underlying_type<enum_type>::type;                                                              \
                                                                                                                                            \
                constexpr char const* strings[]                                                                                             \
                {                                                                                                                           \
                      BOOST_PP_SEQ_ENUM(BOOST_PP_SEQ_TRANSFORM(STRINGIZE_ENUM, 0, seq))                                                     \
                };                                                                                                                          \
                                                                                                                                            \
                constexpr underlying_type values[]                                                                                          \
                {                                                                                                                           \
                      BOOST_PP_ENUM(BOOST_PP_SEQ_SIZE(seq), INDEX_MAP, ((underlying_type) -1, seq))                                         \
                };                                                                                                                          \
                                                                                                                                            \
                constexpr auto info = EnumDefaults::detail::information(values, enum_size);                                                 \
                                                                                                                                            \
                std::pair<enum_type, bool> fromString( char const* str )                                                                    \
                {                                                                                                                           \
                      auto pair = EnumDefaults::fromString<strings, enum_size, underlying_type, values>(str);                               \
                      return {static_cast<enum_type>(pair.first), pair.second};                                                             \
                }                                                                                                                           \
                                                                                                                                            \
                char const* toStringSwitch( enum_type n )                                                                                   \
                {                                                                                                                           \
                      switch (n)                                                                                                            \
                      {                                                                                                                     \
                            BOOST_PP_REPEAT(BOOST_PP_SEQ_SIZE(seq), TO_STRING_CASE, (seq, enum_type))                                       \
                      }                                                                                                                     \
                      return nullptr;                                                                                                       \
                }                                                                                                                           \
                                                                                                                                            \
                char const* toString( enum_type n )                                                                                         \
                {                                                                                                                           \
                      if (info.continuous)                                                                                                  \
                      {                                                                                                                     \
                            auto val = static_cast<underlying_type>(n);                                                                     \
                            if (val - values[0] >= enum_size)                                                                               \
                                  return nullptr;                                                                                           \
                            return strings[val - values[0]];                                                                                \
                      }                                                                                                                     \
                                                                                                                                            \
                      return toStringSwitch(n);                                                                                             \
                }                                                                                                                           \
          }
    
    #define STRING_ENUM(a, b, ...) STRING_ENUM_IMPL(a, b, BOOST_PP_VARIADIC_TO_SEQ(__VA_ARGS__))
    
    STRING_ENUM
    (
    	class MyEnum : unsigned long long, MyEnumInfo,
    
    	(Enumerator1, 7),
    	(Enumerator2, 89),
    	(Enumerator3)
    )
    
    #include <iostream>
    
    int main()
    {
    	std::cout << MyEnumInfo::toString( MyEnumInfo::fromString("Enumerator1").first ) << '\n';
    	std::cout << MyEnumInfo::toString( MyEnumInfo::fromString("Enumerator2").first );
    }
    

    Demo. Hoffentlich war das den Schlaf wert...

    Edit: Vor der Nacht noch die Exceptions entfernt. Die machen hier nicht wirklich sinn.
    Edit²: Scoped-Enums fix, siehe unten



  • Ihr seid Klasse.
    Und @Arcoth: nice...



  • Tim06TR schrieb:

    Doch dann habe ich erstmal gezögert, weil mir das doch etwas 'dirty' vorkam.

    Also ich verwende enums schonmal sehr selten. Und daß ich sie dann noch ausgeben will, ist noch viel seltener. In jenen Fällen kann ich wegen der Seltenheit durchaus gut mit einer Array-Lösung leben.



  • Arcoth bester Mann Junge hier. 👍
    Direkt mal rippen.


  • Mod

    Habe noch einen Bug gefixt (und den fix oben reineditiert), scoped enums haben nicht funktioniert. Es wird jetzt zusätzlich ein Typedef in den globalen Namensraum eingeführt, im Beispiel oben DeducedMyEnumInfo_enum_type . Hoffentlich schreckt das komische Wort andere davon ab den Bezeichner zu verwenden 😉


Anmelden zum Antworten