#include формат защиты заголовка?

Я знаю, что это не имеет большого значения для проекта, но если вы используете защиту заголовков #defined для своего кода C++, какой формат вы используете? например предполагая заголовок с именем foo.hpp:

#ifndef __FOO_HPP__
...

#ifndef INCLUDED_FOO_HPP
...

#ifndef SOME_OTHER_FORMAT

Я в восторге от #define в верхнем регистре, но не могу определиться с форматом для этих охранников.


person Rob    schedule 24.11.2008    source источник


Ответы (13)


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

Например, у вас есть большой проект с двумя файлами где-то в вашем коде.

/myproject/module1/misc.h
/myproject/module2/misc.h

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

Так что я договорился с

MYPROJECT_MODULE1_MISC_H_
MYPROJECT_MODULE2_MISC_H_

Названия эти довольно длинные, но по сравнению с болью от двойных определений оно того стоит.

Другой вариант, если вам не нужна независимость от компилятора/платформы, вы можете поискать некоторые вещи #pragma Once.

person Fionn    schedule 24.11.2008
comment
Спасибо - я использую много разных модулей/пространств имен, так что это имеет смысл. Я потеряю конечное подчеркивание, хотя. :) - person Rob; 25.11.2008

Я всегда использую INCLUDED_FOO_HPP

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

person MrZebra    schedule 24.11.2008

Чтобы действительно избежать конфликтов имен, я использую GUID:

#ifndef GUARD_8D419A5B_4AC2_4C34_B16E_2E5199F262ED
person Martin Cote    schedule 24.11.2008
comment
В этом странный смысл :-) - person Leonardo Herrera; 24.11.2008
comment
Использование даты и времени тоже работает, если вы не хотите генерировать GUID. - person Jack; 25.11.2008
comment
Это работает, но будет запутанно, если вам нужно проверить включение заголовка в какой-либо файл cpp. - person efotinis; 25.11.2008
comment
Маловероятно, что в моей системе будет столько включаемых файлов, сколько атомов в известной вселенной — если только у нас нет субатомных жестких дисков. - person new123456; 12.07.2011
comment
Чтобы действительно избежать конфликтов имен с GUID, запретите копирование кода ;) ... честно: может быть сложнее проверить, имеет ли каждый уникальный заголовок в данной кодовой базе свою собственную уникальную защиту включения с использованием GUID по сравнению с используя удобочитаемые идентификаторы. Возможно, на каждом коммите может быть запущен хук. - person Wolf; 21.11.2017

Лично я просто использую имя файла FOO_HPP. Google использует весь путь, например SRC_ENGINE_FAST_HPP.

Определенные наборы имен и сигнатур функций всегда зарезервированы для реализации:

  • Каждое имя, которое содержит двойное подчеркивание (_ _) или начинается с подчеркивания, за которым следует заглавная буква (2.11), зарезервировано реализацией для любого использования.
  • Каждое имя, начинающееся с подчеркивания, зарезервировано реализацией для использования в качестве имени в глобальном пространстве имен.

(17.4.3.1.2/1)

person Johannes Schaub - litb    schedule 24.11.2008
comment
Я поддерживаю это, за исключением того, что я предпочитаю использовать тот же регистр в #define, что и в имени файла. В стране UNIX (и т. д.) FOO.HPP — это не то же самое, что foo.hpp. - person activout.se; 24.11.2008

Я предпочитаю этот формат:

#ifndef FOO_HPP
#define FOO_HPP

/* ... */

#endif // FOO_HPP
  • Простой #ifndef вместо #if !defined(...), потому что редко имеет смысл использовать сложное условие для защиты заголовка.
  • Часть _HPP для обозначения идентификатора как защиты заголовка.
  • Никаких начальных символов подчеркивания, потому что такие идентификаторы (начинающиеся с 2 символов подчеркивания или с 1 подчеркивания и заглавной буквы) зарезервированы для реализации.
  • Базовая часть — это просто имя файла, FOO. Однако для кода библиотеки, который будет повторно использоваться, рекомендуется добавить еще одну часть в начале. Обычно это содержащее пространство имен или имя «модуля», например. MYLIB_FOO_HPP и помогает избежать конфликтов имен.
person efotinis    schedule 24.11.2008

я использую

 #if !defined(FOO_HPP_INCLUDED)

Я предпочитаю современный синтаксис defined, потому что он позволяет || && операторы, даже если они здесь не используются.

Также

 #ifndef __FOO_HPP__

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

person James Curran    schedule 24.11.2008
comment
Не уверен, что это получило отрицательный голос. Это правильно, даже если бы я всегда использовал #ifndef вместо !defined(), потому что нет необходимости использовать || или && в этом контексте. - person Jonathan Leffler; 24.11.2008

Если вы используете Visual Studio или компилятор Microsoft, используйте прагму

