Я должен использовать #include в заголовках?

Я использую MSBuild полностью для создания. Вот мой универсальный сценарий MSBuild, который ищет дерево .csproj файлы и создает их:

<Project xmlns="http://schemas.microsoft.com/developer/msbuild/2003" DefaultTargets="Build">
  <UsingTask AssemblyFile="$(MSBuildProjectDirectory)\bin\xUnit\xunitext.runner.msbuild.dll" TaskName="XunitExt.Runner.MSBuild.xunit"/>
  <PropertyGroup>
    <Configuration Condition="'$(Configuration)'==''">Debug</Configuration>
    <DeployDir>$(MSBuildProjectDirectory)\Build\$(Configuration)</DeployDir>
    <ProjectMask>$(MSBuildProjectDirectory)\**\*.csproj</ProjectMask>
    <ProjectExcludeMask></ProjectExcludeMask>
    <TestAssembliesIncludeMask>$(DeployDir)\*.Test.dll</TestAssembliesIncludeMask>
  </PropertyGroup>

  <ItemGroup>
    <ProjectFiles Include="$(ProjectMask)" Exclude="$(ProjectExcludeMask)"/>
  </ItemGroup>

  <Target Name="Build" DependsOnTargets="__Compile;__Deploy;__Test"/>

  <Target Name="Clean">
    <MSBuild Projects="@(ProjectFiles)" Targets="Clean"/>
    <RemoveDir Directories="$(DeployDir)"/>
  </Target>

  <Target Name="Rebuild" DependsOnTargets="Clean;Build"/>

  <!--
  ===== Targets that are meant for use only by MSBuild =====
  -->
  <Target Name="__Compile">
    <MSBuild Projects="@(ProjectFiles)" Targets="Build">
      <Output TaskParameter="TargetOutputs" ItemName="AssembliesBuilt"/>
    </MSBuild>
    <CreateItem Include="@(AssembliesBuilt -> '%(RootDir)%(Directory)*')">
      <Output TaskParameter="Include" ItemName="DeployFiles"/>
    </CreateItem>
  </Target>

  <Target Name="__Deploy">
    <MakeDir Directories="$(DeployDir)"/>
    <Copy SourceFiles="@(DeployFiles)" DestinationFolder="$(DeployDir)"/>
    <CreateItem Include="$(TestAssembliesIncludeMask)">
      <Output TaskParameter="Include" ItemName="TestAssemblies"/>
    </CreateItem>
  </Target>

  <Target Name="__Test">
    <xunit Assembly="@(TestAssemblies)"/>
  </Target>
</Project>

(Извините, если это немного плотно. Скидка с цены, кажется, снимает пустые строки.)

Это довольно просто, хотя, после того как Вы понимаете, понятия и все зависимости обрабатываются автоматически. Я должен отметить, что мы используем файлы проекта Visual Studio, которые имеют большую логику, встроенную в них, но эта система позволяет людям создавать почти тождественно и в рамках Visual Studio IDE или в командной строке и все еще дает Вам гибкость добавляющих вещей к канонической сборке как xUnit тестирование, Вы видите в сценарии выше.

Тот, который - PropertyGroup то, где вся конфигурация происходит и вещи, может быть настроен, как исключение определенных проектов от сборки или добавления новых масок опытной сборки.

ItemGroup - то, где логика происходит, который находит все .csproj файлы в дереве.

Затем существуют цели, которые большинство людей, знакомых с, делает, nAnt, или MSBuild должен смочь следовать. При вызове цели Сборки она звонит __, Компиляция, __ Развертываются и __ Тест. Чистая цель называет MSBuild на всех файлах проекта для них для чистки их каталогов, и затем каталог глобального развертывания удален. Восстановите Чистые вызовы и затем Сборка.

70
задан Jonathan Leffler 10 January 2015 в 04:02
поделиться

9 ответов

Хорошая практика - помещать #includes во включаемый файл только в том случае, если они нужны для включаемого файла. Если определения в данном включаемом файле используются только в файле .c, тогда включайте его только в файл .c.

В вашем случае я бы включил его во включаемый файл между # ifdef / # endif.

Это минимизирует зависимости, так что файлы, которым не требуется данное включение, не нужно будет перекомпилировать при изменении включаемого файла.

20
ответ дан 24 November 2019 в 13:27
поделиться

Обычно разработчики библиотек защищают свои включения от многократного включения с помощью "уловки" #ifndef / # define / #endif, чтобы вам не приходилось этого делать.

Конечно, вы должен проверить ... но в любом случае компилятор скажет вам в какой-то момент ;-) В любом случае рекомендуется проверять наличие множественных включений, поскольку это замедляет цикл компиляции.

