2016-06-13 2 views
3

Я пытаюсь сделать то, что Intellisense делает в визуальной студии, когда вы наводите курсор на побитовое перечисление (или, тем не менее, называемое) (при отладке), перебирая и преобразуя его в строку.C++ Bitflaged enum to string

, например:

#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 вы увидите:

enter image description here

Так что я хочу, чтобы быть в состоянии назвать что-то вроде:

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; 
} 

я получаю желаемый результат.

Я предполагаю, что я не мог найти что-нибудь об этом, так как я не знаю, как это называется, но в любом случае -

  1. Есть ли что-то в станд или импульс, что делает это, или может быть используется для этого?

  2. Если нет, то какой самый эффективный способ сделать такую ​​вещь? (Или будет добывать suffic)

+0

Что такое 'singleFlagToString()'? Вы имели в виду вместо 'ColorToString()' вместо этого? На 1-м взгляде все остальное выглядит хорошо для меня, но я бы использовал операцию с битрейтом, а не «индекс% 2». –

+0

singleFlagToString - это 'std :: function', которая принимает перечисление и преобразует его в' const char * '. цель должна была быть такой общей, насколько я могу, поэтому, если вы заметили, я вызываю «BitwiseEnumToString» со вторым параметром «ColorToString». – ZivS

+0

И как бы вы использовали оператор бит-брейка вместо 'index% 2'? Я могу, вероятно, использовать 'index & 0x1 == 0', но это не смещение битов вправо? – ZivS

ответ

0

EDIT: Смотрите ниже общего, template реализации ... Заметим, однако, что это template реализация топчет ALL OVERostream «s operator <<() implemantations для практически все ! Было бы лучше, если бы enum были полномасштабными классами с реализацией базового класса для template. Это общее определение является эквивалентом атомной бомбы в посудной лавке ...


Я написал следующий пример, с помощью функции проверки.Он использует C++ перегружает, чтобы позволить вам просто coutColor - если вы хотите, чтобы иметь возможность еще напечатать простое числовое значение, вы должны бросить его в 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() 

Generic реализации, с примерами

Во-первых, заголовок файла:

// 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() 

круто, да?

+0

Вы не печатаете «Белый». Почему это лучше? Вы должны обновлять массив строк каждый раз, когда вы изменяете перечисление, и вы не получите ошибку компиляции. Кроме того - ваше решение не является общим ... – ZivS

+0

Истина: у меня нет случая с нулевым нулем. Подождите ... –

+0

Теперь я добавил случай с нулем. –

0

Вам нужно будет поддерживать список строковых представлений вашего перечисления, будь то в векторе, жестко закодированном и т. Д. Это одна из возможных реализаций.

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 для каждого перечисления вы хотите сделать это, следуя той же форме

+0

Я не буду -1, но это решение не является ответом на мой вопрос.Он не является общим, не масштабируемым, а не улучшенным по производительности и просто показывает, как конкатенировать строки ... – ZivS