oktonion
Постоялец
Offline
|
|
« : 15-10-2017 12:02 » |
|
Здравствуйте,
ищу библиотеку/класс/кусок кода для удобной работы с bitmask для старого стандарта C++. Все что предлагают просторы интернета основано на std::bitset для C++11. Если у кого то есть готовое решение или ссылка на такое, а так же просто мысли как такой класс лучше организовать средствами C++ 98 того же, буду признателен.
|
|
|
Записан
|
|
|
|
Finch
Спокойный
Администратор
Online
Пол:
Пролетал мимо
|
|
« Ответ #1 : 15-10-2017 15:31 » |
|
Тут не написано, что данный шаблон специфичен для С++11 и выше. А в принципе, если только он тебе нужен. Можно взять его исходник. И причесать его к твоему компилятору.
|
|
|
Записан
|
Не будите спашяго дракона. Джаффар (Коша)
|
|
|
oktonion
Постоялец
Offline
|
|
« Ответ #2 : 16-10-2017 17:39 » |
|
Мне скорее нужно готовое решение чтобы не изобретать своего велосипеда удобного класса. std::bitset всегда идут в паре с новыми enum class из C++11 что в общем то логично, хотелось бы узнать может кто сталкивался с такой задачей.
|
|
« Последнее редактирование: 17-10-2017 14:35 от oktonion »
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #3 : 16-10-2017 22:13 » |
|
Класс std::bitset входит в STL C++98. В С++11 его расширили. Ничего не мешает использовать соответствующую версию. http://www.cplusplus.com/reference/bitset/bitset/Примеры оттуда, собранные с ключом -std=c++98, компилятся без вопросов.
|
|
« Последнее редактирование: 17-10-2017 06:04 от RXL »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
Finch
Спокойный
Администратор
Online
Пол:
Пролетал мимо
|
|
« Ответ #4 : 17-10-2017 02:54 » |
|
Насчет самого компилятора так и ничего и не узнали. Может он какой то специфический. Который не поддерживает даже стандарты своего времени. Твкже не понятна полностью задача. тебе нужен полный функционал класса или работа с битами?
|
|
« Последнее редактирование: 17-10-2017 02:58 от Finch »
|
Записан
|
Не будите спашяго дракона. Джаффар (Коша)
|
|
|
oktonion
Постоялец
Offline
|
|
« Ответ #5 : 17-10-2017 14:57 » |
|
Компилятор там действительно специфический, но по сути это смесь Watcom C и gcc древнего с обвязкой поверх. Уверен что std::biset там в каком то виде есть, вопрос в том что для полного решения нужна (везде используется) связка enum class + std::bitset, чего там явно нет. Мне нужна работа именно с масками в формате класса/классов. То есть установка/снятие последовательности бит, инверсия по маске и т.п., но типобезопасно и удобно. Я уже было ринулся писать свое решение, но подумал что наверное, как обычно, все написано до нас. Что-то в духе: enum Flags { flag1= 0x00, flag2 = 0x01, flag3 = 0x02, flag4 = 0x04, flag5= 0x08, };
bitmask<int, Flags> bm;
bm.set(flag1 | flag2); bm |= flag3; bm.flip(flag1);
Проблемы написать такое вроде и нет, но с другой стороны под C++98 ничего не нашел, да и при реализации возникают вопросы сразу же.
|
|
|
Записан
|
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #6 : 18-10-2017 04:19 » |
|
oktonion, а вот так твой компилятор умеет ? struct Flags { UINT8 flag1:1; UINT8 flag2:1; UINT8 flag3:1; UINT8 flag4:1; UINT8 flag5:1; UINT8 :3;//ещё три бита осталось
Flags() { memset(this,0,sizeof(*this)); } };
Flags bm;
bm.flag1=1; bm.flag2=1; bm.flag3=1; bm.flag1=~bm.flag1;
|
|
« Последнее редактирование: 18-10-2017 04:21 от Алексей++ »
|
Записан
|
|
|
|
oktonion
Постоялец
Offline
|
|
« Ответ #7 : 18-10-2017 06:31 » |
|
Битовые поля умеет. Вопрос не в том как получить доступ к битам (хоть побитовыми всякими |, &, ^, хоть поля), я видимо плохо описал что мне нужно из функциональности такого класса. Задача в следующем: Есть переменная типа T (размерности N bit), типы целые простые - int, unsigned, long, long long, short и т.п. Это переменная хранит набор заранее определенных флагов типа T1 (в общем то совпадает с типом T). Нужно дать простой и типобезопасный способ пользователю устанавливать и снимать, преобразовывать и др. операции с этими заранее определенными флагами над переменной. Я могу понять как сделать такой класс для работы с одним флагом - делаю отдельный enum для флагов, описываю операции для работы только с этим типом enum'а и вот пользователь может работать с переменной только с помощью определенных флагов, а если он попытается писать туда какую либо ахинею, то компилятор ругнется. Но не будет работы с флагами тогда объединенными по или к примеру и т.д. Тогда нужен класс еще для безопасного флага вместо enum выходит - это в C++11 как раз решается enum class, где перегружаются операции ( и, или, не и т.п.) и пишутся для них методы с понятными именами. Вот может есть что то подобное написанное под C++98 уже. Важно по возможности закрыть все способы пользователю отстрелить себе ногу, потому как установка неверной последовательности бит в переменную (отличную от флагов или их комбинации) приведет к неопределенному поведению. Поясню кодом : template<class MaskT, class FlagT> class bitmask { public: bitmask(const MaskT &val) : _val(val) { }
bitmask& set(const FlagT &flag) { _val |= flag; return *this; } /* нельзя, т.к. можно будет установить любую комбинацию флагов, отличную от комбинации в FlagT bitmask& set(const MaskT &flags) { _val |= flags; return *this; } */ private: MaskT _val; };
enum eFlags { flag1 = 0x00, flag2 = 0x01, flag3 = 0x02, flag4 = 0x04, flag5 = 0x08, };
bitmask<int, eFlags> bm(0); bm.set(flag1); // окей, по "или" безопасно выставляем флаг в переменную bm.set(flag1 | flag2); // хотелось бы так, но уже не выйдет
|
|
« Последнее редактирование: 18-10-2017 11:48 от oktonion »
|
Записан
|
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #8 : 18-10-2017 07:07 » |
|
oktonion, примени маску разрешённых битов enum eFlags { flag1 = 0x00, flag2 = 0x01, flag3 = 0x02, flag4 = 0x04, flag5 = 0x08,
mask=(flag1|flag2|flag3|flag4|flag5), }; и в методах класса после любого телодвижения накладывай маску на хранилище: компилятор ругаться не будет, но неадекватное значение задать будет невозможно. Также в дебажном рантайме можно ввести проверку, и при (_val & ~mask)!=0 выкидывать сообщение (но это уже по вкусу)
|
|
|
Записан
|
|
|
|
oktonion
Постоялец
Offline
|
|
« Ответ #9 : 18-10-2017 13:32 » |
|
Как вариант. Но хочется чтобы ругался. В итоге все же пишу свое. Для побитого или к примеру: namespace cppbitmasks { template<class MaskT, class FlagT> class bitmask { public: bitmask() : _val(MaskT()) { }
bitmask(const FlagT &flag) : _val(flag) { }
bitmask(const bitmask &other) : _val(other._val) { }
inline bitmask& set(const FlagT &flag) { _val |= flag; return *this; }
inline bitmask& set(const bitmask &mask) { _val |= mask._val; return *this; }
inline bitmask& operator|(const bitmask<MaskT, FlagT> &flag2) { return set(flag2); }
inline bitmask& operator|(const FlagT &flag2) { return set(flag2); }
bitmask& operator=(const bitmask &other) { _val = other._val; return *this; }
private: MaskT _val; };
template<class MaskT, class FlagT> inline bitmask<MaskT, FlagT> &operator|(const FlagT &flag1, bitmask<MaskT, FlagT> &flag2) { return flag2 | flag1; } } Использование: using namespace cppbitmasks;
enum eFlags { flag1 = 0x00, flag2 = 0x01, flag3 = 0x02, };
enum eFlags2 { flag4 = 0x04, flag5 = 0x08, };
//определяем оператор для самого enum bitmask<int, eFlags> operator|(const eFlags &flag1, const eFlags &flag2) { bitmask<int, eFlags> bm(flag1); return bm | flag2; }
int main() { bitmask<int, eFlags> bm;
flag3 | flag3; // ok, в bitmask преобразуется flag3 | bm; // ok, в bitmask преобразуется bm = flag3 | flag3; // ok bm = bm | flag3 | bm | flag2; // ok a.set(flag3 | flag2); // ok
bm = flag3 | flag4; // ошибка компиляции a = a | flag3 | a | flag4; // ошибка компиляции a.set(flag3 | flag4); // ошибка компиляции
return 0; } Пока что глупость написана в плане того что оператор изменяет значение, но направление гдето такое
|
|
« Последнее редактирование: 18-10-2017 14:27 от oktonion »
|
Записан
|
|
|
|
oktonion
Постоялец
Offline
|
|
« Ответ #10 : 18-10-2017 20:47 » |
|
В общем получилось вот так (вдруг кому-то пригодится): https://github.com/oktonion/PhWidgets-lib/blob/b7eaa6edb3e12c622214cec760af693591cd5b61/src/service/bitmask.hppВозможно что то упустил конечно, но работает так как нужно. А если определить еще свободные операторы для enum: cppbitmasks::bitmask<MaskT, FlagT> operator|(const FlagT &flag1, const FlagT &flag2); cppbitmasks::bitmask<MaskT, FlagT> operator&(const FlagT &flag1, const FlagT &flag2); cppbitmasks::bitmask<MaskT, FlagT> operator^(const FlagT &flag1, const FlagT &flag2); то и "слевые" выражения работать будут =)
|
|
« Последнее редактирование: 18-10-2017 20:51 от oktonion »
|
Записан
|
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #11 : 19-10-2017 04:29 » |
|
oktonion, моё мнение - загромождать код только ради того, чтобы компилятор ругался на лишние биты - это неоправданное усложнение )) Достаточно просто жёстко отключить маской запрещённые биты, а остальное пусть проверяет сам пользователь при использовании, тем более, что маску можно указать при создании экземпляра
а оператор "operator|" замени на "operator|=" - будет логичнее (имею в виду тот, который меняет значение, с присваиванием левому операнду )
|
|
« Последнее редактирование: 19-10-2017 04:31 от Алексей++ »
|
Записан
|
|
|
|
oktonion
Постоялец
Offline
|
|
« Ответ #12 : 19-10-2017 07:10 » |
|
Соглашусь, но смотря какой выхлоп от этого. Просто выбор "ошибка компиляции или неопределенное поведение" для меня очевиден. Выбор "ошибка компиляции или молча проглатывать и исправлять ошибку" не на столько очевиден, но по мне так лучше сделать так чтобы человек понимал что пытается сделать не то что нужно, чем помогать ему писать неверный код, тем более что это стоит только времени на разработку обвязки сверху. И еще мне лень для каждого enum заполнять разрешенную маску (там по 30 флагов может быть) =)
Ага, операторы я реализовал все, теперь все по логике верно уже. operator|= есть, просто это в следующем коммите.
|
|
« Последнее редактирование: 19-10-2017 07:21 от oktonion »
|
Записан
|
|
|
|
Джон
просто
Администратор
Offline
Пол:
|
|
« Ответ #13 : 19-10-2017 08:30 » |
|
Маленькая рекомендация inline bitmask& operator=(const bitmask &other) { _val = other._val; return *this; } в операторе присваивания проверять указатель inline bitmask& operator=(const bitmask &other) { if(this != &other) { _val = other._val; } return *this; } Иначе при a = a; ...
|
|
|
Записан
|
Я вам что? Дурак? По выходным и праздникам на работе работать. По выходным и праздникам я работаю дома. "Just because the language allows you to do something does not mean that it’s the correct thing to do." Trey Nash "Physics is like sex: sure, it may give some practical results, but that's not why we do it." Richard P. Feynman "All science is either physics or stamp collecting." Ernest Rutherford "Wer will, findet Wege, wer nicht will, findet Gründe."
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #14 : 19-10-2017 08:44 » |
|
Джон, а что будет, я ничего такого не вижу
|
|
|
Записан
|
|
|
|
oktonion
Постоялец
Offline
|
|
« Ответ #15 : 19-10-2017 10:54 » |
|
Алексей++, если там нетривиальный тип то может иметь какой то выигрыш проверка указателя по производительности, если еще и в этом типе хреново перегружен оператор присваивания. Для тривиальных типов проблемы не вижу. Джон, это же int'ы всякие.
|
|
|
Записан
|
|
|
|
Алексей++
глобальный и пушистый
Глобальный модератор
Offline
Сообщений: 13
|
|
« Ответ #16 : 19-10-2017 11:43 » |
|
oktonion, даже в случае наикривейшего оператора присваивания - это что же там нужно такое намутить, чтобы производительность упала А, кроме того, вообще наткнуться хотя бы один раз на ситуацию "a=a" , не говоря уж о возникновении её где-то в долгом цикле
|
|
|
Записан
|
|
|
|
oktonion
Постоялец
Offline
|
|
« Ответ #17 : 19-10-2017 11:59 » |
|
Да легко //никогда так не пишите class IneffectiveString { public: IneffectiveString &operator=(IneffectiveString other) { for (int i = 0; i < _str.length(); ++i) { _str.pop_back(); }
for (int i = 0; i < other._str.length(); ++i) { _str.push_back(other._str[i]); }
return *this; } private: std::string _str; }; Хотя сказал что "легко", а вообще как-то сложно написать неэффективное копирование. Ну допустим там тяжеловесная операция какая то, запрос в базу данных. Это так все, из области никак не соприкасающейся с рассматриваемой задачей в итоге то.
|
|
« Последнее редактирование: 19-10-2017 12:02 от oktonion »
|
Записан
|
|
|
|
RXL
Технический
Администратор
Offline
Пол:
|
|
« Ответ #18 : 19-10-2017 21:31 » |
|
Такая мысль. template <typename T> class bitset { T bits; public: bitset() : bits(0) {} bitset(const T init) : bits(init) {} const T& get() const { return bits; } // далее определяем битовые операции только между bitset<T> и никаких конверсий в другие типы };
const bitset<uint8_t> flag0(1), flag1(2), flag2(4), flag3(8), flag4(16), flag5(32);
bitset<uint8_t> bm(flag1); bm |= flag2 | flag4; uint8_t n = bm.get();
|
|
« Последнее редактирование: 19-10-2017 21:33 от RXL »
|
Записан
|
... мы преодолеваем эту трудность без синтеза распределенных прототипов. (с) Жуков М.С.
|
|
|
oktonion
Постоялец
Offline
|
|
« Ответ #19 : 20-10-2017 10:47 » |
|
RXL, так это и есть стандартный std::bitset. Для задачи не подходит по очевидным причинам: const bitset<uint8_t> user_flag(31);
bitset<uint8_t> bm(flag1); bm |= flag2 | flag4 | user_flag; // ошибка, пользователю "нельзя" использовать этот флаг, но он захотел и сделал.
|
|
|
Записан
|
|
|
|
oktonion
Постоялец
Offline
|
|
« Ответ #20 : 22-10-2017 23:22 » |
|
По итогу вот что получилось: bitmask.hppпример использования для enum: #include "bitmask.hpp"
enum eExFlags { ConsumeEvents = 0x00, InternalHelp = 0x01, SkipLayout = 0x02, };
cppbitmasks::bitmask<unsigned long, eExFlags> operator|(const eExFlags &flag1, const eExFlags &flag2) { cppbitmasks::bitmask<unsigned long, eExFlags> bm(flag1); return bm | flag2; }
cppbitmasks::bitmask<unsigned long, eExFlags> operator&(const eExFlags &flag1, const eExFlags &flag2) { cppbitmasks::bitmask<unsigned long, eExFlags> bm(flag1); return bm & flag2; }
cppbitmasks::bitmask<unsigned long, eExFlags> operator^(const eExFlags &flag1, const eExFlags &flag2) { cppbitmasks::bitmask<unsigned long, eExFlags> bm(flag1); return bm ^ flag2; }
void work_with_exflags(unsigned long flags) { //some work }
void func(void) { using namespace cppbitmasks; bitmask<unsigned long, eExFlags> bm;
bm |= ConsumeEvents | SkipLayout; bm.reset(SkipLayout); bm = InternalHelp | bm;
work_with_exflags(bm); }
Я, честно говоря, доволен. Хотя есть еще куда расширяться. Например добавить "запрещенные" на запись флаги - как раз как предлагал Алексей, масками.
|
|
« Последнее редактирование: 23-10-2017 06:11 от oktonion »
|
Записан
|
|
|
|
|