0
ответ дан 24 November 2019 в 13:27
поделиться

Во время компиляции препроцессор просто заменяет директиву #include указанным содержимым файла. Чтобы предотвратить бесконечный цикл, он должен использовать

#ifndef SOMEIDENTIFIER
#define SOMEIDENTIFIER
....header file body........
#endif

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

0
ответ дан 24 November 2019 в 13:27
поделиться

Да, это необходимо, иначе компилятор будет жаловаться, когда попытается скомпилировать код, о котором он «не знает». Подумайте о том, что # include - это подсказка / подтолкновение / локоть для компилятора, чтобы он сказал, чтобы он подбирал объявления, структуры и т. Д. Для успешной компиляции. Уловка заголовка # ifdef / # endif, указанная jldupont, предназначена для ускорения компиляции кода.

Он используется в случаях, когда у вас есть компилятор C ++ и компилирует простой код C, как показано здесь Вот пример уловки:

#ifndef __MY_HEADER_H__
#define __MY_HEADER_H__

#ifdef __cplusplus
extern "C" {
#endif


/* C code here such as structures, declarations etc. */

#ifdef __cplusplus
}
#endif

#endif /* __MY_HEADER_H__ */

Теперь, если это было включено несколько раз, компилятор включит его только один раз, поскольку символ __ MY_HEADER_H __ определен один раз, что ускоряет время компиляции. Обратите внимание на символ cplusplus в приведенном выше примере, это нормальный стандартный способ справиться с компиляцией C ++, если у вас есть код C.

Я включил приведенное выше, чтобы показать это (несмотря на то, что на самом деле не имеет отношения к оригинальный вопрос постера). Надеюсь это поможет, С наилучшими пожеланиями, Том.

PS: Прошу прощения за то, что позволил кому-либо проголосовать против этого, поскольку я думал, что это будет полезным лакомым кусочком для новичков в C / C ++. Оставляйте комментарии / критику и т. Д., Так как они очень приветствуются.

0
ответ дан 24 November 2019 в 13:27
поделиться

Просто включите все внешние заголовки в один общий заголовочный файл в вашем проекте, например global.h , и включить его во все ваши файлы c:

Это может выглядеть так:

#ifndef GLOBAL_GUARD
#define GLOBAL_GUARD

#include <glib.h>
/*...*/
typedef int  YOUR_INT_TYPE;
typedef char YOUR_CHAR_TYPE;
/*...*/
#endif

Этот файл использует include guard, чтобы избежать множественных включений, недопустимых множественных определений и т. д.

0
ответ дан 24 November 2019 в 13:27
поделиться

Вам нужно включить заголовок из вашего заголовка, и нет необходимости включать его в .c. Включаемые файлы должны идти после #define, чтобы они не включались без необходимости несколько раз. Например:

/* myHeader.h */
#ifndef MY_HEADER_H
#define MY_HEADER_H

#include <glib.h>

struct S
{
    gchar c;
};

#endif /* MY_HEADER_H */

и

/* myCode.c */
#include "myHeader.h"

void myFunction()
{
    struct S s;
    /* really exciting code goes here */
}
0
ответ дан 24 November 2019 в 13:27
поделиться

@PersonalPerson - Извините, но это просто ленивое кодирование. Вместо того, чтобы использовать try-catch из-за слишком большого количества операторов if, почему бы не провести рефакторинг вашего кода (т.е. поместить логику проверки в отдельный метод). Это сделает ваш код более чистым и читаемым, а также сохранит лучшие практики для повышения производительности.

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

Клянусь, я уже работал с вашим кодом раньше, и это сделало мою голову больно.

• определять все, что не требуется для кода, который использует заголовок для доступа к средствам, определенным заголовком.

Преимущество этого правила состоит в том, что если кому-то нужно использовать заголовок, им не нужно бороться, чтобы определить, какие другие заголовки также должны быть включены - они знают, что заголовок предоставляет все необходимое.

Возможным недостатком является то, что некоторые заголовки могут быть включены много раз; вот почему защита заголовков множественного включения имеет решающее значение (и почему компиляторы стараются избегать повторного включения заголовков, когда это возможно).

Реализация

Это правило означает, что если заголовок использует тип, например ' FILE * 'или' size_t '- тогда он должен гарантировать, что соответствующий другой заголовок (например, или ) должны быть включены. Следствие, о котором часто забывают, заключается в том, что заголовок не должен включать какой-либо другой заголовок, который не необходим пользователю пакета для использования пакета. Другими словами, заголовок должен быть минимальным.

