Хобрук: Ваш путь к мастерству в программировании

Q_FLAG_NS в Qt 5.9 не предоставляет побитовых операторов

Как следует использовать макрос Q_FLAG_NS?

Мое прочтение Q_FLAG_NS и Новые пространства имен поддержки Qt от KDAB подсказывают мне, что использование этого макроса должно предоставлять побитовые операторы , но что бы я ни пытался,

../main.cpp:11:11: error: invalid operands to binary expression 
('App::ComponentRequirementState' and 'App::ComponentRequirementState')
    r = r | App::ComponentRequirementState::AlwaysRequired;
        ~ ^ ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~

Я разместил минимальный пример кода здесь.

Странно то, что я могу использовать побитовые операторы внутри определения перечисления ComponentRequirementStat, но не вне его.

Я неправильно использую этот макрос? Или это просто не работает?

Когда я вручную определяю операторы, скажем,

auto operator|(const App::ComponentRequirementState a, App::ComponentRequirementState b) -> App::ComponentRequirementState
{
    return static_cast<App::ComponentRequirementState>(static_cast<unsigned int>(a) | static_cast<unsigned int>(b));
}

Тогда это работает.

15.01.2018

Ответы:


1

Использование Qt

Q_FLAGS_NS не поддерживает побитовые операции: он просто регистрирует тип в система метаобъектов Qt.

Если вы хотите зарегистрировать тип и обеспечить побитовую поддержку оператора, вам следует использовать QFlags.

Пример из документации:

class MyClass
{
public:
    enum Option {
        NoOptions = 0x0,
        ShowTabs = 0x1,
        ShowAll = 0x2,
        SqueezeBlank = 0x4
    };
    Q_DECLARE_FLAGS(Options, Option)
    ...
};

Q_DECLARE_OPERATORS_FOR_FLAGS(MyClass::Options)

Вы также можете просто пропустить большую часть этого и сделать:

// not type-safe, unscoped enum. You should likely define this
// in a namespace or class.  
enum Option {
    NoOptions = 0x0,
    ShowTabs = 0x1,
    ShowAll = 0x2,
    SqueezeBlank = 0x4
};

// type-safe flag
using Options = QFlags<Option>;

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

Безопасные типы побитовых операций с перечислениями в области видимости

Вот макрос, который вы можете свободно использовать (общественное достояние, мой собственный код, хотя вам, вероятно, следует изменить макрос, чтобы убедиться, что он имеет префикс, чтобы избежать любых конфликтов имен), чтобы добавить побитовые операции в перечисление с областью видимости. В настоящее время для этого требуется поддержка C++14, однако изменение std::underlying_type_t<T> на typename std::underlying_type<T>::type позволяет макросу работать в C++11.

Использовать

enum class enum1_t
{
    A = 1,
    B,
    C,
    D,
    E,
};


enum class enum2_t
{
    F = 1,
    G,
    H,
    I,
    J,
};

ENUM_FLAG(enum1_t)            // allow bitwise operations for enum1_t and enum1_t
ENUM_FLAG(enum1_t, enum2_t)   // allow bitwise operations for enum1_t and enum2_t

Код

#include <type_traits>
#include <cstdint>

// HELPERS
// -------

/**
 *  \brief Get enum underlying type.
 */
template <typename T>
inline std::underlying_type_t<T> int_t(T t)
{
    return static_cast<std::underlying_type_t<T>>(t);
}

// MACROS
// ------

/**
 *  \brief Macro to define enum operators between enumerations.
 *
 *  Supports `&`, `&=`, `|`, `|=`, `^`, `^=`, `~`, and bool conversion.
 */
