Здравствуйте, Erop, Вы писали:
k55>>error C2104 "'&' в битовом поле". k55>> Была сделана попытка взять адрес битового поля.
E>В стандарте, там где bitfields jписаны...
E>Общая идея такая, что когда ты используешь bitfields, например так:
struct MyBitFields {
E> int Data1 : 7;
E> int Data2 : 12;
E> int Data3 : 13;
E>};
E>То ты говоришь компилятору, что тебе не так важно быстро и удобно работать с этими данным, а важно, чтобы они занимали поменьше места.
Битовые поля — вообще чудаческие вещи.
Например, struct Test { signed int x : 1; }. Какие значения может принимать Test::x? Правильно, 0 и -1. test.x = 1 — переполнение.
Всё даже еще хуже. struct Test { int x : 1; } test = { 1 } может быть и well-formed. Потому что во всех остальных местах int = signed int, но только не в битовых полях! Там это просто не определено (9.6/3), так что int без указания знаковости может быть и unsigned.
Вообще,
1. Битовые поля — зло;
2. Битовые поля с членами знаковых типов — абсолютное зло. :-)
Здравствуйте, k55, Вы писали:
k55>Подскажите где посмотреть вот про этот запрет (или объясните чем он обусловлен):
k55>error C2104 "'&' в битовом поле". k55> Была сделана попытка взять адрес битового поля.
Думаю, потому что минимально адресуемым является байт, а не бит...
Здравствуйте, Erop, Вы писали: E>Ну у тебя же и так там объединение из структуры из двух байт и одного двубайта? E>Зачем туда ещё и bitfields прихреначивать?
Мля.
Горе от ума.
Спасибо.
Если есть желание — найдется 1000 возможностей.
Если нет желания — найдется 1000 причин.
Здравствуйте, k55, Вы писали:
k55>error C2104 "'&' в битовом поле". k55> Была сделана попытка взять адрес битового поля.
В стандарте, там где bitfields jписаны...
Общая идея такая, что когда ты используешь bitfields, например так:
struct MyBitFields {
int Data1 : 7;
int Data2 : 12;
int Data3 : 13;
};
То ты говоришь компилятору, что тебе не так важно быстро и удобно работать с этими данным, а важно, чтобы они занимали поменьше места.
Соответсвенно компилятор заведёт в вышеприведённом примере (это от реализации вообще-то зависит, но так в популярных тут компиляторах будет) ОДНО поле типа int и будет в нём всякими операциями с битами доставать и устанавливать куски.
То есть он, реально родит что-то вроде такого класса (это не реальный код, а просто изложение идеии на языке С++. С формальной точкип зрения тут есть много тонкостей, учёт которых загромоздит объяснение):
class MyBitFieldsImpl {
public:
void SetData1( int value ) { setField( Data1_mask, Data1_offset, value ); }
int GetData1() const { return getField( Data1_mask, Data1_offset ); }
void SetData2( int value ) { setField( Data2_mask, Data2_offset, value ); }
int GetData2() const { return getField( Data2_mask, Data2_offset ); }
void SetData3( int value ) { setField( Data3_mask, Data3_offset, value ); }
int GetData3() const { return getField( Data3_mask, Data3_offset ); }
private:
enum { Data1_offset = 0, Data1_mask = 127 };
enum { Data2_offset = 7, Data2_mask = (1 << 20) - 1 - Data1_mask };
enum { Data3_offset = 19, Data3_mask = 0xFFFFFFFF - Data2_mask };
int data;
void setField( int mask, int offset, int value )
{
data = ( data & ~mask ) | ( mask & ( value << offset ) );
}
int getField( int mask, int offset ) const
{
return ( data >> offset ) & mask;
}
}
В результате ты получаешь более компактные данные, но платишь за это менее эффективной работой с ними. Кроме того у тебя возникает ряд ограничений, одно из которых -- отсутсвие адреса у bitfield.
B действительно, просто в памяти компьютера физически нет такого int, в котором хранится поле MyFields::Data2, например. Так что и адреса у него нет.
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, k55, Вы писали:
k55>Ну и в ответ первому устройству нужно состовную команду из байтов высылать. k55>Посчитал что так удобнее будет.
Ну у тебя же и так там объединение из структуры из двух байт и одного двубайта?
Зачем туда ещё и bitfields прихреначивать?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, k55, Вы писали:
k55>От устройства идет команда в два байта, а точнее шорт. k55>Программа парсит эту команду и в соотвествии с протоколом выдает другому устройству нечто другое. k55>Так вот, чтобы распарсить нужно обращаться к каждому байту.
Здравствуйте, Roman Odaisky, Вы писали:
RO>Всё даже еще хуже. struct Test { int x : 1; } test = { 1 } может быть и well-formed.
Не придирки ради, но только точности для. Well-formed оно-то в любом случае будет (ну, про точку с запятой все понятно, да). Вот значение и в самом деле может быть implementation-defined.
Здравствуйте, valker, Вы писали:
V>Здравствуйте, k55, Вы писали:
k55>>Подскажите где посмотреть вот про этот запрет (или объясните чем он обусловлен):
k55>>error C2104 "'&' в битовом поле". k55>> Была сделана попытка взять адрес битового поля.
V>Думаю, потому что минимально адресуемым является байт, а не бит...
Даже если у меня структура вида:
union TPairByte {
struct {
byte m_FirstByte : 8;
byte m_SecondByte : 8;
};
short m_CommandShort;
};
Т.е. компилятор не парится с разбором?
Если есть желание — найдется 1000 возможностей.
Если нет желания — найдется 1000 причин.
А смысл?
k55>Т.е. компилятор не парится с разбором?
А никто не обещал, что он по граница байт будет на всех платформах ранить...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
От устройства идет команда в два байта, а точнее шорт.
Программа парсит эту команду и в соотвествии с протоколом выдает другому устройству нечто другое.
Так вот, чтобы распарсить нужно обращаться к каждому байту.
Ну и в ответ первому устройству нужно состовную команду из байтов высылать.
Посчитал что так удобнее будет.
Если есть желание — найдется 1000 возможностей.
Если нет желания — найдется 1000 причин.
Здравствуйте, Roman Odaisky, Вы писали:
RO>2. Битовые поля с членами знаковых типов — абсолютное зло.
ИМХО Гитлер всё-таки был хуже
Но в целом я согласен, что bitfields лучше избегать. Просто потому, что так проще.
Есть ещё один забабах -- это когда ты так как-то пишешь:
enum MyEnum { zero, one, two, three };
struct MyStruct { MyEnum Data : 2; };
MyStruct tmp = { two };
То фиг его знаешь чего там получишь
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Roman Odaisky, Вы писали:
Pzz>>P.S. А вот адрес inline функции брать можно — такой у нас уродский язык RO>За что так сразу. inline — это стандартный аналог того, что MS называет __declspec(selectany), и это очень важно.
Вообще говоря, нет. __declspec(any) применяется к переменным, inline к ним неприменим.
Здравствуйте, Roman Odaisky, Вы писали:
Pzz>>P.S. А вот адрес inline функции брать можно — такой у нас уродский язык
RO>За что так сразу. inline — это стандартный аналог того, что MS называет __declspec(selectany), и это очень важно.
Я не знаю, чего мелкософт называет этим страшным словом. Но inline — это намек компилятору о том, что функцию хорошо бы поинлаинить. А register — намек компилятору о том, что переменную хорошо бы положить в регистр. Очевидно, что если функцию удалось поинлайнить, и переменную удалось положить в регистр, у них нет адреса. При этом адрес инлайновой функции получить можно (компилятору, естественно, придется ради этого сгенерировать нормальную, не инлайновую функцию), а адрес регистровой переменной получить нельзя. Получается неконсистентность.
Здравствуйте, Pzz, Вы писали:
Pzz>Но inline — это намек компилятору о том, что функцию хорошо бы поинлаинить.
В действительности современные компиляторы пробуют использовать все доступные в точке вызова определения функций. И inline и не inline. Но ключевое слово inline позволяет опубликовать определение функции для нескольких едениц трансляции, не нарушая ODR.
При этом указатель на inline функцию ничем не хуже указателя на не-inline функцию.
Ты же не можешь с указателем на функцию сдлеать ничего, кроме как сравнить два, или вызывать ненулевой. К тому подставился код функции в каком-то другом месте или нет это не имеет никакого отношения...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
E>В действительности современные компиляторы пробуют использовать все доступные в точке вызова определения функций. И inline и не inline. Но ключевое слово inline позволяет опубликовать определение функции для нескольких едениц трансляции, не нарушая ODR.
"Использовать" это оговорка? Имелось ввиду, инлайнить?
Современные компиляторы хоть и умеют автоматически инлайнить, обычно они все же воспринимают намеки. Скажем, явное описание inline может привести к тому, что поинлайнится функция, которую иначе бы компилятор инлайнить не стал.
E>При этом указатель на inline функцию ничем не хуже указателя на не-inline функцию. E>Ты же не можешь с указателем на функцию сдлеать ничего, кроме как сравнить два, или вызывать ненулевой. К тому подставился код функции в каком-то другом месте или нет это не имеет никакого отношения...
Это все не так просто, на самом деле. Например, если описать функцию в ашнике, и использовать этот ашник из нескольких разных модулей, указатель на функцию будет один и тот же. Статические переменные инлайновой функции будут одни и те же. Это требует довольно нетривиальной поддержки в линкере.
Здравствуйте, Pzz, Вы писали:
Pzz>"Использовать" это оговорка? Имелось ввиду, инлайнить?
А что обозначает слово "инлийнить"? Вообще-то компилятор не обязан именно подставлять. Он просто может использовать для оптимизации определение функции. Есть много путей это сделать вообще-то.
Pzz>Современные компиляторы хоть и умеют автоматически инлайнить, обычно они все же воспринимают намеки. Скажем, явное описание inline может привести к тому, что поинлайнится функция, которую иначе бы компилятор инлайнить не стал.
Ну обычно у них есть ещё всякие другие слова для намёков. А вот собственно inline примерно так же учитывают, как register...
Pzz>Это все не так просто, на самом деле. Например, если описать функцию в ашнике, и использовать этот ашник из нескольких разных модулей, указатель на функцию будет один и тот же. Статические переменные инлайновой функции будут одни и те же. Это требует довольно нетривиальной поддержки в линкере.
Во-первых это таки проблемы линкера, но они невелики
Во-вторых что тут умного? Ну заводишь сегмент данных который называется "статическая переменная из такой-то функции" и всюду ссылаешься на него.
Ну и ставишь на этот символ флаг, чтобы линкер не ругался на наружение ODR, а взял любое из определений.
Ну и на само тело функции тоже.
И что за проблема?
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, Erop, Вы писали:
Pzz>>"Использовать" это оговорка? Имелось ввиду, инлайнить? E>А что обозначает слово "инлийнить"? Вообще-то компилятор не обязан именно подставлять. Он просто может использовать для оптимизации определение функции. Есть много путей это сделать вообще-то.
Вы что сказать-то хотите?
Pzz>>Это все не так просто, на самом деле. Например, если описать функцию в ашнике, и использовать этот ашник из нескольких разных модулей, указатель на функцию будет один и тот же. Статические переменные инлайновой функции будут одни и те же. Это требует довольно нетривиальной поддержки в линкере.
E>Во-первых это таки проблемы линкера, но они невелики E>Во-вторых что тут умного? Ну заводишь сегмент данных который называется "статическая переменная из такой-то функции" и всюду ссылаешься на него.
У Вас появляется ситуация, когда один и тот же символ определен во многих модулях. Линкер должен понять, что это не ошибка, убедиться, что это и впрямь один и тот же символ, и слить все инкарнации в одну при формировании образа программы.
Такой интеллект в линкере появился только вместе с C++, раньше этого не было.
Кстати, ситуация становится еще интереснее, если разные части программы собирались разными компиляторами, но с одними и теми же .h-файлами, и инлайновыми функциями, в них определенными. Или, допустим, если инлайновая функция используется и в программе, и в DLL, т.е. в разных единицах компоновки. Кстати, насколько я понимаю, ни один из существующих тулчейнов не умеет разруливать такие хитрые случаи корректно.
Здравствуйте, Erop, Вы писали:
E>Здравствуйте, Roman Odaisky, Вы писали:
RO>>2. Битовые поля с членами знаковых типов — абсолютное зло. E>ИМХО Гитлер всё-таки был хуже
E>Но в целом я согласен, что bitfields лучше избегать. Просто потому, что так проще.
Да вы просто не умеете их готовить...
bitfields нужны только в тех случаях, когда нужно запарсить какой-то int или short или даже char.
Например выставить какие-то флажки в регистрах аппаратуры или иногда в базе данных.
Здравствуйте, andrey-S, Вы писали:
AS>Да вы просто не умеете их готовить... AS>bitfields нужны только в тех случаях, когда нужно запарсить какой-то int или short или даже char. AS>Например выставить какие-то флажки в регистрах аппаратуры или иногда в базе данных.
Ну это пока ты не захотел переносимости на другой компилятор...
Вообще-то я не понимаю, что мешает написать переносимое и последовательное C++ средство доступа к битовому полю...
Хотя и через bf эта проблема решается более или менее приемлемо.
Так что если у тебя это только в одном месте, то я бы может и оставил бы bf, или через маски написал бы.
Если местах в пяти, то я бы, конечно написал какой-нибудь способ порождать геттеры и сеттеры.
E>>Есть ещё один забабах -- это когда ты так как-то пишешь:
enum MyEnum { zero, one, two, three };
AS>В bitfields все поля обязательно должны быть unsigned. Иначе могут быть бааальшие проблемы при проверках типа: mode.dw.NofPulses == 6
Когда речь идёт о enums, то у тебя обычно нет возможности навязать компилятору беззнаковость...
Все эмоциональные формулировки не соотвествуют действительному положению вещей и приведены мной исключительно "ради красного словца". За корректными формулировками и неискажённым изложением идей, следует обращаться к их автором или воспользоваться поиском
Здравствуйте, andrey-S, Вы писали:
AS>Да вы просто не умеете их готовить... AS>bitfields нужны только в тех случаях, когда нужно запарсить какой-то int или short или даже char. AS>Например выставить какие-то флажки в регистрах аппаратуры или иногда в базе данных.
Да, это все правда. Но только при двух условиях:
1) переносимость не нужна;
2) скорость не интересует.
Здравствуйте, Erop, Вы писали:
AS>>bitfields нужны только в тех случаях, когда нужно запарсить какой-то int или short или даже char. AS>>Например выставить какие-то флажки в регистрах аппаратуры или иногда в базе данных.
E>Ну это пока ты не захотел переносимости на другой компилятор...
Если разрядность процессора не меняется, то никакой непереносимости вроде нет. Да и тогда эта проблема разрешима.
Формат получается одинаковый и в С, и в С++ в MSVC, Builder-6 и в мVision.
Под другим компилятором наверное понимается другой язык программирования? Или я чего-то не понял.
E>Вообще-то я не понимаю, что мешает написать переносимое и последовательное C++ средство доступа к битовому полю... E>Хотя и через bf эта проблема решается более или менее приемлемо. E>Так что если у тебя это только в одном месте, то я бы может и оставил бы bf, или через маски написал бы. E>Если местах в пяти, то я бы, конечно написал какой-нибудь способ порождать геттеры и сеттеры.
Как правило работа с аппаратурой реализуется в одном проекте и компилится одним компилятором.
В этом случае битовое поле довольно удобное средство поскольку:
1) для создания и поддержки разных форматов требуется гораздо меньше усилий
(например написание и поддержка методов get и set например для 20-40 разных форматов — довольно трудоёмко)
2) нельзя по ошибке выставить "левый" разряд в битовом поле перепутав его название,
чего не скажешь про маски. Они работают по принципу все со всеми.
3) часто запись с использованием битового поля короче и нагляднее чем через маски:
Впрочем я предпочитаю комбинацию bitfield и масок:
bitfield — если надо установить одно поле,
маска — если надо установить сразу несколько полей, а форматы уже утряслись
Если требуется платформонезависимый экспорт, можно создать это средство.
Но в реализации его также часто удобно использовать bitfields.
E>>>Есть ещё один забабах -- это когда ты так как-то пишешь:
enum MyEnum { zero, one, two, three };
AS>>В bitfields все поля обязательно должны быть unsigned. Иначе могут быть бааальшие проблемы при проверках типа: mode.dw.NofPulses == 6 E>Когда речь идёт о enums, то у тебя обычно нет возможности навязать компилятору беззнаковость...
Потому надо взять за правило: не использовать enum в bitfields.
Здравствуйте, elcste, Вы писали:
AS>>Да вы просто не умеете их готовить... AS>>bitfields нужны только в тех случаях, когда нужно запарсить какой-то int или short или даже char. AS>>Например выставить какие-то флажки в регистрах аппаратуры или иногда в базе данных.
E>Да, это все правда. Но только при двух условиях:
E>1) переносимость не нужна; E>2) скорость не интересует.
Здравствуйте, elcste, Вы писали:
E>>>2) скорость не интересует. AS>>Про скорость не понял . Можете пояснить?
E>Попробуйте написать разбор/создание какого-нибудь битстрима на bit-fields и посмотрите, какой код Вам сгенерировал Ваш компилятор.
Согласен . В обычной практики для экономии места использовать bit-fields не стоит.
Я лишь хочу отметить, что bit-fields довольно удобны, если надо запарсить или распарсить какой-нибудь int.