Использование класса enum для определения флагов

Каков подходящий шаблон для использования перечислений в качестве флагов в современном C++?

Вопрос связан с тем, что я прочитал техническую спецификацию Предложение добавить рендеринг и отображение 2D-графики в C++, где Маклафлин, Саттер и Зинк предлагают C++ API для 2D-графики на основе Cairo API.

Во всем объявлении API авторы в полной мере используют C++11. В частности, все их перечисления объявлены следующим образом:

enum class font_slant {
  normal,
  italic,
  oblique
};

Кроме одного:

namespace text_cluster_flags {
  enum text_cluster_flags : int {
    none     = 0x0,
    backward = 0x1
  };
}

Тип text_cluster_flags используется в методе класса:

void show_text_glyphs(const ::std::string& utf8,
    const ::std::vector<glyph>& glyphs,
    const ::std::vector<text_cluster>& clusters,
    text_cluster_flags cluster_flags);

Я предполагаю, что постороннее объявление предназначено для того, чтобы text_cluster_flags можно было замаскировать, например:

auto flag = text_cluster_flags::none | text_cluster_flags::backward;

что вы не можете сделать с enum class перечислениями:

enum class flags {
  a = 0x0,
  b = 0x1
};

auto f = flags::a | flags::b; // error because operator `|` is
                              // not defined for enum class flags
                              // values

Должны ли авторы определять операторы маскирования? Или их шаблон enum-ininin-namespace является эффективной практикой?


person Escualo    schedule 11.09.2015    source источник
comment
Я бы сказал, что это лучше подходит для обмена стеками программистов, поскольку ваш вопрос в значительной степени концептуален.   -  person alexroussos    schedule 11.09.2015
comment
@alexroussos, ссылаясь на другие сайты, часто полезно указать, что кросс-постинг не одобряется   -  person gnat    schedule 12.09.2015


Ответы (1)


Он создан по образцу cairo API.

Для font_slant мы можем увидеть эквивалент cairo:

перечисление cairo_font_slant_t

typedef enum {
    CAIRO_FONT_SLANT_NORMAL,
    CAIRO_FONT_SLANT_ITALIC,
    CAIRO_FONT_SLANT_OBLIQUE
} cairo_font_slant_t;

Определяет варианты начертания шрифта в зависимости от их наклона.

CAIRO_FONT_SLANT_NORMAL Вертикальный стиль шрифта, начиная с версии 1.0

CAIRO_FONT_SLANT_ITALIC Курсив, начиная с версии 1.0

CAIRO_FONT_SLANT_OBLIQUE Наклонный стиль шрифта, начиная с 1.0

Для text_cluster_flags мы можем увидеть эквивалент cairo:

перечисление cairo_text_cluster_flags_t

typedef enum {
    CAIRO_TEXT_CLUSTER_FLAG_BACKWARD = 0x00000001
} cairo_text_cluster_flags_t;

Задает свойства сопоставления текстового кластера.

CAIRO_TEXT_CLUSTER_FLAG_BACKWARD Кластеры в массиве кластеров сопоставляются с глифами в массиве глифов от конца до начала. (С версии 1.8)

Функция text_to_glyphs моделирует cairo_show_text_glyphs, которая принимает cairo_text_cluster_flags_t. Кроме того, в API есть функция получения текущего наклона. Итак, мое предположение:

  • enum class предназначен для строгой типизации флага. Нет никакого смысла иметь что-то одновременно «нормальное» и «курсивное». Они прикреплены к «лицу шрифта».

  • text_cluster_flags - разовая сделка. Если вы установите его для функции отображения глифов, это просто изменит поведение. Он не привязан к «текстовому кластеру», как наклон прикреплен к «лицу шрифта». Здесь нет причин для сильной типизации.

Ваша интерпретация была правильной, кстати. Вот фрагмент исходного кода:

// ...

+       const cairo_glyph_t *cur_glyph;
+
+       if (cluster_flags & CAIRO_TEXT_CLUSTER_FLAG_BACKWARD)
+       cur_glyph = glyphs + num_glyphs - 1;
+       else
+       cur_glyph = glyphs;
+
+       for (i = 0; i < num_clusters; i++) {
+       cairo_bool_t cluster_visible = FALSE;
+

// ...
person user5326953    schedule 11.09.2015