#define ENUM_FLAG2(lhs_t, ths_t)                                        \
    /*  \brief Bitwise or operator. */                                  \
    inline lhs_t operator|(lhs_t lhs, ths_t rhs) noexcept               \
    {                                                                   \
        return static_cast<lhs_t>(int_t(lhs) | int_t(rhs));             \
    }                                                                   \
                                                                        \
    /*  \brief Bitwise or assignment operator. */                       \
    inline lhs_t & operator|=(lhs_t &lhs, ths_t rhs) noexcept           \
    {                                                                   \
        lhs = static_cast<lhs_t>(int_t(lhs) | int_t(rhs));              \
        return lhs;                                                     \
    }                                                                   \
                                                                        \
    /*  \brief Bitwise and operator. */                                 \
    inline lhs_t operator&(lhs_t lhs, ths_t rhs) noexcept               \
    {                                                                   \
        return static_cast<lhs_t>(int_t(lhs) & int_t(rhs));             \
    }                                                                   \
                                                                        \
    /*  \brief Bitwise and assignment operator. */                      \
    inline lhs_t & operator&=(lhs_t &lhs, ths_t rhs) noexcept           \
    {                                                                   \
        lhs = static_cast<lhs_t>(int_t(lhs) & int_t(rhs));              \
        return lhs;                                                     \
    }                                                                   \
                                                                        \
    /*  \brief Bitwise xor operator. */                                 \
    inline lhs_t operator^(lhs_t lhs, ths_t rhs) noexcept               \
    {                                                                   \
        return static_cast<lhs_t>(int_t(lhs) ^ int_t(rhs));             \
    }                                                                   \
                                                                        \
    /*  \brief Bitwise xor assignment operator. */                      \
    inline lhs_t & operator^=(lhs_t &lhs, ths_t rhs) noexcept           \
    {                                                                   \
        lhs = static_cast<lhs_t>(int_t(lhs) ^ int_t(rhs));              \
        return lhs;                                                     \
    }


/**
 *  \brief Set enumeration flags within the same enum.
 */
#define ENUM_FLAG1(enum_t)                                              \
    ENUM_FLAG2(enum_t, enum_t)                                          \
                                                                        \
    /*  \brief Bitwise negation operator. */                            \
    inline enum_t operator~(enum_t value) noexcept                      \
    {                                                                   \
        return static_cast<enum_t>(~int_t(value));                      \
    }                                                                   \
                                                                        \
    /*  \brief Negation operator. */                                    \
    inline bool operator!(enum_t value) noexcept                        \
    {                                                                   \
        return int_t(value) == 0;                                       \
    }

/**
 *  \brief Macros to grab the proper bit-wise flag setter.
 *  `ENUM_ID` is required for MSVC compatibility, since MSVC
 *  has issues in expanding `__VA_ARGS__` for the dispatcher.
 *  Don't remove it, even if the above code works without it 
 *  for GCC and Clang.
 */
