簡體   English   中英

使用編譯時常量將枚舉轉換為字符串

[英]Enum convert to string using compile time constants

我正在嘗試將編譯時字符串與枚舉值相關聯。

這是我第一次嘗試解決這個問題:

EnumValue 將在字符串和枚舉之間進行編譯時關聯

template<typename EnumType, int EnumIntValue, const char* EnumStrValue>
class EnumValue
{
public:
    static const char* toString()
    {
        return EnumStrValue;
    }

    static const int toInt()
    {
        return EnumIntValue;
    }

    static EnumType get()
    {
        return static_cast<EnumType>(EnumIntValue);
    }
};

EnumValueHolder 將保存字符串和枚舉的實際值。 我不喜歡我當前的設計,因為它仍然需要保存一個指向字符串的指針。 我更喜歡為此使用編譯時關聯,但無法提出更優雅的解決方案

template<typename EnumType>
class EnumValueHolder
{
public:
    EnumValueHolder()
    {}

    EnumValueHolder(const EnumType& value, const char* str)
        : value(value), str(str)
    {}

    bool operator==(const EnumValueHolder<EnumType>& rhs) { return value == rhs.value; }
    bool operator==(const EnumType& rhs)const { return value == rhs; }

    operator EnumType()const
    {
        return value;
    }

    const char* toString()const
    {
        return str;
    }

    const int toInt()const
    {
        return static_cast<int>(value);
    }

private:
    EnumType value;
    char const* str;
};

Marcos 可以輕松引用枚舉類型和枚舉值持有者構造

#define ENUM_VALUE_TYPE(enumName, enumValue) \
EnumValue<enumName, (int)enumName::enumValue, str_##enumValue>


#define ENUM_VALUE_MAKE(enumName, enumValue) \
EnumValueHolder<enumName> { \
    ENUM_VALUE_TYPE(enumName, enumValue)::get(), \
    ENUM_VALUE_TYPE(enumName, enumValue)::toString() }

以下是我的測試用例和使用示例:

const char str_Apple[] = "Apple";
const char str_Orange[] = "Orange";
const char str_Pineapple[] = "Pineapple";


enum class EFruits
{
    Apple,
    Orange,
    Pineapple
};


int main()
{
    auto evApple = ENUM_VALUE_MAKE(EFruits, Apple);
    std::cout << evApple.toString() << std::endl;

    auto evOrange = ENUM_VALUE_MAKE(EFruits, Orange);
    std::cout << evOrange.toString() << std::endl;

    std::cout << "compare: " << (evApple == evOrange) << std::endl;

    evApple = evOrange;
    std::cout << evApple.toString() << std::endl;

    auto myfruit = ENUM_VALUE_MAKE(EFruits, Pineapple);
    std::cout << myfruit.toString() << std::endl;

    switch (myfruit)
    {
    case EFruits::Apple:
        std::cout << "Im an apple!" << std::endl;
        break;

    case EFruits::Orange:
        std::cout << "Im an Orange!" << std::endl;
        break;

    case EFruits::Pineapple:
        std::cout << "Im a Pineapple!" << std::endl;
        break;

    default:break;
    }
}

目標之一是刪除全局字符串:

const char str_Apple[] = "Apple";
const char str_Orange[] = "Orange";
const char str_Pineapple[] = "Pineapple";

另一種是創建一個將枚舉與字符串關聯的宏

//Some crazy define that makes pairs of enum values and strings as
//compile time constants
#define DEFINE_ENUM_STRING(enumValue)\
enumValue, #enumValue

//Ideally, the macro would be used like this. This should be usable in any
//scope (global, namespace, class) 
//with any access specifier (private, protected, public)
enum class EFruits
{
    DEFINE_ENUM_STRING(Apple),
    DEFINE_ENUM_STRING(Orange),
    DEFINE_ENUM_STRING(Pineapple)
};

所以有2個主要問題:

1)當前的設計是否真的能保證將枚舉關聯到字符串的編譯時常量?

2)如何定義一個宏來對枚舉值進行字符串化並使用 1 行在枚舉類中聲明該值?

編輯:這應該可以使用 c++ 11 在 win64 平台上使用 msvs2017 進行編譯。

謝謝。

我認為它應該適用於 MSVC2017。 它在 constexpr 函數中使用 C++14,但您可以將它們拆分為單個 return 語句 constexprs 以與 C++11 兼容(但是 MSVC2017 支持 C++14)。

