2014-09-19 4 views
5

EDIT: изменил foo_t к Foo как TypeName, потому что POSIX оставляет типы оканчивающиеся в _t EDIT: изменил _foo_s к foo_s, поскольку C утверждает имена, начинающиеся с символа подчеркиванияПротиворечивые анонимную опережающее объявление в заголовке

Я нахожусь озадачен о том, что лучший способ иметь следующий одновременно:

  1. реализация библиотеки видит элементы этой структуры, но пользователи библиотеки сделать не
  2. компилятор проверяет, соответствует ли определение функции в заголовке реализации
  3. использование С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, я тоже не вижу)

Так что же было бы решением, которое соответствует трем критериям, перечисленным вверху? Или это решение, которое я нашел для него?

+4

Там нет необходимости, чтобы объявить 'STRUCT _foo_s;' 'после того, как ЬурейиЕ структуры _foo_s foo_t; '. Только этот typedef объявляет * как * struct _foo_s', так и 'foo_t'. Следующая 'struct _foo_s;' ничего не добавляет к ней. Это избыточно. Возможно, именно это и пытался рассказать вам. – AnT

+1

Код, который вы показываете, не вызывает двойного 'typedef'. У вас не должно возникнуть проблем, если у вас есть работа, включая защитников, защищающих содержимое вашего файла '.h'. –

+1

OT: POSIX не разрешает типы, заканчивающиеся на '_t'. – alk

ответ

4

Престижность на благородном намерении сокрытия данных!

Как насчет следующего?

foo.h (включение охранники для краткости опущены):

typedef struct foo_t foo_t; // note change 0 

// note change 1 

foo_t* foo_create(void); 
void foo_do_something(foo_t *foo); 

foo.c:

#include "foo.h" 

struct foo_t {    // note change 2 
    /* some_hidden_members */ 
}; 

foo_t* foo_create() { 
    /* allocate memory etc */ 
} 

void foo_do_something(foo_t *foo) { 
    /* do something with foo */ 
} 
+1

1+, но я бы избавился от 'typedef'. Это бесполезно. Замените 'typedef struct foo_t foo_t;' на 'struct foo;' и просто придерживайтесь 'struct foo'. – alk

+1

@alk: Спасибо! OP был уже совсем близко, я хотел сделать минимальное изменение ... Я думаю, OP хочет, чтобы клиенты использовали только 'foo_t', а не' struct foo_t'; поэтому я оставил его как есть. Я тоже понимаю ограничение POSIX, но это другая тема. – Arun

+0

Вы правы, я переместил свой комментарий на '_t' в OP. – alk

Смежные вопросы