C ++ #include семантика

Это несколько вопросов для одной и той же инструкции предварительной обработки.

1 – или “”?

Помимо информации, найденной в MSDN:

Директива #include (C-C ++)

1.a: Каковы различия между двумя обозначениями?
1.b: Все ли компиляторы реализуют их одинаково?
1.c: Когда вы будете использовать , и когда вы будете использовать «» (то есть, каковы критерии, которые вы использовали бы для использования одного или другого для заголовка)?

2 – #include {TheProject / TheHeader.hpp} или {TheHeader.hpp}?

Я видел, как минимум два способа записи include в себя заголовки проектов. Учитывая, что у вас есть как минимум 4 типа заголовков, то есть:

  • частные заголовки вашего проекта?
  • заголовки вашего проекта, но которые экспортируют символы (и, следовательно, «публичные»)
  • заголовки другого проекта, который ваш модуль связывает с
  • заголовки компилятора или стандартной библиотеки

Для каждого типа заголовков:

2.a: Вы использовали бы или “”?
2.b: Вы включили бы с помощью {TheProject / TheHeader.hpp} или с помощью {TheHeader.hpp}?

3 – Бонус

3.a: Вы работаете над проектом с источниками и / или заголовками в древовидной организации (т. Е. Каталоги внутри каталогов, в отличие от «каждого файла в одном каталоге») и каковы плюсы и минусы?

Прочитав все ответы, а также документацию компилятора, я решил, что буду следовать следующему стандарту.

Для всех файлов, будь то заголовки проектов или внешние заголовки, всегда используйте шаблон:

#include  

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

Конечно, это означает, что каталог проекта, в котором заголовки проекта должны быть добавлены как «default include header» в make-файл, тоже.

Причиной этого выбора является то, что я нашел следующую информацию:

1. Шаблон include “” является зависимым от компилятора

Я дам ответы ниже

1.a Стандарт

Источник:

  • C ++ 14 Рабочий проект n3797: https://isocpp.org/files/papers/N3797.pdf
  • C ++ 11, C ++ 98, C99, C89 (цитируемый раздел не изменяется во всех этих стандартах)

В разделе 16.2 «Ввод исходного файла» мы можем прочитать:

Директива предварительной обработки формы

  #include  new-line 

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

Это означает, что #include <...> будет искать файл в определенной реализации.

Затем следующий параграф:

Директива предварительной обработки формы

  #include "q-char-sequence" new-line 

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

  #include  new-line 

с идентичной содержащейся последовательностью (включая> символы, если таковые имеются) из исходной директивы.

Это означает, что #include “…” будет искать файл в определенном порядке реализации, а затем, если файл не будет найден, будет выполняться другой поиск, как если бы он был #include <...>

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

Обратите внимание, что по какой-либо причине нигде в стандартах не делается различий между заголовками «system» или «library» или другими заголовками. Единственное отличие в том, что #include <...> похоже, предназначено для заголовков, а #include “…”, похоже, предназначено для источника (по крайней мере, в английской формулировке).

1.b Visual C ++:

Источник:

#include “MyFile.hpp”

Препроцессор выполняет поиск включенных файлов в следующем порядке:

  1. В том же каталоге, что и файл, содержащий оператор #include.
  2. В каталогах любых ранее открытых включенных файлов в обратном порядке, в которых они были открыты. Поиск начинается с каталога включенного файла, который был открыт последним, и продолжается через каталог открытого файла, который был открыт первым.
  3. По пути, указанному каждым параметром компилятора / I.
  4. (*) Вдоль путей, заданных переменной среды INCLUDE или включенной по умолчанию средой разработки.

#include

Препроцессор выполняет поиск включенных файлов в следующем порядке:

  1. По пути, указанному каждым параметром компилятора / I.
  2. (*) Вдоль путей, заданных переменной среды INCLUDE или включенной по умолчанию средой разработки.

Обратите внимание на последний шаг

В документе не ясно, что «Вдоль путей, заданных переменной среды INCLUDE», для части <...> и "..." . Следующая цитата заставляет его придерживаться стандарта:

Для include файлов, которые указаны как #include “path-spec”, поиск каталога начинается с каталога родительского файла, а затем проходит через каталоги любых файлов дедушки и бабушки. То есть поиск начинается относительно каталога, содержащего исходный файл, который содержит директиву #include, которая обрабатывается. Если нет файла grandparent и файл не найден, поиск продолжается, как если бы имя файла было заключено в угловые скобки.

Последний шаг (отмеченный звездочкой) является, таким образом, интерпретацией из чтения всего документа.

1.c g ++

Источник:

Следующая цитата суммирует процесс:

