Как можно привести void* к общему типу структуры, имя которого отличается в зависимости от TYPE переданного аргумента?

Я пытаюсь смоделировать поведение «шаблона» в C как часть упражнения по рефакторингу кода с cpp на C. Я разработал MACRO-подход, определив как struct вот так:

#define FIFO(TYPE, depth, NAME)                                     \
     typedef struct fifo_##TYPE##NAME                               \
     {                                                              \
        TYPE array[depth];                                          \
        uint8 st_idx;                                               \
        uint8 end_idx;                                              \
        uint32 cnt;                                               \
     }fifo_##TYPE##NAME;                                            \
     void fifo_##TYPE##NAME_init(fifo_##TYPE##NAME *f);             \
     int fifo_##TYPE##NAME_push(fifo_##TYPE##NAME *f, TYPE *elem);  \
     TYPE* fifo_##TYPE##NAME_pop(fifo_##TYPE##NAME *f);             \
     TYPE* fifo_##TYPE##NAME_peek(fifo_##TYPE##NAME *f);            \

Затем я определил функции MACRO для прототипов, показанных выше. Это фактически увеличило размер моего кода по сравнению с шаблонным подходом, поскольку каждая из функций определялась для каждого ТИПА.

Теперь я пытаюсь использовать void* подход вместо MACRO. Я все еще использую определение структуры MACRO, как указано выше, но я не определяю функции MACRO. Я использую обычные встроенные функции в файле .h, но я застрял на том, как получить доступ к членам общей структуры TYPE через void* ptr. К какому типу я могу привести его? Если имя структуры изменяется в соответствии с TYPE, у меня нет определенного типа структуры для его приведения. Кроме того, макросы не будут работать в функциях, не являющихся макросами.

Извините за многословие! Спасибо.


person thegeeklife    schedule 13.03.2015    source источник
comment
Я думаю, что это не большая проблема, что размер кода увеличивается. У вас будет безопасность типов, которой у вас не будет, используя ваш подход void*.   -  person wimh    schedule 13.03.2015
comment
void* может быть приведен к любому другому типу указателя. Таким образом, ваши универсальные функции должны работать с указателями, а не со структурами.   -  person Eugene Sh.    schedule 13.03.2015
comment
@Wimmel Спасибо за ответ. Не могли бы вы уточнить это, если не возражаете? Помог бы мне получить более четкое представление. Спасибо!   -  person thegeeklife    schedule 13.03.2015
comment
Память @thegeeklife стоит дешево. хранение дешевое. исправлять ошибки дорого. Если вы не разрабатываете встроенную среду с ограниченными ресурсами, я думаю, вам не следует сильно заботиться о размере кода. Читаемость вашего кода гораздо важнее, поэтому вам и другим будет легче поддерживать его. Обратите внимание, что мне также не очень нравится большой #define, я бы предпочел решить это с помощью генерации кода. Сгенерированный код легче отлаживать.   -  person wimh    schedule 13.03.2015
comment
Я бы обращался к членам этой структуры в общих функциях, поэтому, думаю, мне нужно было бы привести ее к этому конкретному типу структуры, верно? Кроме того, вызов этих функций будет передавать адрес структуры, которая будет собрана в void* ..   -  person thegeeklife    schedule 13.03.2015
comment
@Wimmel Я согласен с вами насчет уродства большого макроса, хотя это то, что я мог бы придумать в C. И да, я оптимизирую ограничения по размеру кода. Если ничего другого, я бы, вероятно, вернулся к cpp   -  person thegeeklife    schedule 14.03.2015
comment
Рассмотрите возможность размещения массива в последнюю очередь. См. 6.5.2.3 Члены структуры и объединения: Для упрощения использования объединений делается одна специальная гарантия: если объединение содержит несколько структур, имеющих общую начальную последовательность (см. ниже), и если объект объединения в настоящее время содержит одной из этих структур, разрешается проверять общую начальную часть любой из них в любом месте, где видно объявление завершенного типа объединения. Две структуры имеют общую начальную последовательность, если соответствующие элементы имеют совместимые типы (...) для последовательности из одного или нескольких начальных элементов.   -  person Jonathan Leffler    schedule 14.03.2015
comment
Можете ли вы привести пример кода того, что вы пытаетесь сделать? (Т.е. какой-то код, который не работает, но вы хотите работать)   -  person user253751    schedule 14.03.2015
comment
Зачем вам NAME? Если вы собираетесь использовать шаблоны C++, то вы должны знать, что FIFO<int, 10> будет похоже на FIFO(int, 10). Какая польза от NAME при определении такого типа (отличается ли FIFO_intX с глубиной 10 по структуре от FIFO_intY с глубиной 10?) Имеет ли Общие контейнеры с макросами помогают вообще?   -  person    schedule 14.03.2015
comment
Кроме того, можно ли изменить размер array? Если это так, то мне вообще не имеет смысла иметь параметр depth, когда вы можете просто выделить, используя размер по умолчанию, аналогичный конструктору по умолчанию в C++, и вы можете предоставить другую функцию для выделения определенной суммы для depth как с конструктором, принимающим аргумент depth. Я извиняюсь за все вопросы, но чем больше я смотрю на это, тем больше кажется, что он пытается достичь большего, чем просто шаблоны C++. Думаю, я не понимаю причины особого дизайна макроса.   -  person    schedule 14.03.2015
comment
Массив @Chrono Kitsune Yes можно изменять в зависимости от типа. Я ссылался на ссылку, которую вы предоставили. Спасибо! Но моя основная проблема здесь по-прежнему заключается в доступе к членам структуры через функции, не являющиеся макросами.   -  person thegeeklife    schedule 14.03.2015
comment
Попытка сделать это: inline void init(void* f) { f->count=0; //f needs to be type cast into the struct type f->end_idx =0; f->st_idx =0; } Функция вызова выглядит так: init(&fifo1);   -  person thegeeklife    schedule 14.03.2015
comment
Я думаю, вам нужно привести к общему типу и использовать общие функции, а не наоборот. Я только что закончил это реализовывать. Это некрасиво, но, кажется, работает (во всяком случае, я пока не нашел никаких ошибок). Вы можете просмотреть все это в один исходный файл. Справедливое предупреждение: это долго. Надеюсь, вы разберетесь с арифметикой указателей на уровне байтов. int16_t *p = (void*)0; p += 2; и unsigned char *p = (void *)0; p += 2 * sizeof(int16_t); оба приводят к тому, что p==(void*)8 является истинным. Я просто использую его много, и все.   -  person    schedule 14.03.2015