EnumConverter 存儲每個枚舉條目的 char*、枚舉和字符串哈希值。 對於每個枚舉,您必須專門化EnumConverter::StrEnumContainer 可以使用您指定的類似宏生成枚舉字符串對。

#include <tuple>
#include <array>
#include <stdexcept>

using namespace std;

enum ELogLevel {
    Info,
    Warn,
    Debug,
    Error,
    Critical
};

static constexpr size_t constexprStringHash( char const* const str ) noexcept
{
    return (
        ( *str != 0 ) ?
            ( static_cast< size_t >( *str ) + 33 * constexprStringHash( str + 1 ) ) :
            5381
    );
}

class EnumConverter final
{
public:

    EnumConverter() = delete;
    EnumConverter( const EnumConverter& ) = delete;
    EnumConverter( EnumConverter&& ) = delete;
    EnumConverter& operator =( const EnumConverter& ) = delete;
    EnumConverter& operator =( EnumConverter&& ) = delete;

    template< typename ENUM_T >
    static constexpr const char* toStr( const ENUM_T value )
    {
        const auto& strEnumArray{ StrEnumContainer< ENUM_T >::StrEnumPairs };
        const char* result{ nullptr };
        for( size_t index{ 0 }; index < strEnumArray.size(); ++index ) {
            if( std::get< 1 >( strEnumArray[ index ] ) == value ) {
                result = std::get< 0 >( strEnumArray[ index ] );
                break;
            }
        }
        return ( ( result == nullptr ) ? throw std::logic_error{ "Enum toStrBase conversion failed" } : result );
    }

    template< typename ENUM_T >
    static constexpr ENUM_T fromStr( const char* const str )
    {
        const auto& strEnumArray{ StrEnumContainer< ENUM_T >::StrEnumPairs };
        const size_t hash{ constexprStringHash( str ) };
        const ENUM_T* result{ nullptr };
        for( size_t index{ 0 }; index < strEnumArray.size(); ++index ) {
            if( std::get< 2 >( strEnumArray[ index ] ) == hash ) {
                result = &( std::get< 1 >( strEnumArray[ index ] ) );
            }
        }
        return ( ( result == nullptr ) ? throw std::logic_error{ "Enum toStrBase conversion failed" } : *result );
    }

private:
    template< typename ENUM_T, size_t LEN >
    using ARRAY_T = std::array< std::tuple< const char* const, const ENUM_T, const size_t >, LEN >;

    template< typename ENUM_T >
    static constexpr std::tuple< const char* const, ENUM_T, size_t > getTuple( const char* const str, const ENUM_T type ) noexcept
    {
        return std::tuple< const char* const, ENUM_T, size_t >{ str, type, constexprStringHash( str ) };
    }

    template< typename ENUM_T >
    struct StrEnumContainer
    {
    };

    template< typename ENUM_T >
    friend struct StrEnumContainer;
};

template<>
struct EnumConverter::StrEnumContainer< ELogLevel >
{
    using ENUM_T = ELogLevel;

    static constexpr EnumConverter::ARRAY_T< ENUM_T, 5 > StrEnumPairs{ {
        { getTuple( "Info", ENUM_T::Info ) },
        { getTuple( "Warn", ENUM_T::Warn ) },
        { getTuple( "Debug", ENUM_T::Debug ) },
        { getTuple( "Error", ENUM_T::Error ) },
        { getTuple( "Critical", ENUM_T::Critical ) },
    } };
};

int main()
{
    //static_assert( EnumConverter::fromStr< ELogLevel >( "Info" ) == EnumConverter::fromStr< ELogLevel >( EnumConverter::toStr( Error ) ), "Error" ); // Error
    static_assert(
        EnumConverter::toStr( Warn )[ 0 ] == 'W' &&
        EnumConverter::toStr( Warn )[ 1 ] == 'a' &&
        EnumConverter::toStr( Warn )[ 2 ] == 'r' &&
        EnumConverter::toStr( Warn )[ 3 ] == 'n',
        "Error"
    );
    static_assert( EnumConverter::fromStr< ELogLevel >( "Info" ) == EnumConverter::fromStr< ELogLevel >( EnumConverter::toStr( Info ) ), "Error" );
}

暫無
暫無

聲明:本站的技術帖子網頁,遵循CC BY-SA 4.0協議,如果您需要轉載,請注明本站網址或者原文地址。任何問題請咨詢:yoyou2525@163.com.

 
粵ICP備18138465號  © 2020-2024 STACKOOM.COM