#pragma once
person JaredPar    schedule 24.11.2008
comment
Наверное, потому что это не портабельно. Используйте прагму, но также поставьте защиту включения на тот случай, если вам когда-нибудь понадобится использовать другой компилятор. - person KeithB; 24.11.2008
comment
потому что такой код заставит вас немедленно провалить собеседование со мной. - person shoosh; 24.11.2008
comment
Даже если вы используете Visual Studio, вы все равно должны придерживаться стандарта, насколько это возможно. - person jalf; 24.11.2008
comment
@KeithB, вопрос не касался переносимости. - person JaredPar; 24.11.2008
comment
@Shy, если вы подводите людей за правильные ответы с правильной квалификацией, вам, вероятно, не следует ни с кем брать интервью. - person JaredPar; 24.11.2008
comment
Этот ответ не получит от меня поддержки без гораздо большего количества оговорок и оговорок в тексте. Я не уверен в отрицательном голосовании; это заманчиво, но не обязательно. - person Jonathan Leffler; 24.11.2008
comment
Если вопрос конкретно не требует решения для конкретной платформы, я всегда предпочитаю переносное решение. Даже если указана платформа, при выборе между переносимым и зависящим от платформы решением следует отдавать предпочтение переносимому решению. - person KeithB; 24.11.2008
comment
Также поддерживается GCC с версии 3.4, не только Microsoft. Конечно, вопрос о предпочтительности переносимого кода стоит. - person Steve Jessop; 25.11.2008
comment
Обратите внимание, что Microsoft в своих собственных заголовках для VisualC++, которым нужна переносимость Zero, использует оба стиля защиты. - person James Curran; 25.11.2008

Я всегда использую использование

#ifndef FOOBAR_CPP
person Scott    schedule 24.11.2008

Я также всегда использовал что-то вроде:

#ifndef FOO_HPP
#define FOO_HPP 1

...

#endif

Как упоминало большинство людей, не добавляйте символы с двойным подчеркиванием, поскольку они зарезервированы стандартом С++ для внутреннего использования реализацией.

Возможно, вы захотите взглянуть на прекрасную книгу Джона Лакоса «Крупномасштабный дизайн программного обеспечения на C++» (ссылка на Amazon - очищено для скрипта kiddy link nazis) по некоторым соображениям, касающимся включения заголовков.

ХТН

ваше здоровье,

Роб

person Rob Wells    schedule 24.11.2008

Когда мне платят за мое время, а стандарта компании еще нет, я использую:

#ifndef path_to_file_h
#define path_to_file_h

Причина использования нижнего регистра заключается в том, что проще копировать и вставлять имена файлов и заменять косые черты символами подчеркивания. Причина использования #ifndef заключается в том, что он хорошо сочетается с #define, что облегчает понимание того, что символы одинаковы. Однако мне нравится идея GUID, так что я мог бы попробовать ее.

Когда мне не платят за мое время и я не выпускаю свой код в открытый доступ, я просто использую #pragma once. В отличие от большинства других проблем с переносимостью, включить защиту позже так же просто, как и сейчас, и это может сделать кто-то, кто ничего не знает о кодовой базе (например, я через год или какой-нибудь невинный программист, которому я отправляю свой код) , поэтому YAGNI применяется.

person Steve Jessop    schedule 25.11.2008

Я склонен использовать:

#ifndef FILE_DATE_H_

(замените _H_ соответствующим расширением, например _HPP_ и т. д.). Отметка даты предназначена для предотвращения коллизий с другими заголовками с теми же именами в других направлениях/библиотеках.

так что в итоге это выглядит так:

#ifndef SOMEFILE_20082411_H_
person Evan Teran    schedule 24.11.2008

я использую

<FILENAME_IN_ALL_CAPS>_<YYYYMMDD>

or

<FILENAME_IN_ALL_CAPS>_INCLUDED_<YYYYMMDD>

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

<FILENAME_IN_ALL_CAPS>_<YYYYMMDD>a
<FILENAME_IN_ALL_CAPS>_<YYYYMMDD>b
<FILENAME_IN_ALL_CAPS>_<YYYYMMDD>...
person Sebastian Mach    schedule 15.12.2011

Я бы выбрал путь к файлу + суффикс boost _INCLUDED плюс широко поддерживаемый в настоящее время #pragma once

Во многих редакторах (для меня это великолепно) вы также можете определить некоторые макросы/фрагменты для этого.

Вот тот, который сделает это за вас:

<snippet>
    <content><![CDATA[
#ifndef ${1:${TM_FILEPATH/(.*\/(include|src))*([^a-zA-Z0-9_]+)*([a-zA-Z0-9_]+)([.])*([a-zA-Z0-9_]+)*/\U$4_$6/ig}_INCLUDED}
#define $1
#pragma once


$0


#endif // $1
]]></content>
    <tabTrigger>incguard</tabTrigger>
    <description>include guard</description>
</snippet>

so yourproject/include/yourlib/yourfile.hpp

становится YOURLIB_YOURFILE_HPP_INCLUDED

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

person nonsensation    schedule 29.06.2014