GCC […] будет искать заголовки, запрошенные с #include в [системных каталогах] […] Все каталоги, названные именем -I, выполняются в порядке слева направо перед каталогами по умолчанию

GCC ищет заголовки, запрошенные с #include «file» сначала в каталоге, содержащем текущий файл, затем в каталогах, указанных в параметрах -iquote, а затем в тех же местах он искал заголовок, запрошенный с помощью угловых скобок.

#include “MyFile.hpp”

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

  1. В том же каталоге, что и файл, содержащий оператор #include.
  2. Вдоль пути, заданного параметром -iquote компилятора.
  3. Что касается #include

#include

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

  1. По пути, указанному каждым параметром -I компилятора.
  2. Внутри системных каталогов.

1.d Oracle / Sun Studio CC

Источник:

Обратите внимание, что текст несколько противоречит (см. Пример для понимания). Ключевая фраза: « Разница заключается в том, что текущий каталог выполняется только для файлов заголовков, имена которых указаны в кавычках ».

#include “MyFile.hpp”

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

  1. Текущий каталог (то есть каталог, содержащий файл «включая»)
  2. Каталоги, названные с параметрами -I, если они есть
  3. Системный каталог (например, каталог / usr / include)

#include

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

  1. Каталоги, названные с параметрами -I, если они есть
  2. Системный каталог (например, каталог / usr / include)

1.e Справочник компилятора XL C / C ++ – IBM / AIX

Источник:

Оба документа называются «Справочник компилятора XL C / C ++». Первый документ старше (8,0), но его легче понять. Второй – более новый (12.1), но немного сложнее расшифровать.

#include “MyFile.hpp”

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

  1. Текущий каталог (то есть каталог, содержащий файл «включая»)
  2. Каталоги, названные с параметрами -I, если они есть
  3. Системный каталог (например, директории / usr / vac [cpp] / include или / usr / include)

#include

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

  1. Каталоги, названные с параметрами -I, если они есть
  2. Системный каталог (например, каталог / usr / vac [cpp] / include или / usr / include)

1.e Заключение

Шаблон “” может привести к точной ошибке компиляции компиляторов, и поскольку в настоящее время я работаю как на Windows Visual C ++, Linux g ++, Oracle / Solaris CC и AIX XL, это неприемлемо.

В любом случае, преимущество «описанных функций» далеко не интересно, так что …

2. Используйте шаблон {namespace} /header.hpp

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

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

Использование каталога в include могло бы сэкономить время, потому что пользователю пришлось бы либо написать:

 #include  

или

 #include  

Вы заметите, что пока

 #include "Header.hpp" 

было бы успешно скомпилировано, тем самым, все еще скрывая проблему, тогда как

 #include  

не собирались бы в обычных условиях.

Таким образом, приклеивание к нотации <> сделало бы обязательным для разработчика префикс include с правильным каталогом, еще одна причина предпочесть <> to “”.

3. Заключение

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

Конечно, стандартные библиотеки по-прежнему включены как обычно, то есть:

 #include  #include  

Обычно я использую <> для заголовков системы и “” для заголовков проектов. Что касается путей, это необходимо только в том случае, если файл, который вы хотите, находится в подкаталоге пути включения.

например, если вам нужен файл в / usr / include / SDL /, но только / usr / include / находится в вашем пути включения, то вы можете просто использовать:

 #include  

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

EDIT TO ANSWER COMMENT: Это зависит, если в библиотеке есть только несколько включений, я бы включил его подкаталог в путь include, но если в библиотеке много заголовков (например, десятки), я бы предпочел просто это в subdir, который я указываю. Хорошим примером этого являются системные заголовки Linux. Вы используете их как:

 #include  #include  

и т.п.

ИЗМЕНИТЬ, ЧТОБЫ ВКЛЮЧАТЬ ДРУГОЙ ОТВЕТ: также, если возможно, что две или более библиотеки предоставляют заголовки с тем же именем, то решение подкаталога в основном дает каждому заголовку пространство имен.

Чтобы процитировать из стандарта C99 (с первого взгляда формулировка кажется идентичной в стандарте C90, но я не могу вырезать-n-paste из этого):

Директива предварительной обработки формы

# include "q-char-sequence" new-line

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

# include new-line

с идентичной содержащейся последовательностью (включая> символы, если таковые имеются) из исходной директивы.

Таким образом, места, которые искали: #include "whatever" являются супер-множеством местоположений, поиск которых осуществляется с помощью #include . objective состоит в том, что первый стиль будет использоваться для заголовков, которые в целом «принадлежат» вам, а второй метод будет использоваться для заголовков, которые «принадлежат» компилятору / среде. Конечно, часто есть серые области, которые вы должны использовать для заголовков Boost, например? Я бы использовал #include <> , но я бы не стал спорить слишком много, если бы кто-то из моей команды захотел #include "" .

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

