Является ли # прагма когда-то безопасным включать охрану?
Я читал, что есть некоторая оптимизация компилятора при использовании #pragma once
которая может привести к более быстрой компиляции. Я признаю, что это нестандартно и, следовательно, может представлять проблему межплатформенной совместимости.
Это что-то, что поддерживается большинством современных компиляторов на платформах без windows (gcc)?
Я хочу избежать проблем с компиляцией платформы, но также хочу избежать дополнительной работы аварийных охранников:
- Почему мои защитники не препятствуют рекурсивному включению и множественным определениям символов?
- Именование включает охранников
- Почему C # C ++ не имеет «стандартного стандарта ISO»?
- Определение переменной в файлах заголовков
#pragma once #ifndef HEADER_H #define HEADER_H ... #endif // HEADER_H
Должен ли я быть обеспокоен? Должен ли я потратить на это дальнейшую психическую энергию?
Использование #pragma once
должно работать на любом современном компиляторе, но я не вижу причин не использовать стандартный #ifndef
include guard. Он работает отлично. Одно из предостережений заключается в том, что GCC не поддерживал #pragma once
до версии 3.4 .
Я также обнаружил, что, по крайней мере, в GCC, он распознает стандартный #ifndef
include guard и оптимизирует его , поэтому он не должен быть намного медленнее, чем #pragma once
.
#pragma once
имеет один недостаток (кроме нестандартного), и если у вас есть тот же файл в разных местах (у нас это есть, потому что наша система сборки копирует файлы), тогда компилятор будет думать, что это разные файлы.
Я хочу, чтобы # #pragma once
(или что-то в этом роде) была в стандарте. Включить охранников не очень-то большое дело (но они кажутся немного трудными для объяснения людям, изучающим язык), но это похоже на небольшое раздражение, которого можно было бы избежать.
Фактически, с 99,98% времени, поведение #pragma once
– это желаемое поведение, было бы неплохо, если бы предотrotation множественного включения заголовка автоматически обрабатывалось компилятором, с #pragma
или чем-то, что позволило бы включить двойное включение.
Но у нас есть то, что у нас есть (за исключением того, что у вас может не быть #pragma once
).
Я не знаю о каких-либо преимуществах производительности, но это, безусловно, работает. Я использую его во всех моих проектах на C ++ (если я использую компилятор MS). Я считаю, что это более эффективно, чем использование
#ifndef HEADERNAME_H #define HEADERNAME_H ... #endif
Он выполняет ту же работу и не заполняет препроцессор дополнительными макросами.
GCC поддерживает #pragma once
официально с версии 3.4 .
GCC поддерживает #pragma once
с 3.4, см. http://en.wikipedia.org/wiki/Pragma_once для дальнейшей поддержки компилятора.
Большой потенциал роста, который я вижу при использовании #pragma once
в отличие от включения охранников, заключается в том, чтобы избежать ошибок копирования / вставки.
Давайте посмотрим правде в глаза: большинство из нас вряд ли начнут новый заголовок с нуля, а просто скопируют существующий и модифицируют его в соответствии с нашими потребностями. Гораздо проще создать рабочий шаблон с помощью #pragma once
а не включать защитников. Чем меньше я должен изменить шаблон, тем меньше я могу столкнуться с ошибками. Наличие того же, что и охранник в разных файлах, приводит к странным ошибкам компилятора, и для выяснения того, что пошло не так, требуется некоторое время.
TL; DR: #pragma once
проще в использовании.
Я использую его, и я доволен этим, поскольку мне приходится вводить гораздо меньше, чтобы создать новый заголовок. Он отлично работал для меня на трех платформах: Windows, Mac и Linux.
У меня нет информации о производительности, но я считаю, что разница между #pragma и включенным защитником будет ничем не отличаться от медленности parsingа грамматики C ++. Это настоящая проблема. Попробуйте собрать такое же количество файлов и строк с помощью компилятора C #, например, чтобы увидеть разницу.
В конце концов, использование охраны или прагмы не будет иметь никакого значения.
Использование « #pragma once
» может не иметь никакого эффекта (оно не поддерживается повсеместно – хотя оно все чаще поддерживается), поэтому вам нужно использовать условный код компиляции в любом случае, и в этом случае зачем беспокоиться о « #pragma once
»? Компилятор, вероятно, оптимизирует его. Однако это зависит от ваших целевых платформ. Если все ваши цели поддерживают его, тогда идите и используйте его – но это должно быть сознательное решение, потому что все ад сломается, если вы используете только прагму, а затем порт для компилятора, который его не поддерживает.
Не всегда.
http://gcc.gnu.org/bugzilla/show_bug.cgi?id=52566 имеет хороший пример двух файлов, предназначенных для обоих включений, но по ошибке считается идентичным из-за идентичных временных меток и содержимого (не идентичного имени файла) ,
Преимущество производительности состоит в том, что не нужно повторно открывать файл после того, как однажды была прочитана надпись #pragma. С защитой компилятор должен открыть файл (который может быть дорогостоящим во времени), чтобы получить информацию о том, что он не должен включать его содержимое снова.
Это теория только потому, что некоторые компиляторы автоматически не открывают файлы, для которых не было никакого кода чтения, для каждой единицы компиляции.
В любом случае, это не относится ко всем компиляторам, поэтому в идеале #pragma следует избегать для кросс-платформенного кода, если он вообще не является стандартным / не имеет стандартизованного определения и эффекта. Однако, практически, это действительно лучше, чем охранники.
В конце концов, лучшее предложение, которое вы можете получить, чтобы быть уверенным, что у вас есть максимальная скорость от вашего компилятора без необходимости проверять поведение каждого компилятора в этом случае, заключается в использовании как прагмы, так и защитных устройств.
#ifndef NR_TEST_H #define NR_TEST_H #pragma once #include "Thing.h" namespace MyApp { // ... } #endif
Таким образом, вы получаете лучшее из обоих (кросс-платформенная и вспомогательная скорость компиляции).
Поскольку это длиннее для ввода, я лично использую инструмент, который поможет сгенерировать все это очень фитильным способом (Visual Assist X).
Используя gcc 3.4 и 4.1 на очень больших деревьях (иногда используя distcc ), я пока не вижу никакой скорости при использовании #pragma один раз вместо или в сочетании со стандартными включенными защитными устройствами.
Я действительно не вижу, как его ценность может сбить с толку более старые версии gcc или даже других компиляторов, поскольку нет реальной экономии. Я не пробовал все различные делинтеры, но я готов поспорить, что это смутит многих из них.
Я тоже хочу, чтобы он был принят на ранней стадии, но я вижу аргумент «Зачем нам это нужно, когда ifndef работает отлично?». Учитывая множество темных углов и сложностей C, включите охранники – одно из самых легких, объясняющих себя вещей. Если у вас есть небольшое представление о том, как работает препроцессор, они должны быть понятны.
Однако, если вы заметили значительную скорость, обновите свой вопрос.
Сегодня старая школа включает в себя охранников так же быстро, как и # прагма. Даже если компилятор не относится к ним специально, он все равно остановится, когда он увидит, что определено #ifndef WHATEVER и WHATEVER. Открытие файла сегодня грязно. Даже если бы было улучшение, это было бы порядка милисекунд.
Я просто просто не использую #pragma один раз, так как он не имеет никакой пользы. Чтобы избежать столкновения с другими включенными охранниками, я использую что-то вроде: CI_APP_MODULE_FILE_H -> CI = инициалы компании; APP = название приложения; остальное самоочевидно.
Основное различие заключается в том, что компилятор должен был открыть файл заголовка, чтобы прочитать защитный код include. Для сравнения, прагма однажды заставляет компилятор отслеживать файл и не делать ни одного файла IO, когда он встречается с другим, для одного и того же файла. Хотя это может показаться незначительным, оно может легко масштабироваться с помощью огромных проектов, особенно без хорошего заголовка, включая дисциплины.
Тем не менее, в наши дни компиляторы (включая GCC) достаточно умен, чтобы лечить включенные охранники, такие как прагма один раз. т.е. они не открывают файл и не штрафуют файл IO.
В компиляторах, которые не поддерживают прагму, я видел ручные реализации, которые немного громоздки.
#ifdef FOO_H #include "foo.h" #endif
Мне лично нравится #pragma когда-то подход, поскольку это позволяет избежать проблем с именами коллизий и потенциальными ошибками опечатки. Это также более элегантный код для сравнения. Тем не менее, для переносимого кода не должно быть вреда, если и компилятор не жалуется на это.
Если мы используем msvc или Qt (вплоть до Qt 4.5), так как GCC (до 3.4), msvc поддерживают #pragma once
, я не вижу причин не использовать #pragma once
.
Имя исходного файла обычно совпадает с тем же именем classа, и мы знаем, что когда-то нам нужен рефакторинг , чтобы переименовать имя classа, тогда нам также пришлось изменить #include XXXX
, поэтому я считаю, что руководство по эксплуатации #include xxxxx
– это не умная работа. даже с расширением Visual Assist X, поддержка «xxxx» не является необходимой работой.
Дополнительная заметка для людей, думающих, что всегда требуется автоматическое однократное включение заголовочных файлов: я создаю генераторы кода, используя двойные или множественные включения файлов заголовков с десятилетий. Специально для создания ящиков библиотеки протоколов мне очень удобно иметь чрезвычайно портативный и мощный генератор кода без каких-либо дополнительных инструментов и языков. Я не единственный разработчик, использующий эту схему, как показано в этом блоге X-Macros . Без пропущенной автоматической защиты это было бы невозможно.