EDIT: изменил foo_t к Foo как TypeName, потому что POSIX оставляет типы оканчивающиеся в _t EDIT: изменил _foo_s к foo_s, поскольку C утверждает имена, начинающиеся с символа подчеркиванияПротиворечивые анонимную опережающее объявление в заголовке
Я нахожусь озадачен о том, что лучший способ иметь следующий одновременно:
- реализация библиотеки видит элементы этой структуры, но пользователи библиотеки сделать не
- компилятор проверяет, соответствует ли определение функции в заголовке реализации
- использование С99
Мой первый удар в это было сделать следующее:
foo.h (включение охранники для краткости опущены):
typedef struct foo_s foo;
struct foo_s;
foo* foo_create(void);
void foo_do_something(foo *foo);
foo.c:
#include "foo.h"
struct foo_s {
/* some_hidden_members */
};
foo* foo_create() {
/* allocate memory etc */
}
void foo_do_something(foo *foo) {
/* do something with foo */
}
Это похоже на работу с gcc. Каждый, кто включает в себя foo.h
, видит анонимную декларацию вперед, а реальный макет struct foo_s
известен только в foo.c
.
Я начал чувствовать запах чего-то странного выше, когда я пытался использовать include-what-you-use, который использует clang. Когда я использовал его для проверки foo.c
, он сообщил мне, что foo.h
не должен содержать прямого объявления struct foo_s
. Я думал, что это ошибка в iwyu, потому что, очевидно, это не проблема для кого-то еще, кто включает foo.h
.
В этот момент позвольте мне прийти ко второму требованию с самого начала. foo.c
содержит foo.h
, так что компилятор может убедиться, что каждая функция, объявленная в foo.h
, соответствует реализации в foo.c
. Я думаю, что мне это нужно, потому что я слишком часто сталкивался с сегментационными ошибками, потому что подпись функции моей реализации не соответствовала той, что была в заголовке, используемом другим кодом.
Позже я попытался скомпилировать код с лязгом (я компилирую с -Wall -Wextra -Werror
) и был проинформирован о том, что:
error: redefinition of typedef 'foo' is a C11 feature
Я не хочу, чтобы мой код зависит от функции C11, и я хочу быть уверен, что функции в общем заголовке соответствуют реализации. Как я могу это решить?
Я вижу путь, который заключается в разделении foo.h
в foo.h
и foo_private.h
:
foo.h (включение ограждения опущены для краткости):
struct foo_s;
#include "foo_private.h"
foo_private.h (включение ограждения опущены для краткости) :
typedef struct foo_s foo;
foo* foo_create(void);
void foo_do_something(foo *foo);
и тогда я включил бы foo_private.h
в foo.c
и других код будет включать foo.h
.Это означало бы, что foo.c
больше не видит переднего объявления foo_s
и, таким образом, clang и iwyu должны быть счастливы. Это также означает, что реализация моих функций проверяется на соответствие заголовку.
Но в то время как это работает, что заставляет меня задаться вопросом, является ли это лучшим решением, потому что:
- кажется тратой иметь один заголовочный файл с только две строки
- Я не знаю других проектов, сделайте это так (и посмотрев в моем/usr/include, я тоже не вижу)
Так что же было бы решением, которое соответствует трем критериям, перечисленным вверху? Или это решение, которое я нашел для него?
Там нет необходимости, чтобы объявить 'STRUCT _foo_s;' 'после того, как ЬурейиЕ структуры _foo_s foo_t; '. Только этот typedef объявляет * как * struct _foo_s', так и 'foo_t'. Следующая 'struct _foo_s;' ничего не добавляет к ней. Это избыточно. Возможно, именно это и пытался рассказать вам. – AnT
Код, который вы показываете, не вызывает двойного 'typedef'. У вас не должно возникнуть проблем, если у вас есть работа, включая защитников, защищающих содержимое вашего файла '.h'. –
OT: POSIX не разрешает типы, заканчивающиеся на '_t'. – alk