Как мой код может сказать константу времени компиляции в сравнении с переменной? - PullRequest
5 голосов
/ 04 августа 2011

Вот моя проблема. У меня есть BINARY_FLAG макрос:

#define BINARY_FLAG( n ) ( static_cast<DWORD>( 1 << ( n ) ) )

Который может использоваться как этот (сценарий "константы"):

static const SomeConstant = BINARY_FLAG( 5 );

или как это (сценарий «переменной»):

for( int i = 0; i < 10; i++ ) {
    DWORD flag = BINARY_FLAG( i );
    // do something with the value
}

Этот макрос вообще не защищен от дурака - туда можно передать -1 или 34, и самое большее будет предупреждение, но поведение будет неопределенным. Я бы хотел сделать его более надежным.

Для постоянного сценария я мог бы использовать шаблон:

template<int Shift> class BinaryFlag {
staticAssert( 0 <= Shift && Shift < sizeof( DWORD) * CHAR_BIT );
public:
static const DWORD FlagValue = static_cast<DWORD>( 1 << Shift );
};
#define BINARY_FLAG( n ) CBinaryFlag<n>::FlagValue

но это не относится к «переменному» сценарию - мне нужно утверждение там во время выполнения:

inline DWORD ProduceBinaryFlag( int shift )
{
    assert( 0 <= shift && shift < sizeof( DWORD) * CHAR_BIT );
    return static_cast<DWORD>( 1 << shift );
}
#define BINARY_FLAG( n ) ProduceBinaryFlag(n)

Последнее хорошо, но не проверяет время компиляции. Конечно, я хотел бы проверить во время компиляции, где это возможно, и проверку во время выполнения в противном случае. Я всегда хочу как можно меньше накладных расходов во время выполнения, поэтому я не хочу вызова функции (которая, возможно, не будет встроена), когда возможна проверка во время компиляции.

Я видел этот вопрос , но это не похоже на ту же проблему.

Существует ли какая-либо конструкция, которая позволяла бы чередовать их в зависимости от того, является ли выражение, переданное в виде числа флага, константой времени компиляции или переменной?

Ответы [ 3 ]

2 голосов
/ 04 августа 2011

Это проще, чем вы думаете:)

Давайте посмотрим:

#include <cassert>

static inline int FLAG(int n) {
    assert(n>=0 && n<32);
    return 1<<n;
}

int test1(int n) {
    return FLAG(n);
}
int test2() {
    return FLAG(5);
}

Я не использую MSVC, но я скомпилировал с Mingw GCC 4.5:

g ++ -c -S -O3 08042.cpp

Результирующий код для первого метода выглядит так:

__Z5test1i:
    pushl   %ebp
    movl    %esp, %ebp
    subl    $24, %esp
    movl    8(%ebp), %ecx
    cmpl    $31, %ecx
    ja  L4
    movl    $1, %eax
    sall    %cl, %eax
    leave
    ret
L4:
    movl    $4, 8(%esp)
    movl    $LC0, 4(%esp)
    movl    $LC1, (%esp)
    call    __assert
    .p2align 2,,3

И второй:

__Z5test2v:
    pushl   %ebp
    movl    %esp, %ebp
    movl    $32, %eax
    leave
    ret

См? Компилятор достаточно умен, чтобы сделать это за вас. Нет необходимости в макросах, нет необходимости в метапрограммировании, нет необходимости в C ++ 0x. Так просто.

Проверьте, выполняет ли MSVC то же самое ... Но посмотрите - компилятору действительно легко вычислить константное выражение и отбросить неиспользуемую условную ветвь. Проверьте это, если хотите быть уверенным ... Но в целом - доверяйте своим инструментам.

1 голос
/ 04 августа 2011

невозможно передать аргумент макросу или функции и определить, является ли она постоянной времени компиляции или переменной.

Лучший способ - это #define BINARY_FLAG(n) с кодом времени компиляции, разместить этот макрос повсюду и затем скомпилировать его. Вы получите ошибки компилятора в местах, где n будет во время выполнения. Теперь вы можете заменить эти макросы своим макросом времени выполнения BINARY_FLAG_RUNTIME(n). Это единственный возможный способ.

1 голос
/ 04 августа 2011

Я предлагаю вам использовать два макроса. BINARY_FLAG CONST_BINARY_FLAG Это облегчит понимание вашего кода для других. Вы знаете, на момент написания статьи, является ли это const или нет. И я бы ни за что не беспокоился о времени выполнения. Ваш оптимизатор, по крайней мере в VS, разберутся за вас.

...