Как-то не продумано это. А используется везде и постоянно. За неимением флагов народ использует enum, а в новых плюсиках — enum class, но это же костыли.
Хочется типизированного флагового типа, хз, какое ключевое слово к нему прилепить, может enum class flags bla-bla
Пока обхожусь разными вариантами, в основном enum/enum class, или что-то типа такого:
template<typename UnderlayingType>
struct Flags
{
typedef UnderlayingType underlaying_type;
static const UnderlayingType v1 = x1;
//...
// Тут конструкторы и преобразователи в UnderlayingType
// Можно брать значение, как мембер с именем value
underlaying_type value;
};
Здравствуйте, Marty, Вы писали:
M>Как-то не продумано это. А используется везде и постоянно. За неимением флагов народ использует enum, а в новых плюсиках — enum class, но это же костыли.
M>Хочется типизированного флагового типа, хз, какое ключевое слово к нему прилепить, может enum class flags bla-bla
Непонятно:
1. Что именно ты подразумеваешь под "флагом"
2. Какую задачу хочешь решить.
bool theFlag;
struct Flagzz
{
unsigned int flagA:1;
unsigned int flagB:1;
}
Вполне себе флаги и существуют давно. Плюс упомянутые тобой енумы.
_____________________
С уважением,
Stanislav V. Zudin
Здравствуйте, Stanislav V. Zudin, Вы писали:
SVZ>Здравствуйте, Marty, Вы писали:
M>>Как-то не продумано это. А используется везде и постоянно. За неимением флагов народ использует enum, а в новых плюсиках — enum class, но это же костыли.
M>>Хочется типизированного флагового типа, хз, какое ключевое слово к нему прилепить, может enum class flags bla-bla
SVZ>Непонятно: SVZ>1. Что именно ты подразумеваешь под "флагом" SVZ>2. Какую задачу хочешь решить.
SVZ>
bool theFlag;
SVZ>struct Flagzz
SVZ>{
SVZ> unsigned int flagA:1;
SVZ> unsigned int flagB:1;
SVZ>}
SVZ>
SVZ>Вполне себе флаги и существуют давно.
Это — слишком ограниченная реализация набора флагов, ибо она не поддерживает run-time адресации флагов. Это настолько серьезное ограничение, что такая реализация практически бесполезна.
Любая реализация набора флагов должна поддерживать операции вроде "проверить, выставлен ли хотя бы один флаг по маске", "сбросить все флаги по маске" и т.п. для run-time маски. По этой причине флаги всегда сразу вручную пакуют в целочисленный тип. Реализация набора флагов через битовые поля — антипаттерн, который в какой-то момент обязательно приведет к необходимости переделки кода: выкашиванию битовых полей и использованию целочисленного типа вместо них.
Здравствуйте, Marty, Вы писали:
M>Здравствуйте!
M>Как-то не продумано это. А используется везде и постоянно. За неимением флагов народ использует enum, а в новых плюсиках — enum class, но это же костыли.
M>Хочется типизированного флагового типа, хз, какое ключевое слово к нему прилепить, может enum class flags bla-bla
M>Пока обхожусь разными вариантами, в основном enum/enum class, или что-то типа такого: M>
template<typename UnderlayingType>
M>struct Flags
M>{
M> typedef UnderlayingType underlaying_type;
M> static const UnderlayingType v1 = x1;
M> //...
M> // Тут конструкторы и преобразователи в UnderlayingType
M> // Можно брать значение, как мембер с именем value
M> underlaying_type value;
M>};
Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>Любая реализация набора флагов должна поддерживать операции вроде "проверить, выставлен ли хотя бы один флаг по маске", "сбросить все флаги по маске" и т.п. для run-time маски. По этой причине флаги всегда сразу вручную пакуют в целочисленный тип. Реализация набора флагов через битовые поля — антипаттерн, который в какой-то момент обязательно приведет к необходимости переделки кода: выкашиванию битовых полей и использованию целочисленного типа вместо них.
н-р чем такая реализация не устраивает?
union BitMask
{
unsigned int all = 0;
struct Bits
{
unsigned int first: 1;
unsigned int second: 1;
unsigned int third: 1;
} bits;
};
Доступен каждый бит и в тоже время наложить маску никто не запрещает.
Здравствуйте, sergii.p, Вы писали:
SP>Здравствуйте, Андрей Тарасевич, Вы писали:
АТ>>Любая реализация набора флагов должна поддерживать операции вроде "проверить, выставлен ли хотя бы один флаг по маске", "сбросить все флаги по маске" и т.п. для run-time маски. По этой причине флаги всегда сразу вручную пакуют в целочисленный тип. Реализация набора флагов через битовые поля — антипаттерн, который в какой-то момент обязательно приведет к необходимости переделки кода: выкашиванию битовых полей и использованию целочисленного типа вместо них.
SP>н-р чем такая реализация не устраивает?
SP>
SP>union BitMask
SP>{
SP> unsigned int all = 0;
SP> struct Bits
SP> {
SP> unsigned int first: 1;
SP> unsigned int second: 1;
SP> unsigned int third: 1;
SP> } bits;
SP>};
SP>
SP>Доступен каждый бит и в тоже время наложить маску никто не запрещает.
Вот сейчас опять надо с флагами повозится, думаю, как быть.
1) enum class, + куча свободных функций operator&, operator|, operator^ и тп, в которых кастить с underlying_type, делать операцию, и кастить результат обратно. Из плюсов — оно будет двоично совместимо — т.е. где-то во внешней С-либе есть функция void doSomething(..., unsigned flags), я у себя могу написать прототип, где поменяю тип флагов с unsigned на мой enum class и всё будет работать. Из минусов — будут порождаться значения, которых нет в исходном enum, и это попахивает говнецом
Вроде получше, но не совсем уверен, что можно подменять тип в прототипе внешней сишной функции, хотя вроде такая структура должна по значению передаться как и примитивный тип.
Второе — если куда-то надо передать набор флагов, то придётся писать SomeFlags(SomeFlags::flag1|SomeFlags::flag2), что тоже не очень. В принципе, можно конструктор и оператор преобразования сделать не explicit — нет ли тут граблей? Не будет ли неявного преобразования через underlying_type в другой аналогичный флаговый тип? Тогда получится, что хоть этот тип можно для перегрузки использовать, но все равно можно подсунуть чужие флаги, и получается практически то же самое, от чего хотелось уйти.
Третье — я пока в рамках 0x03 стандарта прикидывал, там нельзя было в классе инициализировать кроме static const интегральных типов. Наверное, если подумать, в рамках стандартов посвежее можно сделать что-то более приятное?
В общем, кто что думает, какой способ лучше? И что надо добавить/убавить к одному из перечисленных вариантов?
В общем, покопал я немножко инет на тему флагов, проблема, как оказалось, зудит не только у меня.
Накопал, что C++ сейчас гарантирует, что все возможные битовые комбинации значений enum'а влезут в underlying тип (если не задан явно). Похоже, что это таки подготовка к тому, чтобы из enum'а сделать флаговый тип просто каким-нибудь атрибутов, или ещё как. Но C++29 или 32, в котором это таки будет реализовано, мне ждать неохота, поэтому я накидал свой макросик (вернее, большую часть нагло стырил где-то тут — https://stackoverflow.com/questions/1448396/how-to-use-enums-as-flags-in-c)
Там, кстати, нашел, что оказывается в winnt.h есть подобный макрос, называется DEFINE_ENUM_FLAG_OPERATORS
В общем, у меня такой получился:
Скрытый текст
#define MARTY_CPP_MAKE_ENUM_FLAGS(TEnum) \
inline constexpr bool operator==(TEnum a, std::underlying_type<TEnum>::type b) { \
using TUnder = typename std::underlying_type<TEnum>::type; \
return static_cast<TUnder>(a) == b; \
} \
inline constexpr bool operator==(std::underlying_type<TEnum>::type a, TEnum b) { \
using TUnder = typename std::underlying_type<TEnum>::type; \
return a == static_cast<TUnder>(b); \
} \
inline constexpr bool operator!=(TEnum a, std::underlying_type<TEnum>::type b) { \
using TUnder = typename std::underlying_type<TEnum>::type; \
return static_cast<TUnder>(a) != b; \
} \
inline constexpr bool operator!=(std::underlying_type<TEnum>::type a, TEnum b) { \
using TUnder = typename std::underlying_type<TEnum>::type; \
return a != static_cast<TUnder>(b); \
} \
inline constexpr TEnum operator<<(TEnum a, unsigned b) { \
using TUnder = typename std::underlying_type<TEnum>::type; \
return static_cast<TEnum>(static_cast<TUnder>(a) << b); \
} \
inline constexpr TEnum operator>>(TEnum a, unsigned b) { \
using TUnder = typename std::underlying_type<TEnum>::type; \
return static_cast<TEnum>(static_cast<TUnder>(a) >> b); \
} \
inline constexpr TEnum operator~(TEnum a) { \
using TUnder = typename std::underlying_type<TEnum>::type; \
return static_cast<TEnum>(~static_cast<TUnder>(a)); \
} \
inline constexpr TEnum operator|(TEnum a, TEnum b) { \
using TUnder = typename std::underlying_type<TEnum>::type; \
return static_cast<TEnum>(static_cast<TUnder>(a) | static_cast<TUnder>(b)); \
} \
inline constexpr TEnum operator&(TEnum a, TEnum b) { \
using TUnder = typename std::underlying_type<TEnum>::type; \
return static_cast<TEnum>(static_cast<TUnder>(a) & static_cast<TUnder>(b)); \
} \
inline constexpr TEnum operator^(TEnum a, TEnum b) { \
using TUnder = typename std::underlying_type<TEnum>::type; \
return static_cast<TEnum>(static_cast<TUnder>(a) ^ static_cast<TUnder>(b)); \
} \
inline constexpr TEnum& operator<<=(TEnum& a, unsigned b) { \
using TUnder = typename std::underlying_type<TEnum>::type; \
a = static_cast<TEnum>(static_cast<TUnder>(a) << b ); \
return a; \
} \
inline constexpr TEnum& operator>>=(TEnum& a, unsigned b) { \
using TUnder = typename std::underlying_type<TEnum>::type; \
a = static_cast<TEnum>(static_cast<TUnder>(a) >> b ); \
return a; \
} \
inline constexpr TEnum& operator|=(TEnum& a, TEnum b) { \
using TUnder = typename std::underlying_type<TEnum>::type; \
a = static_cast<TEnum>(static_cast<TUnder>(a) | static_cast<TUnder>(b)); \
return a; \
} \
inline constexpr TEnum& operator&=(TEnum& a, TEnum b) { \
using TUnder = typename std::underlying_type<TEnum>::type; \
a = static_cast<TEnum>(static_cast<TUnder>(a) & static_cast<TUnder>(b)); \
return a; \
} \
inline constexpr TEnum& operator^=(TEnum& a, TEnum b) { \
using TUnder = typename std::underlying_type<TEnum>::type; \
a = static_cast<TEnum>(static_cast<TUnder>(a) ^ static_cast<TUnder>(b)); \
return a; \
}
Я добавил сдвиг и сравнение (==/!=) с underlying type.
Сдвиг для битовых масок имхо полезен.
Сравнение — чтобы можно было делать что-то типа if ((a&b)==0). Я обычно явно так пишу, но некоторые не пишут, и тут пока есть недоработка — нельзя просто написать if (a&b). Меня особо не парит, но если есть идеи — предлагаю обсудить
Здравствуйте, vopl, Вы писали:
M>>В общем, у меня такой получился:
V>ИМХО, у Videoman подход более приятный, более сиплюсплюсный. И его можно и в классах использовать тоже .
Ну, вот когда он все проблемы решит, тогда и перейду на его подход
Здравствуйте, Marty, Вы писали:
M>Здравствуйте, vopl, Вы писали:
M>>>В общем, у меня такой получился:
V>>ИМХО, у Videoman подход более приятный, более сиплюсплюсный. И его можно и в классах использовать тоже .
M>Ну, вот когда он все проблемы решит, тогда и перейду на его подход
Так у него нет проблем, все нормально фурычит. Но, конечно же, хозяин — барин
Здравствуйте, vopl, Вы писали:
V>Так у него нет проблем, все нормально фурычит. Но, конечно же, хозяин — барин
Как это нет?
V>1. Данных подход очень не удобен в случае если у нас множество вложенных наймспейсов. Каждый раз приходится "лесенкой" выходит их них, а потом объявлять шаблон полностью квалифицируя именя. Типа:
V>2. У нас флаги очень часто используются внутри классов. В этом случае все еще сильнее усложняется.
V>3. Для флагов в защищенных секциях данный подход вообще не работает.
Здравствуйте, Marty, Вы писали:
M>Здравствуйте, vopl, Вы писали:
V>>Так у него нет проблем, все нормально фурычит. Но, конечно же, хозяин — барин
M>Как это нет?
M>
V>>1. Данных подход очень не удобен в случае если у нас множество вложенных наймспейсов. Каждый раз приходится "лесенкой" выходит их них, а потом объявлять шаблон полностью квалифицируя именя. Типа:
V>>2. У нас флаги очень часто используются внутри классов. В этом случае все еще сильнее усложняется.
V>>3. Для флагов в защищенных секциях данный подход вообще не работает.