Я пытаюсь сделать то, что делает 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;
}
Если вы наводите yellow
вот увидишь:
Поэтому я хочу иметь возможность назвать что-то вроде:
std::cout << BitwiseEnumToString(yellow) << std::endl;
и иметь вывод на печать: 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();
}
Так что теперь я могу позвонить:
int main()
{
Color yellow = Color(Green | Blue);
std::cout << BitwiseEnumToString<Color>(yellow, ColorToString) << std::endl;
return 0;
}
Я получаю желаемый результат.
Я предполагаю, что я ничего не мог найти об этом, так как я не знаю, как это называется, но в любом случае —
Есть ли что-то в std или boost, что делает это или может быть использовано для обеспечения этого?
Если нет, какой самый эффективный способ сделать это? (или мне хватит)
Вам нужно будет поддерживать список строковых представлений вашего перечисления, будь то в векторе, в жестком коде и т. Д. Это одна из возможных реализаций.
enum Color : char
{
White = 0x00,
Red = 0x01,
Green = 0x02,
Blue = 0x04,
//any others
}
std::string EnumToStr(Color color)
{
std::string response;
if(color & Color::White)
response += "White | ";
if(color & Color::Red)
response += "Red | ";
if(color & Color::Green)
response += "Green | ";
if(color & Color::Blue)
response += "Blue | ";
//do this for as many colors as you wish
if(response.empty())
response = "Unknown Color";
else
response.erase(response.end() - 3, response.end());
return response;
}
Затем создайте другую функцию EnumToStr для каждого перечисления, для которого вы хотите сделать это, следуя той же форме
РЕДАКТИРОВАТЬ: см. Ниже для общего, template
реализация…
Обратите внимание, что это template
реализация попирает ПОВСЮДУ ostream
«s operator <<()
реализации практически для всего! Было бы лучше, если enum
Это были полноценные классы с реализацией базового класса для 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()
Сначала файл заголовка:
// 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)
Далее ваш пример:
// 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>()
Затем еще один пример битов в значении:
// 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>()
Наконец, тестовая программа:
#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()
Круто, да?