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

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

1 - < > или ""?

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

# включить директиву (C-С++)

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

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

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

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

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

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

3 - Бонус

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

Ответ 1

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

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

#include <namespace/header.hpp>

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

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

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

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

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

1.a Стандарт

Источник:

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

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

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

  #include <h-char-sequence> new-line

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

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

Затем следующий абзац:

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

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

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

  #include <h-char-sequence> new-line

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

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

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

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

1.b Visual С++:

Источник:

#include "MyFile.hpp"

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

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

#include < MyFile.hpp >

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

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

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

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

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

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

1.c g++

Источник:

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

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

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

#include "MyFile.hpp"

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

  • В том же каталоге, что и файл, содержащий оператор #include.
  • По пути, указанному параметром -iquote компилятора.
  • Что касается #include <MyFile.hpp>

#include < MyFile.hpp >

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

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

1.d Oracle/Sun Studio CC

Источник:

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

#include "MyFile.hpp"

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

  • Текущий каталог (то есть каталог, содержащий файл "include" )
  • Каталоги, названные с параметрами -I, если есть
  • Системный каталог (например, каталог /usr/include )

#include < MyFile.hpp >

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

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

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

Источник:

Оба документа называются "XL Comp/С++ Reference". Первый документ старше (8.0), но его легче понять. Второй - более новый (12.1), но немного сложнее расшифровать.

#include "MyFile.hpp"

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

  • Текущий каталог (то есть каталог, содержащий файл "include" )
  • Каталоги, названные с параметрами -I, если есть
  • Системный каталог (например, каталоги /usr/vac [cpp]/include или /usr/include )

#include < MyFile.hpp >

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

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

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

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

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

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

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

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

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

#include <MyLocalProject/Header.hpp>

или

#include <GlobalInclude/Header.hpp>

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

#include "Header.hpp"

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

#include <Header.hpp>

не будет компилироваться в обычных условиях.

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

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

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

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

#include <cstdlib>
#include <vector>

Ответ 2

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

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

#include <SDL/whatever.h>

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

ИЗМЕНИТЬ ОТВЕТ НА КОММЕНТАРИЙ: Это зависит от того, есть ли только несколько включений для библиотеки, я бы просто включил его в подкаталог в пути include, но если в библиотеке много заголовков (например, десятки), я бы предпочел просто иметь его в субдире, который я укажу. Хорошим примером этого являются заголовки систем Linux. Вы используете их как:

#include <sys/io.h>
#include <linux/limits.h>

и др.

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

Ответ 3

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

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

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

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

# include <h-char-sequence> new-line

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

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

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

Ответ 4

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

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

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

Ответ 5

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

"" начинает поиск в каталоге, где находится файл 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" Открытый заголовок моей подсистемы в проекте (не видимый вне проекта, но доступный для других подсистем). Используйте или, в зависимости от соглашения, адаптированного для проекта. Я предпочел бы использовать

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

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

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

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

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

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

Ответ 6

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

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

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

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

Ответ 7

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

Что касается пути, внешняя библиотека укажет соглашение для заголовков; например <boost/preprocessor.hpp> <wx/window.h> <Magic++.h>. В локальном проекте я бы написал все пути относительно верхнего уровня srcdir (или в проекте библиотеки, где они разные, каталог include).

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

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

Ответ 8

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

Причина такова: #include "x.h" и #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 <speech.h>

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 <speech.h>, хотя этот файл находится в том же каталоге, что и mylibrary.h!

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

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

2) Мы используем Junctions, чтобы отобразить физическое местоположение на жестком диске в каталоге на логическом диске, а затем использовать полностью -qualified путь на логическом диске во всех #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 для машины разработки, просто усложняет работу, и требуется больше времени для получения новой машины до скорости и генерации кода.

Ответ 9

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

Ответ 10

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