Кроме того, правила GSFC предоставляют простой метод, гарантирующий, что именно это и происходит:

  • В исходном файле, который определяет функциональность, заголовок должен быть первым в списке заголовком. .

Следовательно, предположим, что у нас есть Волшебная Сортировка.

magicsort.h

#ifndef MAGICSORT_H_INCLUDED
#define MAGICSORT_H_INCLUDED

#include <stddef.h>

typedef int (*Comparator)(const void *, const void *);
extern void magicsort(void *array, size_t number, size_t size, Comparator cmp);

#endif /* MAGICSORT_H_INCLUDED */

magicsort.c

#include <magicsort.h>

void magicsort(void *array, size_t number, size_t size, Comparator cmp)
{
    ...body of sort...
}

Обратите внимание, что заголовок должен включать некоторый стандартный заголовок, который определяет size_t ; наименьший стандартный заголовок, который делает это, - , хотя некоторые другие также делают это ( , , , возможно, несколько других).

Также, как упоминалось ранее, если файлу реализации требуются другие заголовки, пусть так и будет, и совершенно нормально, что некоторые дополнительные заголовки необходимы. Но файл реализации (magicsort.c) должен включать их сам, а не полагаться на свой заголовок для их включения. Заголовок должен включать только то, что нужно пользователям программного обеспечения; не то, что нужно разработчикам.

Заголовки конфигурации

Если ваш код использует заголовок конфигурации (например, GNU Autoconf и сгенерированный 'config.h'), вам может потребоваться использовать его в 'magicsort.c':

#ifdef HAVE_CONFIG_H
#include "config.h"
#endif /* HAVE_CONFIG_H */

#include "magicsort.h"

...

Это единственный раз, когда я знаю, что закрытый заголовок модуля не является самым первым заголовком в файле реализации. Однако условное включение config.h, вероятно, должно быть в самом magicsort.h.


Обновление 2011-05-01

Указанный выше URL-адрес больше не работает (404). Вы можете найти стандарт C ++ (582-2003-004) на EverySpec.com ; стандарт C (582-2000-005), кажется, отсутствует в действии.

Руководящие принципы стандарта C были:

§2.1 ЕДИНИЦЫ

(1) Код должен быть структурирован как единицы или как стенд -alone файлы заголовков.

(2) Модуль должен состоять из одного файла заголовка (.h) и одного или нескольких файлов тела (.c). В совокупности файлы заголовка и тела называются исходными файлами.

(3) Файл заголовка модуля должен содержать всю соответствующую информацию, требуемую модулем клиента. Единицы 1 БЛОКИ

(1) Код должен быть структурирован как блоки или как отдельные файлы заголовков.

(2) Блок должен состоять из одного файла заголовка (.h) и одного или нескольких тел (.c) ) файлы. В совокупности файлы заголовка и тела называются исходными файлами.

(3) Файл заголовка модуля должен содержать всю соответствующую информацию, требуемую модулем клиента. Единицы 1 БЛОКИ

(1) Код должен быть структурирован как блоки или как отдельные файлы заголовков.

(2) Блок должен состоять из одного файла заголовка (.h) и одного или нескольких тел (.c) ) файлы. В совокупности файлы заголовка и тела называются исходными файлами.

(3) Файл заголовка модуля должен содержать всю соответствующую информацию, требуемую модулем клиента. Единицы клиенту необходим доступ только к файлу заголовка, чтобы использовать модуль.

(4) Файл заголовка модуля должен содержать операторы #include для всех других заголовков, требуемых заголовком модуля. Это позволяет клиентам использовать модуль, включая один файл заголовка.

(5) Файл тела модуля должен содержать оператор #include для заголовка модуля перед всеми другими операторами #include. Это позволяет компилятору проверить, что все необходимые операторы #include находятся в файл заголовка.

(6) Основной файл должен содержать только функции, связанные с одним модулем. Одно тело файла не может предоставлять реализации для функций, объявленных в разных заголовках.

(7) Все клиентские модули, которые используют любую часть данного модуля U, должны включать файл заголовка для модуля U; этот гарантирует, что существует только одно место, где определены объекты в модуле U. Клиентские подразделения могут вызывать только функции, определенные в заголовке модуля; они не могут вызывать функции, определенные в тело, но не объявленное в заголовке. Клиентские модули не могут обращаться к переменным, объявленным в теле но не в заголовке.

Компонент содержит один или несколько блоков. Например, математическая библиотека - это компонент, содержащий несколько единиц, таких как вектор, матрица и кватернион.

