C++ Bitflaged 枚举到字符串

Ziv*_*ivS 5 c++ enums bit-manipulation bitflags

当您将鼠标悬停在按位枚举(或无论如何称呼)变量(在调试时)上时,我试图通过获取枚举并将其转换为字符串来执行 Intellisense 在 Visual Studio 中所做的操作。

例如:

#include <iostream>

enum Color {
    White = 0x0000,
    Red = 0x0001,
    Green = 0x0002,
    Blue = 0x0004,
};

int main()
{
    Color yellow = Color(Green | Blue);
    std::cout << yellow << std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

如果您将鼠标悬停在上面,yellow您会看到:

在此输入图像描述

所以我希望能够调用类似的东西:

std::cout << BitwiseEnumToString(yellow) << std::endl;
Run Code Online (Sandbox Code Playgroud)

并输出打印:Green | Blue

我编写了以下内容,试图提供打印枚举的通用方法:

#include <string>
#include <functional>
#include <sstream>

const char* ColorToString(Color color)
{
    switch (color)
    {
    case White:
        return "White";
    case Red:
        return "Red";
    case Green:
        return "Green";
    case Blue:
        return "Blue";
    default:
        return "Unknown Color";
    }
}

template <typename T>
std::string BitwiseEnumToString(T flags, const std::function<const char*(T)>& singleFlagToString)
{
    if (flags == 0)
    {
        return singleFlagToString(flags);
    }

    int index = flags;
    int mask = 1;
    bool isFirst = true;
    std::ostringstream oss;
    while (index)
    {
        if (index % 2 != 0)
        {
            if (!isFirst)
            {
                oss << " | ";
            }
            oss << singleFlagToString((T)(flags & mask));
            isFirst = false;
        }

        index = index >> 1;
        mask = mask << 1;
    }
    return oss.str();
}
Run Code Online (Sandbox Code Playgroud)

所以现在我可以打电话:

int main()
{
    Color yellow = Color(Green | Blue);
    std::cout << BitwiseEnumToString<Color>(yellow, ColorToString) << std::endl;
    return 0;
}
Run Code Online (Sandbox Code Playgroud)

我得到了想要的输出。

我猜我找不到任何关于它的信息,因为我不知道它叫什么名字,但无论如何 -

  1. std 或 boost 中是否有某些东西可以做到这一点或可以用来提供这一点?

  2. 如果没有,做这样的事情最有效的方法是什么?(或者我的就足够了)

Joh*_*ger 0

编辑:请参阅下面的通用template实现... 但请注意,此template实现践踏了ALL OVER ostreamoperator <<()几乎所有内容的实现!如果这些enums 是成熟的类,并且具有template. 这个通用定义相当于瓷器店里的原子弹......


我编写了以下示例,并带有测试功能。它使用 C++ 重载来允许您简单地cout-Color如果您希望仍然能够打印简单的数值,您必须将其转换为int

#include <iostream>

enum Color {
    White = 0x0000,
    Red   = 0x0001,
    Green = 0x0002,
    Blue  = 0x0004,
}; // Color

std::ostream &operator <<(std::ostream &os, Color color) {
    static const char *colors[] = { "Red", "Green", "Blue", 0 }; // Synchronise with Color enum!

    // For each possible color string...
    for (const char * const *ptr = colors;
         *ptr != 0;
         ++ptr) {

        // Get whether to print something
        bool output = (color & 0x01)!=0;

        // Is color bit set?
        if (output) {
            // Yes! Output that string.
            os << *ptr;
        } // if

        // Next bit in color
        color = (Color)(color >> 1);

        // All done?
        if (color == 0) {
            // Yes! Leave
            break;
        } // if

        // No, so show some more...
        if (output) {
           // If output something, need 'OR'
           os << " | ";
        } // if
    } // for
    return os;
} // operator <<(Color)

void PrintColor() {
    for (unsigned c = 0; c < 8; ++c) {
        Color color = Color(c);
        std::cout << color << std::endl;
    } // fors
} // PrintColor()
Run Code Online (Sandbox Code Playgroud)

通用实现,带有示例

首先是一个头文件:

// EnumBitString.h

template <typename ENUM>
const char * const *Strings() {
    static const char *strings[] = { "Zero", 0 }; // By default there are no Strings
    return strings;
} // Strings<ENUM>()

template <typename ENUM>
std::ostream &operator <<(std::ostream &os, ENUM e) {
    const char * const *ptr = Strings<ENUM>();
    if (e == 0) {
        os.operator <<(*ptr);
        return os;
    } // if

    // For each possible ENUM string...
    while (*ptr != 0) {
        bool output = (e & 0x01) != 0;

        // Is bit set?
        if (output) {
            // Yes! Output that string.
            os.operator <<(*ptr);
        } // if

        // Next bit in e
        e = (ENUM)(e >> 1);

        // All done?
        if (e == 0) {
            // Yes! Leave
            break;
        } // if

        // No, so show some more...
        if (output) {
            os.operator <<(" | ");
        } // if

        ++ptr;
    } // while
    return os;
} // operator <<(ENUM)
Run Code Online (Sandbox Code Playgroud)

接下来,你的例子:

// Colors.h

#include "EnumBitString.h"

enum Colors {
    White = 0x0000,
    Red   = 0x0001,
    Green = 0x0002,
    Blue  = 0x0004,
    NumColors = 4
}; // Colors

template <>
const char * const *Strings<Colors>() {
    static const char *strings[] { "White", // Zero case
                                   "Red",
                                   "Green",
                                   "Blue",
                                   0 }; // Don't forget final 0
    static_assert((sizeof(strings)/sizeof(strings[0])==NumColors+1, "Colors mismatch!");
    return strings;
} // Strings<Colors>()
Run Code Online (Sandbox Code Playgroud)

然后,值中位的另一个示例:

// Flags.h

#include "EnumBitString.h"

enum Flags {
    CF = 0x0001,
//  Res1 = 0x02,
    PF = 0x0004,
//  Res2 = 0x08,
    AF = 0x0010,
//  Res3 = 0x20,
    ZF = 0x0040,
    NumFlags = 7
}; // Flags

template <>
const char * const *Strings<Flags>() {
    static const char *strings[] =  { "None",
                                      "Carry",
                                      "",
                                      "Parity",
                                      "",
                                      "Arithmetic",
                                      "",
                                      "Zero",
                                      0 }; // Don't forget final 0
    static_assert((sizeof(strings)/sizeof(strings[0])==NumFlags+1, "Flags mismatch!");
    return strings;
} // Strings<Flags>()
Run Code Online (Sandbox Code Playgroud)

最后给出一个测试程序:

#include <iostream>

#include "Colors.h"
#include "Flags.h"

void TestENUM() {
    for (unsigned c = 0; c < 0x0008; ++c) {
        Colors color = Colors(c);
        std::cout << color << std::endl;
    } // for
    for (unsigned f = 0; f < 0x0080; ++f) {
        Flags flag = Flags(f);
        std::cout << flag << std::endl;
    } // for
} // TestENUM()
Run Code Online (Sandbox Code Playgroud)

很酷吧?