макрос вычисления маски битового поля

Здесь у нас есть набор макросов C для использования препроцессора для выполнения операций с битовыми полями, и мы сталкиваемся с предупреждениями при попытке использовать эти макросы в Visual Studio. Проблема может быть продемонстрирована очень легко:

#define BITFIELD_WIDTHMASK(Width) \
    ((Width) >= 32 ? ~0x0ul : (1ul << (Width)) - 1)

unsigned long foo(void)
{
    return BITFIELD_WIDTHMASK(32);
}

Компиляция этого с помощью MSVC дает предупреждение:

test.c(12) : warning C4293: '<<' : shift count negative or too big, undefined behavior

Это не проблема поведения — в этом случае оператор << использоваться не будет, и это должно быть обнаружено во время компиляции. Но есть ли у кого-нибудь предложения о том, как переписать макрос, чтобы избежать предупреждения? Или, в противном случае, как переделать интерфейс макроса для этого?

заранее спасибо


person Aidan Cully    schedule 04.05.2010    source источник


Ответы (3)


Как насчет:

#define BITFIELD_WIDTHMASK(Width) \
    ((Width) >= 32 ? ~0x0ul : (1ul << (Width % 32)) - 1)

?

person caf    schedule 04.05.2010
comment
Работает, чисто и плотно. Именно то, что я был после. Спасибо! - person Aidan Cully; 05.05.2010
comment
Это так же эффективно, когда ширина является константой. Однако, если это переменная, она может иметь более низкую эффективность во время выполнения, чем сдвиг, если это имеет значение. Все еще; покрывает всю основу. +1. - person Clifford; 05.05.2010

#define BITFIELD_WIDTHMASK(Width) (((0x80000000ul >> (32-Width)) << 1) - 1)

Или обрабатывать ширину 0, а также запрошенную...

#define BITFIELD_WIDTHMASK(Width) \
  ((Width) >= 32 \
  ? ~0x0ul \
  : (((1ul << ((Width)/2)) << ((Width)/2)) << ((Width)&1)) - 1)
person Doug Currie    schedule 04.05.2010
comment
Он решает указанную проблему, но имеет аналогичную проблему, когда аргумент Width равен 0. - person Aidan Cully; 05.05.2010
comment
#define BITFIELD_WIDTHMASK(Ширина) ((Ширина == 0) ? 0 : (Ширина == 1) ? 1 : (Ширина == 2) ? 3 : (Ширина == 3) ? 7 : (Ширина == 4) ? 15 : ... ;-) - person Doug Currie; 05.05.2010
comment
@Aidan Cully: Какой результат вы хотели получить для BITFIELD_WIDTHMASK(0)? потому что это приводит к 0xffffff (то есть так же, как BITFIELD_WIDTHMASK(32)). Ваш дал ноль. Но, возможно, битовая ширина нуля не имеет смысла. - person Clifford; 05.05.2010
comment
@Clifford: Нет, первый макрос в этом ответе дает неопределенное поведение для ширины 0, поскольку он пытался сдвинуться на 32. - person caf; 05.05.2010
comment
@Clifford - Цель состоит в том, чтобы BITFIELD_WIDTHMASK(x) возвращал число с установленными младшими x битами, а остальные очищались. Таким образом, аргумент 0 должен привести к 0, а аргумент 32 должен привести к 0xffffffff. Поле нулевой ширины служит для попыток записи/чтения этого поля без операций, что может быть полезным для документации или в целях совместимости. - person Aidan Cully; 05.05.2010

Препроцессор, а не компилятор, оценивает выражение, когда Width является буквальной константой, и слишком глупо не оценивать обе стороны выражения ?:. Я думаю, потому что он вообще не обрабатывает его, а вставляет выражение ?: с постоянными операндами!

Если ширина нуля не требуется, следующее упрощение работает для значений от 1 до 32:

#define BITFIELD_WIDTHMASK(Width) (~0ul >> (32-(Width)))

Мне кажется, что если вы знаете, что ширина равна нулю (возможно, чтобы отключить функцию), что неявно, если вы используете буквальную нулевую константу, было бы разумно просто использовать ноль, а не вызывать макрос.

person Clifford    schedule 04.05.2010
comment
Похоже, что это так же может вызвать ложное предупреждение, как и определение OP, просто для другого набора значений ширины (код OP был правильным C, просто по какой-то причине компилятор не мог видеть, что сдвиг никогда не будет оцениваться когда ширина была слишком большой). - person caf; 05.05.2010
comment
Вы заметите, что у моего исходного решения было такое ограничение, но это не помешало компилятору предупредить о недопустимом сдвиге. Я только что попробовал эту версию макроса с упомянутым компилятором, и он выдает такое же предупреждение. - person Aidan Cully; 05.05.2010
comment
@Aidan Cully: Да, я только что понял это, ошибка теста с моей стороны: проблема в том, что пре=процессор все еще оценивает оба выражения правого операнда в ?: даже когда ширина является константой. Я изменил сообщение (что может сделать действительные комментарии устаревшими, но просто игнорирует проблему!) - person Clifford; 05.05.2010