Автономные файлы заголовков не имеют связанных тел; например, заголовок общих типов делает не объявляют функции, поэтому тело не требуется.

Некоторые причины наличия нескольких файлов тела для модуля:

  • Часть основного кода зависит от оборудования или операционной системы, но остальное является общим.
  • Файлы слишком велики.
  • Модуль представляет собой общий пакет служебных программ, и в некоторых проектах используются только некоторые из функции. Помещение каждой функции в отдельный файл позволяет компоновщику исключить те, которые не используется из окончательного изображения.

§2.1.1 Обоснование включения заголовка

Этот стандарт требует, чтобы заголовок модуля содержал операторы #include для всех остальных требуемых заголовков по заголовку блока. Размещение #include для заголовка модуля первым в теле модуля позволяет компилятору убедитесь, что заголовок содержит все требуемые операторы #include .

Альтернативный дизайн, не разрешенный этим стандартом, не допускает использование операторов #include в заголовках; все #include выполняются в основных файлах. Заголовочные файлы модуля должны содержать операторы #ifdef , которые проверяют что необходимые заголовки включены в правильном порядке.

Одно из преимуществ альтернативного дизайна состоит в том, что список #include в основном файле точно соответствует Список зависимостей необходим в make-файле, и этот список проверяется компилятором. Со стандартом design, необходимо использовать инструмент для создания списка зависимостей. Однако все ветки рекомендуемые среды разработки предоставляют такой инструмент.

Главный недостаток альтернативной конструкции заключается в том, что при изменении требуемого списка заголовков модуля каждый файл который использует этот модуль, необходимо отредактировать, чтобы обновить список операторов #include . Также необходимый список заголовков для модуля библиотеки компилятора может отличаться для разных целей.

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

Другой распространенной практикой является включение всех файлов заголовков системы перед файлами заголовков проекта в файлы тела. Этот стандарт не следует этой практике, потому что некоторые файлы заголовков проекта могут зависят от файлов системных заголовков, потому что они используют определения в системном заголовке, или потому что они хотят переопределить определение системы. Такие файлы заголовков проекта должны содержать #include операторы для системных заголовков; если тело включает их первыми, компилятор не проверяет это.

Стандарт GSFC доступен в Интернет-архиве 2012-12-10

Информация любезно Эрик С. Буллингтон :

Ссылка на стандарт кодирования C NASA доступна и загружена через Интернет-архив:

http://web.archive.org/web/20090412090730/http://software.gsfc.nasa.gov/assetsbytype.cfm?TypeAsset = Standard

Sequencing

В вопросе также задается следующий вопрос:

Если да, нужно ли мне также поместить его (строки #include ) между #ifndef и #define или после #define .

Ответ показывает правильный механизм - вложенные включения и т. Д. должен быть после #define #define должен быть второй строкой заголовка без комментариев) - но это не объясняет, почему это правильно.

Учтите что произойдет, если вы поместите #include между #ifndef и #define . Предположим, что сам другой заголовок включает в себя различные заголовки, возможно, даже косвенно #include "magicsort.h" . Если второе включение magicsort.h происходит до #define MAGICSORT_H_INCLUDED , то заголовок будет включен во второй раз до того, как будут определены типы, которые он определяет. Так, в C89 и C99 любое имя типа typedef будет ошибочно переопределено (C2011 позволяет переопределить их на тот же тип), и вы получите накладные расходы на многократную обработку файла, что в первую очередь сводит на нет цель защиты заголовка. По этой же причине #define является второй строкой и не записывается непосредственно перед #endif . Приведенная формула является надежной:

#ifndef HEADERGUARDMACRO
#define HEADERGUARDMACRO

...original content of header — other #include lines, etc...

#endif /* HEADERGUARDMACRO */
98
ответ дан 24 November 2019 в 13:27
поделиться

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

#ifndef INCLUDE_FILE_H
 #error "#include INCLUDE.h" must appear in source files before "#include THISFILE.h"
#endif
0
ответ дан 24 November 2019 в 13:27
поделиться

Обычно я делаю один включаемый файл, который включает все необходимые зависимости в правильном порядке. Так что у меня может быть:

#ifndef _PROJECT_H_
#define _PROJECT_H_
#include <someDependency.h>
#include "my_project_types.h"
#include "my_project_implementation_prototypes.h"
#endif

Все в project.h. Теперь project.h можно включить где угодно без требований к порядку, но у меня все еще есть возможность иметь свои зависимости, типы и прототипы функций API в разных заголовках.

-1
ответ дан 24 November 2019 в 13:27
поделиться
Другие вопросы по тегам:

Похожие вопросы: