Включает в заголовочные файлы - PullRequest
2 голосов
/ 06 июля 2011

Должны ли заголовочные файлы иметь #include s?

Я вообще придерживаюсь мнения, что такого рода иерархические включения плохие. Скажем, у вас есть это:

foo.h:

#include <stdio.h> // we use something from this library here
struct foo { ... } foo;

main.c

#include "foo.h"
/* use foo for something */
printf(...)

День реализации main.c изменится, и вы больше не будете использовать foo.h, компиляция прервется, и вы должны вручную добавить <stdio.h>.

По сравнению с этим:

foo.h

// Warning! we depend on stdio.h
struct foo {...

main.c

#include <stdio.h> //required for foo.h, also for other stuff
#include "foo.h"

И когда вы перестанете использовать foo, удаление его ничего не нарушит, но удаление stdio.h приведет к поломке foo.h.

Следует ли запретить #includes в файлах .h?

Ответы [ 6 ]

4 голосов
/ 06 июля 2011

Вы изложили две основные философии на эту тему.

Мое собственное мнение (и я думаю, что это все, что можно на самом деле иметь в этом) заключается в том, что заголовки должны быть максимально автономными. Я не хочу знать все зависимости foo.h, чтобы иметь возможность использовать этот заголовок. Я также презираю необходимость включать заголовки в определенном порядке.

Однако разработчик foo.h также должен взять на себя ответственность за то, чтобы сделать его как можно более независимым. Например, заголовок foo.h должен быть написан так, чтобы он не зависел от stdio.h, если это вообще возможно (использование форвардных объявлений может помочь в этом).

Обратите внимание, что стандарт C запрещает стандартному заголовку включать другой стандартный заголовок, но стандарт C ++ этого не делает. Таким образом, вы можете увидеть проблему, которую вы описываете при переходе с одной версии компилятора C ++ на другую. Например, в MSVC, включая <vector>, используемый для ввода <iterator>, но это больше не встречается в MSVC 2010, поэтому код, скомпилированный ранее, может больше не работать, поскольку вам может понадобиться специально включить <iterator>.

Однако, хотя может показаться, что стандарт C поддерживает вторую философию, имейте в виду, что он также требует, чтобы ни один заголовок не зависел от другого, и что вы можете включать заголовки в любом порядке. Таким образом, вы получаете лучшее из обоих миров, но ценой сложности для разработчиков библиотеки C. Для этого им нужно прыгнуть через несколько обручей (особенно для поддержки определений, которые могут быть введены через любой из нескольких заголовков, например NULL или size_t). Я думаю, что люди, которые разработали стандарт C ++, решили добавить, что сложность для имитаторов больше не является разумной (я не знаю, в какой степени разработчики библиотек C ++ используют преимущества «лазейки» - похоже, что MS может ужесточить это, даже если это технически не требуется).

3 голосов
/ 06 июля 2011

Мои общие рекомендации:

  • Файл должен #include, что ему нужно.
  • Не следует ожидать, что что-то еще #include будет нужно.
  • Это не должно #include то, что ему не нужно, потому что это может захотеть что-то другое.

Настоящий тест заключается в следующем: вы должны быть в состоянии скомпилировать исходный файл, состоящий из одного #include, и не получать ошибок или предупреждений, кроме "Нет main()". Если вы пройдете этот тест, вы можете ожидать, что все остальное сможет без проблем #include ваш файл. Я написал короткий скрипт под названием "hcheck", который я использую для проверки этого:

#!/usr/bin/env bash
# hcheck: Check header file syntax (works on source files, too...)
if [ $# -eq 0 ]; then
    echo "Usage: $0 <filename>"
    exit 1
fi

for f in "$@" ; do
    case $f in
        *.c | *.cpp | *.cc | *.h | *.hh | *.hpp )
            echo "#include \"$f\"" > hcheck.cc
            printf "\n\033[4mChecking $f\033[0m\n"
            make -s $hcheck.o
            rm -f hcheck.o hcheck.cc
            ;;
    esac
done

Я уверен, что есть несколько вещей, которые этот скрипт мог бы сделать лучше, но это должно быть хорошей отправной точкой.

Если это слишком много, и если ваши заголовочные файлы почти всегда имеют соответствующие исходные файлы, то другой способ заключается в том, чтобы соответствующий заголовок был первым #include в исходном файле. Например:

foo.h:

#ifndef Foo_h
#define Foo_h

/* #includes that Foo.h needs go here. */

/* Other header declarations here */

#endif

Foo.c:

#include "Foo.h"
/* other #includes that Foo.c needs go here. */

/* source code here */

Здесь также показаны «включенные охранники» в Foo.h, о которых упоминали другие.

Если поставить #include "Foo.h" первым, Foo.h должен #include его зависимости, в противном случае вы получите ошибку компиляции.

2 голосов
/ 06 июля 2011

Ну, главное не должно полагаться на "foo.h", во-первых, на stdio.Нет ничего плохого в том, чтобы включить что-то дважды.Кроме того, возможно, foo. h на самом деле не нужно stdio.Что более вероятно, так это то, что foo.c (реализация) нуждается в stdio.

Короче говоря, я думаю, что все должны просто включать все, что им нужно, и полагаться на , включая охранников .

1 голос
/ 06 июля 2011

Как только вы попадаете в проекты с сотнями или тысячами заголовочных файлов, это становится несостоятельным. Скажем, у меня есть файл заголовка "MyCoolFunction.h", который содержит прототип для MyCoolFunction(), и эта функция принимает указатели на структуры в качестве параметров. Я должен быть в состоянии предположить, что включение MyCoolFunction.h будет включать в себя все, что необходимо, и позволит мне использовать эту функцию, не заглядывая в файл .h, чтобы увидеть, что еще мне нужно включить.

0 голосов
/ 06 июля 2011

Почему бы вам #include вещи в * .c файле, соответствующем заголовку?

0 голосов
/ 06 июля 2011

Если заголовочный файл требует определенного заголовка, добавьте его в заголовочный файл

#ifndef HEADER_GUARD_YOUR_STYLE
#define HEADER_GUARD_YOUR_STYLE

#include <stdio.h> /* FILE */
int foo(FILE *);

#endif /* HEADER GUARD */

, если кодовый файл не нуждается в заголовке, не добавляйте его

/* #include <stdio.h> */ /* removed because unneeded */
#include <stddef.h> /* NULL */
#include "header.h"
int main(void) {
  foo(NULL);
  return 0;
}
Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...