Я займу вторую часть вашего вопроса:

Обычно я использую когда я заголовки третьей стороны. И "myHeader.h" при включении заголовков из проекта.

Причина, по которой я использую вместо заключается в том, что возможно, что более одной библиотеки имеет файл «libHeader.h». Чтобы включить их, вам нужно имя библиотеки как часть включенного имени файла.

1.a: Каковы различия между двумя обозначениями?

“” начинает поиск в каталоге, где находится файл C / C ++. <> запускает поиск в каталогах -I и по умолчанию (например, / usr / include). Оба они, в конечном счете, ищут один и тот же набор мест, только порядок отличается.

1.b: Все ли компиляторы реализуют их одинаково?

Надеюсь, что так, но я не уверен.

1.c: Когда вы будете использовать <>, и когда вы будете использовать «» (то есть, каковы критерии, которые вы использовали бы для использования одного или другого для заголовка)?

Я использую «», когда включенный файл должен находиться рядом с файлом C, <> во всех остальных случаях. В частности, в нашем проекте все «общедоступные» включают файлы в каталог project / include, поэтому я использую для них <>.

2 – #include {TheProject / TheHeader.hpp} или {TheHeader.hpp}?

Как уже указывалось, xxx / filename.h позволяет делать такие вещи, как diskio / ErrorCodes.h и netio / ErrorCodes.h

* частные заголовки вашего проекта?

Частный заголовок моей подсистемы в проекте. Используйте «filename.h» Общий заголовок моей подсистемы в проекте (не видимый вне проекта, но ansible для других подсистем). Используйте или, в зависимости от соглашения, адаптированного для проекта. Я предпочел бы использовать

* заголовки вашего проекта, но которые экспортируют символы (и, следовательно, «публичные»)

включите их точно так же, как пользователи вашей библиотеки. Вероятно

* заголовки другого проекта, который ваш модуль связывает с

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

3.a: Вы работаете над проектом с источниками и / или заголовками в древовидной организации (т. Е. Каталоги внутри каталогов, в отличие от «каждого файла в одном каталоге») и каковы плюсы и минусы?

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

Если я правильно помню.

Вы используете бриллиант для всех библиотек, которые можно найти в вашем «пути». Таким образом, любая библиотека, находящаяся в STL, или те, которые вы установили. В Linux ваш путь обычно «/ usr / include», в Windows я не уверен, но я бы предположил, что он находится под «C: \ windows».

Вы используете «», чтобы указать все остальное. «my_bla.cpp» без информации о начальном каталоге будет разрешен в каталог, в котором находится ваш код / ​​компиляция, или вы также можете указать точное местоположение вашего объекта с ним. Подобно этому “c: \ myproj \ some_code.cpp”

Тип заголовка не имеет значения, просто местоположение.

Re <> vs “”. В моем магазине я очень разбираюсь в вопросах «стиля». Одна из немногих областей, где у меня есть требование, заключается в использовании угловых скобок в операторах #include – это правило: если вы # включаете в себя операционную систему или файл компилятора, вы можете использовать угловые скобки, если это необходимо. Во всех остальных случаях они запрещены. Если вы # включаете файл, написанный кем-то здесь или сторонней библиотекой, <> запрещен.

Причина такова: #include “xh” и #include не искать одни и те же пути. #include будет искать только системный путь и все, что у вас есть. Важно, что он не будет искать путь, где находится файл xh, если этот каталог не включен в путь поиска каким-либо другим способом.

Например, предположим, что у вас есть следующие файлы:

C: \ DEV \ углы \ main.cpp

 #include "c:\utils\mylib\mylibrary.h" int main() { return 0; } 

C: \ Drivers \ MyLib \ mylibrary.h

 #ifndef MYLIB_H #define MYLIB_H #include  namespace mylib { void Speak(SpeechType speechType); }; #endif 

C: \ Drivers \ mhlib \ speech.h

 #ifndef SPEECH_H #define SPEECH_H namespace mylib { enum SpeechType {Bark, Growl}; }; #endif 

Это не будет компилироваться без изменения пути либо путем установки переменной среды PATH, либо -i’ing в каталоге c: \ utils \ mhlib \. Компилятор не сможет переустановить #include хотя этот файл находится в том же каталоге, что и mylibrary.h !

Мы широко используем относительные и абсолютные пути в операторах #include в нашем коде по двум причинам.

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

2) Мы используем Junctions для сопоставления физического местоположения на жестком диске с каталогом на логическом диске, а затем используем полностью определенный путь на логическом диске во всех #includes. Например:

