Hi уважаемый All.
Вопрос по работе с битовыми флагами. Понимаю, что пытаюсь экономить на спичках, но все же. Как говорится мелочь, а неприятно.
Итак, есть два набора битовых флагов (маска и значение), хочется инкапсулировать работу с такой структурой посредством inline функций.
class Opts
{
public:
// Должно быть protected, public стоит для эксперимента (сравнения сгенерированного ассемблерного кода).
union OptsFlags
{
WORD Flags;
struct
{
bool bShowPointNames:1;
bool bShowPoints:1;
bool bShowPointIcons:1;
};
};
OptsFlags m_Mask;
OptsFlags m_Values;
public:
// И делаем такие функции для доступа к переменным с масками.
inline bool GetDrawPoints() const
{
if(m_Mask.bShowPoints) {return m_Values.bShowPointIcons;}
else return false;
}
};
Пытаемся это дело использовать:
Opts O;
if(O.GetDrawPoints()) {return TRUE;}
Смотрим, что сгенерировал компилятор:
test BYTE PTR _O$[esp+756], 2
je SHORT $LN3@InitInstan
mov al, BYTE PTR _O$[esp+758]
shr al, 2
and al, 1
je SHORT $LN3@InitInstan
Вот зачем он генерирует выделенные строки?
Ладно бы он был просто тупой и в принципе не умел работать с битовыми полями. Но так может же сделать нормально, если захочет. Проверяем. Переносим тело функции непосредственно в место вызова (для этого и нужна метка public для членов данных в классе Opts, иначе следующий фрагмент не скомпилируется)
Opts O;
if (O.m_Mask.bShowPoints && O.m_Values.bShowPointIcons) {return TRUE;}
Смотрим, что получилось:
test BYTE PTR _O$[esp+756], 2
je SHORT $LN3@InitInstan
test BYTE PTR _O$[esp+758], 4
je SHORT $LN3@InitInstan
Ну? Совсем другое дело! Может же зараза, когда хочет
Но позвольте, ведь при использовании inline функций результаты должны быть эквивалентны! Почему же здесь результаты разные? Ну и собственно вопрос: как заставить компилятор генерировать оптимальный код при использовании функций? Все же хочется писать понятный код, который проще сопровождать (поменять одну функцию проще, чем выискивать во всей программе места, где используется этот кусок кода).
Ухищрения типа
inline bool GetDrawPoints() const
{
if(m_Mask.bShowPoints) {return m_Values.bShowPointIcons != 0;}
else return false;
}
или даже
inline bool GetDrawPoints() const
{
return m_Mask.bShowPoints ? m_Values.bShowPointIcons: false;
}
к желаемому результату не приводят.
Пока пришел к компромиссному варианту, когда вместо m_Values используются реальные bool переменные. Тогда код генерируется нормальный:
test BYTE PTR _O$[esp+756], 2
je SHORT $LN3@InitInstan
cmp BYTE PTR _O$[esp+760], 0
je SHORT $LN3@InitInstan
Но все же хочется чтобы и вместо m_Values можно было использовать битовые поля без потери эффективности (и не только ради экономии места, а еще и ради того, чтобы не нужно было дублировать объявление флагов (один раз для битовых полей, второй раз для реальных bool переменных)). Что скажет сообщество? Или дело в каких-то ограничениях стандарта, который где-то говорит что-то типа того, что если стоит return, то компилятор просто обязан преобразовать возвращаемое значение к "каноническому" виду булевской переменной? В общем, хотелось бы разобраться.