C sizeof эквивалент для макросов - PullRequest
5 голосов
/ 21 августа 2009

Итак, я оптимизирую некоторый код, развернув несколько циклов (да, я знаю, что должен сделать это для себя, полагаясь на свой компилятор, но я не работаю с выбором компиляторов), и я хотел это сделать несколько изящно, так что в случае, если мой размер данных изменится из-за некоторых изменений в будущем, код будет элегантно ухудшаться.

Что-то вроде:

typedef struct {
    uint32_t alpha;
    uint32_t two;
    uint32_t iii;
} Entry;

/*...*/

uint8_t * bytes = (uint8_t *) entry;
#define PROCESS_ENTRY(i) bytes[i] ^= 1; /*...etc, etc, */ 
#if (sizeof(Entry) == 12)
    PROCESS_ENTRY( 0);PROCESS_ENTRY( 1);PROCESS_ENTRY( 2);
    PROCESS_ENTRY( 3);PROCESS_ENTRY( 4);PROCESS_ENTRY( 5);
    PROCESS_ENTRY( 6);PROCESS_ENTRY( 7);PROCESS_ENTRY( 8);
    PROCESS_ENTRY( 9);PROCESS_ENTRY(10);PROCESS_ENTRY(11);
#else
#   warning Using non-optimized code
    size_t i;
    for (i = 0; i < sizeof(Entry); i++)
    {
        PROCESS_ENTRY(i);
    }
#endif
#undef PROCESS_ENTRY

Конечно, это не работает, так как sizeof недоступен препроцессору (по крайней мере, это то, что этот ответ , казалось, указывает).

Есть ли простой обходной путь, который я могу использовать, чтобы получить sizeof структуру данных для использования с макросом C, или я просто SOL?

Ответы [ 6 ]

17 голосов
/ 21 августа 2009

Вы не можете сделать это в препроцессоре, но вам это не нужно. Просто сгенерируйте простой if в вашем макросе:

#define PROCESS_ENTRY(i) bytes[i] ^= 1; /*...etc, etc, */ 
if (sizeof(Entry) == 12) {
    PROCESS_ENTRY( 0);PROCESS_ENTRY( 1);PROCESS_ENTRY( 2);
    PROCESS_ENTRY( 3);PROCESS_ENTRY( 4);PROCESS_ENTRY( 5);
    PROCESS_ENTRY( 6);PROCESS_ENTRY( 7);PROCESS_ENTRY( 8);
    PROCESS_ENTRY( 9);PROCESS_ENTRY(10);PROCESS_ENTRY(11);
} else {
    size_t i;
    for (i = 0; i < sizeof(Entry); i++) {
        PROCESS_ENTRY(i);
    }
}

sizeof является константным выражением, и сравнение константы с константой также является константой. Любой здравомыслящий компилятор C оптимизирует ветвь, которая всегда ложна во время компиляции - постоянное свертывание является одной из самых основных оптимизаций. Вы теряете #warning, однако.

9 голосов
/ 21 августа 2009

Если вы используете autoconf или другую систему конфигурации сборки, вы можете проверить размер структур данных во время конфигурации и записать заголовки (например, #define SIZEOF_Entry 12). Конечно, это становится более сложным при кросс-компиляции и тому подобное, но я предполагаю, что ваша сборка и целевые архитектуры одинаковы.

В противном случае да, вам не повезло.

5 голосов
/ 21 августа 2009

Вам не повезло - препроцессор даже не знает, что такое структура, не говоря уже о каком-либо способе определить ее размер.

В таком случае вы можете просто #define константа, равный тому, что вы знаете, размер структуры, а затем статически утверждать, что он на самом деле равен размеру, используя трюк массива отрицательного размера.

Также вы можете попробовать просто выполнить if (sizeof(Entry) == 12) и посмотреть, способен ли ваш компилятор оценить состояние ветвления во время компиляции и удалить мертвый код. Это не такая большая просьба.

1 голос
/ 21 августа 2009

На ум приходят два других подхода - либо написать небольшое приложение для написания развернутого цикла, либо использовать вариант устройства Даффа с ожидаемым размером структуры.

1 голос
/ 21 августа 2009

Это, вероятно, не поможет, но если у вас есть возможность сделать это в C ++, вы можете использовать шаблон, чтобы компилятор отправлял в соответствующий цикл во время компиляции:

template <std::size_t SizeOfEntry>
void process_entry_loop(...)
{
    // ... the nonoptimized version of the loop
}

template <>
void process_entry_loop<12>(...)
{
    // ... the optimized version of the loop
}

// ...

process_entry_loop<sizeof(Entry)>(...);
0 голосов
/ 21 августа 2009

Если вам нужен наименьший возможный размер для структуры (или для выравнивания его по 4-байтовой границе или как угодно), вы можете использовать упакованные или выровненные атрибуты.

В Visual C ++ вы можете использовать # pragma pack , а в GCC вы можете использовать __attribute __ ((упакованный)) и __attribute __ ((выровненный (num-bytes)).

...