#include "x:\utils\mylib.h" – хорошо, x: является подключенным диском, а x: \ utils указывает на c: \ code \ utils_1.0 на жестком диске

#include "c:\utils_1.0\mylib.h" – плохо! приложение tha t # включает mylib.h теперь связано с конкретной версией библиотеки MYLIB, и все разработчики должны иметь ее в том же каталоге на своем жестком диске c: \ utils_1.0

Наконец, широкая, но трудно достижимая цель моей команды состоит в том, чтобы иметь возможность поддерживать компиляцию с одним кликом. Это включает в себя возможность скомпилировать основное исходное дерево, делая не что иное, как получение кода из исходного элемента управления, а затем попадание «компиляции». В частности, я отказываюсь устанавливать пути и машинные каталоги #include для того, чтобы их можно было компилировать, потому что каждый маленький дополнительный шаг, который вы добавляете к фазе настройки в buildign для машины разработки, просто усложняет работу, и требуется больше времени, чтобы получить новую машину для ускорения и генерации кода.

Существуют два основных различия между <> и "" . Первый – это символ, который закончит имя – в именах заголовков нет escape-последовательностей, и поэтому вы можете принудительно выполнить #include или "bla>file.cpp" . Это, вероятно, не будет Однако другое отличие заключается в том, что система не должна встречаться на "" , просто <> . Таким образом, #include "iostream" не гарантированно работает; #include is. Мои личные предпочтения использовать "" для файлов, которые являются частью проекта, и <> для файлов, которые не являются. Некоторые люди используют только <> для стандартных заголовков библиотек и "" для всех остальных. Некоторые люди даже используют <> только для Boost и std, это зависит от проекта. Как и все аспекты стиля, самое главное – быть последовательным.

Что касается пути, внешняя библиотека укажет соглашение для заголовков; например . В локальном проекте я бы написал все пути относительно srcdir верхнего уровня (или в проекте библиотеки, где они разные, каталог include).

При написании библиотеки вы также можете использовать <> для разделения между частными и публичными заголовками или не -I исходный каталог, но каталог выше, поэтому вы #include "public_header.hpp" и "src/private_header.hpp" . Это действительно зависит от вас.

EDIT: Что касается проектов со структурами каталогов, я бы очень рекомендовал их. Представьте себе, если все импульсы были в одном каталоге (и не было подпространств)! Структура каталогов хороша тем, что позволяет вам находить файлы проще, и это позволяет вам больше гибкости в именовании ( "module\_text\_processor.hpp" в отличие от "module/text\_processor.hpp" ). Последнее более естественно и проще в использовании.

Я использую <...> из системного файла заголовка (stdio, iostreams, string и т. Д.) И «…» для заголовков, специфичных для этого проекта.

Мы используем #include “header.h” для заголовков, локальных для проекта, и #include для включения системы, сторонних включений и других проектов в решении. Мы используем Visual Studio, и гораздо проще использовать каталог проекта в заголовке include, таким образом, всякий раз, когда мы создаем новый проект, нам нужно указывать путь include для каталога, содержащего все каталоги проектов, а не отдельный путь для каждый проект.

  • Препроцессор дампа GCC определяет
  • Почему большинство разработчиков C используют определение вместо const?
  • Просмотр расширенных макросов C
  • Макро против функции в C
  • #define в Java
  • Сначала препроцессор C комментирует или расширяет macros?
  • Использование X-Macros в реальном мире
  • Должен ли я использовать #define, enum или const?
  • C #define макрос для отладочной печати
  • Что означает «#define STR (a) #a»?
  • Почему macros препроцессора злые и какие альтернативы?
  • Interesting Posts

    прокручивайте вверх и вниз по нажатию на кнопку выбора с помощью jquery

    Как получить отпечаток сертификата подписчика (SHA1) для OAuth 2.0 на Android?

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

    Windows 8.1 – отключить сенсорный экран?

    Шаблоны C ++, неопределенная ссылка

    Как вводить CSS в элемент управления WebBrowser?

    Какова механика оптимизации коротких строк в libc ++?

    Ограничение для предотвращения вставки пустой строки в MySQL

    В запрошенном ресурсе нет заголовка «Access-Control-Allow-Origin» – при попытке получить данные из REST API

    Что делает iTunes «Media Kind» на самом деле?

    Как использовать JDK 7 на Mac OSX?

    Возможно ли, чтобы команды bash продолжались до результата предыдущей команды?

    Как вставить запись SQLite с установленным значением «сейчас» в приложении Android?

    Всегда ли BroadcastReceiver.onReceive работает в streamе пользовательского интерфейса?

    setValue: forUndefinedKey: этот class не является ключевым значением, совместимым с кодировкой для ключа

    Давайте будем гением компьютера.