#define ENUM_ID(x) x
#define GET_ENUM_FLAG(_1,_2,NAME,...) NAME
#define ENUM_FLAG(...) ENUM_ID(GET_ENUM_FLAG(__VA_ARGS__, ENUM_FLAG2, ENUM_FLAG1)(__VA_ARGS__))
15.01.2018
  • Спасибо! Я попробую это как можно скорее, хотя один вопрос, вы и документы, оба делаете Q_DECLARE_FLAGS(Options, Option), почему единственное и множественное число для Option? 16.01.2018
  • @Matt Форма множественного числа создает безопасный для типов флаг из перечисления в области класса (типа QFlags<T>). Затем Q_DECLARE_OPERATORS_FOR_FLAGS экспортирует все операторы в глобальную область. Вы можете немного опустить это, но я в основном использовал свой собственный макрос, а затем регистрировал его в объектной системе Qt в своем собственном коде. Оба работают, мне просто больше нравятся перечисления с областью видимости С++ 11, чем перечисления области видимости Qt. 16.01.2018
  • @Matt Я обновил свой пример, несмотря ни на что. По сути, класс в основном предназначен для предотвращения выхода значений перечисления с незаданной областью в глобальное пространство имен, а макросы — просто вспомогательные средства для создания экземпляра шаблона. 16.01.2018

  • 2

    Q_FLAG_NS не проблема. Это вызвано новыми перечислениями области действия C++11 (enum class). Они не преобразуются неявно в целочисленный тип, и вы не можете использовать их для побитовых операций, которые определены только для целочисленных типов [1][2].

    Вы должны предоставить эти операции самостоятельно или использовать перечисление unscoped, old.

    Одна из проблем, связанных с предоставлением этих операций, заключается в том, что потребуется определить все возможные комбинации флагов как значения перечисления или вернуть целочисленный тип:

    ??? operator|(App::ComponentRequirementState lhs, App::ComponentRequirementState rhs);
    
    15.01.2018
  • Да, я на самом деле выписал все комбинации. Вот тут-то я и надеялся, что Q_FLAG_NS поможет. И поскольку я не мог придумать шаблонное решение, я сделал это дважды. Сейчас я пытаюсь реализовать шаблонное решение Александра Хузага. 16.01.2018
  • Я пробовал это (в пространстве имен), он все еще не предоставлял мне операторов. Я не пытался сделать это в минимальном примере, может быть, я мог бы попробовать там, чтобы увидеть, смогу ли я заставить его работать. 16.01.2018
  • ??? operator|(App::ComponentRequirementState lhs, App::ComponentRequirementState rhs); На самом деле мне пришлось делать последнее в моем собственном коде, поэтому я написал макрос, который сделает это за меня. Макросы могут быть злыми, но в этом случае (безопасность типов) я считаю, что могу ослабить правило. Я действительно хотел бы, чтобы другие люди взяли мою работу, поскольку это означает, что другим не нужно писать тот же шаблон, что и я. 16.01.2018
  • Классические перечисления также не будут работать с auto по тем же причинам, но godbolt.org/g/pB7qFv 16.01.2018
  • К сожалению, это не обеспечивает безопасность типов. В противном случае было бы здорово. Я все еще могу выполнить Fruit::ORANGE | Vegetable::TOMATO и не иметь никаких проблем, даже если Fruit и Vegetable исключают друг друга. 16.01.2018
  • Новые материалы

    Обнаружение маски или без маски с YOLO😷
    Это руководство по созданию пользовательской модели обнаружения объектов для обнаружения людей, носящих или не носящих маски в общественных местах, созданной с использованием YOLO v3. Модель..

    Управление приборами в чистом PHP
    Этот пост дополняет эти: E2e тестирование Работа с несколькими средами . Мы разработали это решение для базы данных MariaDB, оно может отличаться, если вы используете другую базу..

    Неделя 1 — Кентерберийские рельсы.
    Неделя 1 — Кентерберийские рельсы. So. Мы все еще живы, все еще усердно работаем и еще не пассивно-агрессивно рассылаем друг другу сообщения «за мое последнее сообщение в Slack…», поэтому, на..

    Цена завтрашнего дня  — Джефф Бут
    Технологический прогресс в наши дни происходит с молниеносной скоростью, и мы не в состоянии это понять. Джефф в основном говорит о влиянии технологий на экономику по всему миру. Он твердо верит..

    Данные: суперсила современного бизнеса
    В цифровой среде данные превратились из простого побочного продукта бизнес-операций в центральный актив, стимулирующий рост и инновации. Крейг Манди, бывший главный директор по стратегии..

    Как симулировать серию пенальти на Python с помощью симуляции Монте-Карло, часть 1: генерация функций
    Серия пенальти была огромным испытанием во время чемпионата мира по футболу. Они вызвали много споров в социальных сетях и новостных агентствах. Даже финальный матч турнира решался по..

    AST для разработчиков JavaScript
    TL; DR Эта статья - мое выступление на недавно состоявшейся конференции Stockholm ReactJS Meetup. Вы можете посмотреть слайды здесь..