Структуры без ifdef в C или C

#c #c #struct

Вопрос:

Есть несколько проектов на языке Си со структурами, полными ifdef (например. Вольфссл https://github.com/wolfSSL/wolfssl/blob/bb70fee1ecff8945af8179f48e90d78ea7007c66/wolfssl/internal.h#L2792)

 struct {
int filed_1;
int field_2;
#ifdef SETTING_A
    int filed_b;
#endif
#ifdef SETTING_B
    int field_b;
#endif
}
 

Причина в том, чтобы уменьшить размер структуры для неиспользуемых опций.
Есть много ifdef! Повсюду!

Существует ли способ C избавиться от этих ifdef, сохранив возможность компилятора оптимизировать неиспользуемые поля? Может быть, с шаблонами, использованием или наследованием CRTP?

Комментарии:

1. Вы смотрите на проект C. В C не так много возможно в этом отношении. В C вы можете просто использовать (множественное) наследование.

2. С наследование использует таблицу виртуальных указателей и, таким образом, влияет на производительность. Также этот подход требует наличия 4 разных классов для 2 настроек. Что делать, если у меня 10 настроек?

3. наследование не подразумевает наличие таблицы v

4. Даже если бы вы смогли это сделать, всем пользователям кода все равно необходимо выполнить проверку во время компиляции, существуют ли field_a и field_b.

5. @XeenychXeenych Структура без виртуальных участников не имеет виртуальных накладных расходов

Ответ №1:

Вы можете сделать это на C 20 с [[no_unique_address]] помощью и некоторой хитрости. Однако это не гарантирует результат для небольших типов, поэтому я все равно предлагаю вам использовать #define s

 template<typename>
struct Empty {};

template<typename T, bool enable, typename uniquer>
using MaybeEmpty = std::conditional_t<enable, T, Empty<uniquer>>;

struct foo {
    int filed_1;
    int field_2;
    [[no_unique_address]] MaybeEmpty<int, settingA, struct filed_b_uniquer> filed_b;
    [[no_unique_address]] MaybeEmpty<int, settingB, struct field_b_uniquer> field_b;
};
 

До C 20 это должно было быть сделано с базовыми классами

 struct with_filed_b {
    int filed_b;
};

struct with_field_b {
    int field_b;
};

struct foo : MaybeEmpty<with_filed_b, settingA, struct filed_b_uniquer>, MaybeEmpty<with_field_b , settingB, struct field_b_uniquer>  {
    int filed_1;
    int field_2;        
};
 

Комментарии:

1. Выглядит неплохо. Как насчет более старого C ?

2. @XeenychXeenych до этого единственным способом, чтобы пустые объекты не занимали места, было то, что они были базовыми подобъектами

3. Не могли бы вы привести пример, пожалуйста?

4. @GuillaumeRacicot упс, исправлено