Версия для печати темы
Нажмите сюда для просмотра этой темы в оригинальном формате
Форум программистов > C/C++: Для новичков > [FAQ] Множественное включение header'ов


Автор: archimed7592 29.12.2007, 20:23

M
archimed7592
Сама статья теперь здесь.

Цитата(JackYF @  29.12.2007,  20:00 Найти цитируемый пост)
Почему так происходит?

Хорошо бы ещё привести в коде оба этапа:
1. когда были включены и a и b.
2. когда снова был включен a(уже из b).


Цитата(JackYF @  29.12.2007,  20:00 Найти цитируемый пост)
Решение:

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

Цитата(JackYF @  29.12.2007,  20:00 Найти цитируемый пост)
Некоторые компиляторы С/С++ предоставляют ещё одно решение проблемы.

Какие именно (кроме MSVC).
Хорошо было бы перечислить их. Не обязательно все(если их очень много, в чём я немного сомневаюсь).


В общем и целом всё гут. Молодец smile.

Автор: JackYF 29.12.2007, 20:36
Цитата(archimed7592 @  29.12.2007,  20:23 Найти цитируемый пост)
Хорошо бы ещё привести в коде оба этапа:
1. когда были включены и a и b.
2. когда снова был включен a(уже из b).

мм... не понял, поясни? прокомментировать инклюды в main.cpp?


Цитата(archimed7592 @  29.12.2007,  20:23 Найти цитируемый пост)
После описания решения взять код в котором будет уже всё "включено" и построчно закомментировать мол "здесь A_H неопределён и поэтому этот кусок не будет выкинут". Потом показать окончательный код, когда уже всё выкинуто.

а... имеется в виду - продемонстрировать работу #include'ов за компилятор?
Цитата(archimed7592 @  29.12.2007,  20:23 Найти цитируемый пост)
Какие именно (кроме MSVC).
Хорошо было бы перечислить их. Не обязательно все

MSVC, Builder, g++. Ещё? Эти я укажу.

Автор: archimed7592 29.12.2007, 20:39
Цитата(JackYF @  29.12.2007,  20:36 Найти цитируемый пост)
а... имеется в виду - продемонстрировать работу #include'ов за компилятор?

Угумс. Многим это непонятно. В частности, многие считают, что между именем заголовочного файла и именем модуля трансляции есть какая-то магическая связь.
Цитата(JackYF @  29.12.2007,  20:36 Найти цитируемый пост)
MSVC, Builder, g++. Ещё? Эти я укажу.

Укажи обязательно. Насчёт "ещё?" - не знаю, ибо конструкцией этой не пользуюсь smile.

Автор: JackYF 29.12.2007, 20:48
Цитата(archimed7592 @  29.12.2007,  20:39 Найти цитируемый пост)
ибо конструкцией этой не пользуюсь

Я тоже. Но в кодах новичков встречал.

Цитата(archimed7592 @  29.12.2007,  20:39 Найти цитируемый пост)
В частности, многие считают, что между именем заголовочного файла и именем модуля трансляции есть какая-то магическая связь.

Таки да. Хорошо, скоро сделаю.

Автор: JackYF 29.12.2007, 21:15
Сделал.

Автор: archimed7592 29.12.2007, 21:21
Цитата(JackYF @  29.12.2007,  20:00 Найти цитируемый пост)
компилятор от MS Visual Studio, компилятор от Borland C++ Builder, g++.

компилятор из MS Visual Studio(MSVC), компилятор Borland C++ Builder, компилятор g++ из GCC.

Добавлено через 2 минуты и 32 секунды
Ну а в остальном, как я уже сказал, всё гут. Пускай повисит до "после НГ" - если критики никакой не будет, то добавим в FAQ.

Автор: maxim1000 29.12.2007, 22:19
можно ещё в func_b() добавить параметр point *x, например
чтобы яснее было, зачем в h-файле может понадобиться включение другого

Добавлено @ 22:28
кстати, касательно #pragma once
надёжен ли он?
ведь далеко не всегда компилятор может автоматически определить, нужно ли два файла считать одинаковыми (я думаю, несложно придумать два примера, отличающиеся только семантикой и требующие разного поведения)
это я к тому, что, может, стоит предостеречь о возможных трудностях, чтобы уменьшить тягу к дурным привычкам smile

Автор: JackYF 29.12.2007, 22:47
Цитата(maxim1000 @  29.12.2007,  22:19 Найти цитируемый пост)
можно ещё в func_b() добавить параметр point *x, например
чтобы яснее было, зачем в h-файле может понадобиться включение другого

да, можно. Если никто не против, добавляю.

Цитата(maxim1000 @  29.12.2007,  22:19 Найти цитируемый пост)
кстати, касательно #pragma once
надёжен ли он?

Он нестандартен, поэтому не очень рекомендуется к использованию. Другое дело, что насчёт ненадёжности я весьма сомневаюсь - что мешает препроцессору это самое #pragma once тупо преобразовывать в #ifndef...#define...#endif?


Автор: maxim1000 30.12.2007, 00:50
Цитата(JackYF @  29.12.2007,  22:47 Найти цитируемый пост)
что мешает препроцессору это самое #pragma once тупо преобразовывать в #ifndef...#define...#endif?

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

Автор: chipset 30.12.2007, 10:42
Все MSVC поддерживают pragma once начиная c  _MCS_VER 1000. ИМХО, pragma once это нормально и нечего всякое старье поддерживать smile  Давайте ещё тогда namespace уберем, их Turbo C++ не держит.

А статья прикольная, +1!

Автор: bsa 30.12.2007, 11:34
Цитата(chipset @ 30.12.2007,  10:42)
Все MSVC поддерживают pragma once начиная c  _MCS_VER 1000. ИМХО, pragma once это нормально и нечего всякое старье поддерживать smile  Давайте ещё тогда namespace уберем, их Turbo C++ не держит.

А статья прикольная, +1!

Что стандарт говорит про #pragma once? Если он ничего говорит, то и использовать не стоит в серьезных проектах, имхо.

Автор: chipset 30.12.2007, 11:50
А что стандарт говорит по поводу Win32API с ихними FAR поинтерами? smile

Автор: MAKCim 30.12.2007, 12:01
Цитата(maxim1000 @  30.12.2007,  00:50 Найти цитируемый пост)
здесь возникает вопрос идентификатора, используемого в #ifndef и #endif
например, может получиться так, что в двух разных местах окажутся два файла с одинаковыми именами
(ситуация не очень правильная, но возможная)
и оба они должны будут использоваться в программе

#pragma once гарантирует семантику #ifndef #define #endif
как он это делает - детали реализации

Автор: bsa 30.12.2007, 12:03
Цитата(chipset @ 30.12.2007,  11:50)
А что стандарт говорит по поводу Win32API с ихними FAR поинтерами? smile

WIN32API - это всего лишь библиотека со своими макроопределениями и пр. А не расширения языка. А вот #pragma once - это расширение. Есть гарантия, что оно работает на всех компиляторах поддерживающих текущий стандарт? Нет. Значит рекомендовать к использованию не стоит.

Автор: Mayk 30.12.2007, 12:06
Статья полезно, одно но.
Длинная очень. Не знаю как ужать, но читать это сложно.
Можно сэкономить пару строк выкинув func_a и изменив func_b(void) на func( struct point *pt );


Цитата(JackYF @  30.12.2007,  00:00 Найти цитируемый пост)

Описание структуры point, итого, вставится в main.cpp два раза: 1-й раз - явно - при включении файла a.h, второй раз - неявно - при включении файла b.h, так как файл b.h тоже включает в себя содержимое файла a.h.

имхо "явно"/"неявно" здесь лишни.

Цитата(chipset @  30.12.2007,  15:50 Найти цитируемый пост)
А что стандарт говорит по поводу Win32API с ихними FAR поинтерами?

а хто это? помню что в досе ближние от дальных отличались. А вот после препроцессирования
Код

int main(){
    printf("%d\n", sizeof(DWORD FAR * ) );
    printf("%d\n", sizeof(DWORD * ) );
}

в bcc551 получаем что нет никаких FAR поинтеров
Код

* a.cpp 4: */int main(){
/* a.cpp 5: */printf("%d\n", sizeof(DWORD  * ) );
/* a.cpp 6: */printf("%d\n", sizeof(DWORD * ) );
/* a.cpp 7: */}


Автор: maxim1000 30.12.2007, 13:02
Модераторам: может, выделить обсуждение #pragma once в свою тему? А то уже, ИМХО, оффтопик пошёл, но пообсуждать интересно smile
Цитата(MAKCim @  30.12.2007,  12:01 Найти цитируемый пост)
#pragma once гарантирует семантику #ifndef #define #endif
как он это делает - детали реализации

во-первых, насколько я знаю, #pragma once не является чем-то стандартизированным
а это значит, что каждая реализация, которая её реализует, реализует её в своём понимании
поэтому "всё остальное - детали реализации" звучит несколько странно: #pragma once целиком - деталь реализации

во-вторых она попросту не может гарантировать семантику #ifndef-#define-#endif, т.к. у неё недостаточно информации чётко ответить на вопрос "являются ли файлы одинаковыми?"
а если быть более точным - "нужно ли для заданной пары заголовков оставлять только один?"

небольшой пример:
у меня установлен VC++2005 Express, на нём я попробовал такое:
создал проект
в его директории создал ещё одну qqq
и создал три файла:
main.cpp:
Код

#include "aaa.h"
#include "qqq/aaa.h"
int main()
{
    return 0;
}

aaa.h (в директории проекта)
Код

#pragma once
int x;

qqq/aaa.h
Код

#pragma once
int x;

при компиляции получил ошибку двойного определения "x"

судя по этому, VC++ сравнивает файлы по их полному имени (с путём)
значит, эти два файла для неё - разные
однако, вполне возможна такая ситуация, окгда программист будет таскать с собой, к примеру, какой-нибудь файлик с нужными вещами, который он использует везде, а когда начнёт объединять какие-то два проекта, полезут ошибки

зато если бы использовались #ifndef AAA_H_ #define AAA_H_ ... #endif, никаких ошибок не было бы

--------------------------------
конечно, можно возразить: "ну не знаю, что там VC++, вот мой компилятор в даном случае будет считать их одним файлом"
это чудесно, но та же ситуация могла бы случиться, когда эти файлы семантически разные, например, какой-нибудь config.h в разных поддиректориях может конфигурировать разные части программы
в данном случае уже ошибётся компилятор, который будет считать, что они одинаковые

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

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

Автор: JackYF 30.12.2007, 14:08
Цитата(Mayk @  30.12.2007,  12:06 Найти цитируемый пост)
Длинная очень.

Понимаю. Но я тоже не знаю, как особо ужать. Раскрыть тему желательно полнее, чтобы не оставалось вопросов по теме.

Цитата(Mayk @  30.12.2007,  12:06 Найти цитируемый пост)
имхо "явно"/"неявно" здесь лишни.

Хм... ну не знаю. Мне так не кажется. Камрады, что скажете? Оставлять/убирать?

Автор: archimed7592 30.12.2007, 17:38
Цитата(maxim1000 @  30.12.2007,  13:02 Найти цитируемый пост)
Модераторам: может, выделить обсуждение #pragma once в свою тему? А то уже, ИМХО, оффтопик пошёл, но пообсуждать интересно smile

Здесь обсуждайте - полезнее для статьи.

Цитата(JackYF @  30.12.2007,  14:08 Найти цитируемый пост)
Камрады, что скажете? Оставлять/убирать?

Убирать не нужно. Возможно, что стоит перефразировать, если сможете придумать как(у меня идей нет) smile.


Цитата(maxim1000 @  30.12.2007,  13:02 Найти цитируемый пост)
а когда начнёт объединять какие-то два проекта, полезут ошибки

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

Автор: maxim1000 30.12.2007, 21:11
Цитата(archimed7592 @  30.12.2007,  17:38 Найти цитируемый пост)
Если файлик общий, то его нужно выделить в одно место(общее для обоих проектов), что даст явные преимущества и, в том числе, не будет ошибки с сабжевой прагмой.

так я сразу написал, что при более-менее логичной структуре файлов проекта и их имён больших проблем быть не должно
но ведь далеко не всегда структура логична
особенно объединение двух проектов: далеко не всегда код обоих проектов может контролирвоаться (third party всякие), а эта конструкция добавит кучу ошибок, причин для которых в общем-то нету...

P.S.
всё это было к тому, что, может, стоит добавить какое-то предупреждение о возможных проблемах smile

Автор: archimed7592 30.12.2007, 21:19
Цитата(maxim1000 @  30.12.2007,  21:11 Найти цитируемый пост)
P.S.
всё это было к тому, что, может, стоит добавить какое-то предупреждение о возможных проблемах

Возможно, что лучше вообще не упоминать о прагме, либо написать отдельную статью о вреде прагмы и давать в ФАКе ссылочку на описание вреда прагм smile.

Автор: JackYF 3.1.2008, 14:20
Цитата(maxim1000 @  29.12.2007,  22:19 Найти цитируемый пост)
можно ещё в func_b() добавить параметр point *x, например

так... сделано.

Ещё замечания будут, или будем выкладывать?

Автор: archimed7592 3.1.2008, 14:22
Цитата(JackYF @  3.1.2008,  14:20 Найти цитируемый пост)
Ещё замечания будут, или будем выкладывать? 

Думаю, что не будет, но кто их знает smile.
Обожди выкладывать - сейчас у многих выходные.

Автор: JackYF 3.1.2008, 14:38
Цитата(archimed7592 @  3.1.2008,  14:22 Найти цитируемый пост)
Обожди выкладывать - сейчас у многих выходные. 

Хорошо, "обождеваю" smile

Автор: MAKCim 3.1.2008, 14:57
Цитата(maxim1000 @  30.12.2007,  13:02 Найти цитируемый пост)
во-первых, насколько я знаю, #pragma once не является чем-то стандартизированным
а это значит, что каждая реализация, которая её реализует, реализует её в своём понимании
поэтому "всё остальное - детали реализации" звучит несколько странно: #pragma once целиком - деталь реализации

если препроцессор поддерживает #pragma once
то значит он каким-то образом реализует семантику #ifndef #define #endif
а уж как - детали реализации в конкретном препроцессоре
нам об этом беспокоится не стоит
поведение будет ожидаемым
как можно реализовать? легко
#pragma once -> #ifndef NAME #define NAME #endif
где NAME - трансформируемый абсолютный путь к текущему заголовочному файлу
т. е для Linux, к примеру, если абсолютный путь к файлу - /home/user/include/math.h
то трансформируемый абсолютный путь - homeuserincludemathh
для Windows, если абсолютный путь - d:\user\include\math.h
то трансформируемый абсолютный путь - duserincludemathh
т. к в одной и той же директории не может быть двух файлов с одинаковым названием, то абсолютный путь уникально трансформируется в NAME

Автор: archimed7592 3.1.2008, 15:01
Цитата(MAKCim @  3.1.2008,  14:57 Найти цитируемый пост)
то абсолютный путь уникально трансформируется в NAME 

В данном случае не совсем уникально, ну да ладно smile.

Автор: MAKCim 3.1.2008, 15:08
maxim1000, 
+ твой пример не работает не из-за #pragma once
замени #pragma once на #ifndef #define #endif
у тебя все равно не будет работать
 smile 
если конечно в #define обоих файлов будет не одинаковый идентификатор
+ то, что ты показал примером - имхо, неправильно
потому что ты из  двух физически разных файлов хочешь сделать семантически эквивалентные

Добавлено через 1 минуту и 44 секунды
Цитата(archimed7592 @  3.1.2008,  15:01 Найти цитируемый пост)
В данном случае не совсем уникально, ну да ладно

в смысле?
приведи пример 2-ух разных файлов, для которых NAME будут одинаковые при использовании моего алгоритма

Автор: archimed7592 3.1.2008, 15:19
Цитата(MAKCim @  3.1.2008,  15:08 Найти цитируемый пост)
приведи пример 2-ух разных файлов, для которых NAME будут одинаковые при использовании моего алгоритма 

/home/user/math.h
и
/home/usermath.h
smile

Автор: bsa 3.1.2008, 15:20
/home/user/include/math.h
и
/home/user/include/m/ath.h

Добавлено через 18 секунд
не успел  smile

Добавлено через 11 минут и 24 секунды
как вариант, можно заменять спецсимволы на коды: _<HexCode>:
Код

/home/user/include/math.h -> HEADER_2Fhome_2Fuser_2Finclude_2Fmath_2Eh
/home/user/include/m_ath.h -> HEADER_2Fhome_2Fuser_2Finclude_2Fm_5Fath_2Eh

Автор: MAKCim 3.1.2008, 17:19
archimed7592, 
ага
согласен
но смысл от этого не меняется
ввести дополнительные правила
тогда будет точно все однозначно
 smile 

Автор: archimed7592 3.1.2008, 17:21
Цитата(MAKCim @  3.1.2008,  17:19 Найти цитируемый пост)
но смысл от этого не меняется

Да я сразу сказал:
Цитата(archimed7592 @  3.1.2008,  15:01 Найти цитируемый пост)
ну да ладно 


 smile 

Автор: MTWizard 8.1.2008, 17:03
Кстати, с каких это времён Builder поддерживает #pragma once?
Я писал на 6-м, 2006, 2007 - там эти прагмы просто игнорируются.

Автор: JackYF 8.1.2008, 17:06
Цитата(MTWizard @  8.1.2008,  16:03 Найти цитируемый пост)
Кстати, с каких это времён Builder поддерживает #pragma once?
Я писал на 6-м, 2006, 2007 - там эти прагмы просто игнорируются. 

да-а? странно, а чего тогда кодогенератор Билдера их в исходники вставляет? просто так?

Автор: bsa 8.1.2008, 17:32
JackYF
#pragma once не видел ни разу.
а вот #pragma argsused и #pragma hdrstop встречаются.

Автор: MTWizard 8.1.2008, 18:36
Вот именно. Только что перепроверил. C++ Builder 2007 Update 2.
Файлы:

File.cpp:
Код

#pragma hdrstop

#include "File.h"
#include "File.h"

//---------------------------------------------------------------------------

#pragma argsused
int main(int argc, char* argv[])
{
    return 0;
}


File.h:
Код

#pragma once

int x = 0;


Вылезает ошибка:
Код

[BCC32 Error] File.h(3): E2238 Multiple declaration for 'x'

Стоит добавить header guard, как ошибка устраняется. 

Вывод: Builder не поддерживает #pragma once

Автор: JackYF 8.1.2008, 20:44
Цитата(MTWizard @  8.1.2008,  17:36 Найти цитируемый пост)
Вывод: Builder не поддерживает #pragma once

оппа. Приехали... Значит, надо убирать из списка, скоро поправлю.

MTWizard, спасибо за информацию.

Автор: SergeCpp 9.1.2008, 09:54
Некоторая информация относительно #include, include guards, #pragma once, скорости компилирования etc.

http://gamearchitect.net/Articles/ExperimentsWithIncludes.html

http://www.gamesfromwithin.com/articles/0501/000067.html

http://www.convexhull.com/mt/mt-comments.cgi?entry_id=67

Автор: JackYF 9.1.2008, 10:12
SergeCpp, почитал, спасибо. Правда, к самому FAQ'у это имеет мало отношения.

Автор: archimed7592 10.1.2008, 11:27
Так замечаний больше вроде нет. Главред Евгений, статья на Ваш взгляд готова? Можно приступать к окончательной вычитке мною? smile

Автор: JackYF 10.1.2008, 12:08
Цитата(JackYF @  8.1.2008,  19:44 Найти цитируемый пост)
Значит, надо убирать из списка, скоро поправлю.

Итак, это поправил, Билдер из списка убрал.

Цитата(archimed7592 @  10.1.2008,  10:27 Найти цитируемый пост)
Можно приступать к окончательной вычитке мною?

 smile 

Автор: warmonger_ 10.1.2008, 13:53
Цитата(archimed7592 @ 29.12.2007,  20:39)
Цитата(JackYF @  29.12.2007,  20:36 Найти цитируемый пост)
а... имеется в виду - продемонстрировать работу #include'ов за компилятор?

Угумс. Многим это непонятно. В частности, многие считают, что между именем заголовочного файла и именем модуля трансляции есть какая-то магическая связь.
Цитата(JackYF @  29.12.2007,  20:36 Найти цитируемый пост)
MSVC, Builder, g++. Ещё? Эти я укажу.

Укажи обязательно. Насчёт "ещё?" - не знаю, ибо конструкцией этой не пользуюсь smile.

а какой конструкцией нужно пользоваться? есть альтернатива? ( кроме сказаного #pragma once)

Автор: JackYF 10.1.2008, 14:44
Цитата(warmonger_ @  10.1.2008,  12:53 Найти цитируемый пост)
кроме сказаного #pragma once

ты статью вообще читал? она почти готова, прочитай, пожалуйста. Первый пост в теме.

Автор: archimed7592 10.1.2008, 16:50

M
archimed7592
Закрыто(для окончательного оформления).

Автор: warmonger_ 10.1.2008, 20:26
Цитата(JackYF @ 10.1.2008,  14:44)
Цитата(warmonger_ @  10.1.2008,  12:53 Найти цитируемый пост)
кроме сказаного #pragma once

ты статью вообще читал? она почти готова, прочитай, пожалуйста. Первый пост в теме.

Первый пост читал. Я имел ввиду кроме #pragma once, #ifndef...#define...#endif.
Извините, я неверное не так понял.

Автор: archimed7592 10.1.2008, 20:35
Цитата(warmonger_ @  10.1.2008,  20:26 Найти цитируемый пост)
Извините, я неверное не так понял. 

Наверняка.


M
archimed7592
Любуемся результатом :).

Автор: bsa 10.1.2008, 21:02
Ну вот. Все-таки в другое измерение засунули...

archimed7592, сделай хотя бы ссылку в прикрепленной теме на форуме на весь FAQ. А то новички туда вряд ли пойдут.

Автор: JackYF 10.1.2008, 21:06
Цитата(warmonger_ @  10.1.2008,  19:26 Найти цитируемый пост)
#ifndef...#define...#endif.

что в этом способе тебя не устраивает?

Добавлено через 8 минут и 44 секунды
archimed7592, я увидел вот это в середине поста. Что ты хотел сделать? smile
[code=nocolor]a.h:1: error: redefinition of ‘struct point’[/QUOTE]

Добавлено через 13 минут и 26 секунд
уже поправил последнее.

Автор: archimed7592 10.1.2008, 21:30
Цитата(bsa @  10.1.2008,  21:02 Найти цитируемый пост)
Ну вот. Все-таки в другое измерение засунули...

archimed7592, сделай хотя бы ссылку в прикрепленной теме на форуме на весь FAQ. А то новички туда вряд ли пойдут.

Будет, будет. Как только хотя бы текущие статьи(те что в разработке) завершат, так сразу и будет(я уже об этом думал).


Цитата(JackYF @  10.1.2008,  21:06 Найти цитируемый пост)
archimed7592, я увидел вот это в середине поста. 

Ooopps smile.

Автор: warmonger_ 10.1.2008, 21:35
Цитата(JackYF @  10.1.2008,  21:06 Найти цитируемый пост)
что в этом способе тебя не устраивает?

я же уже написал, что не правильно понял

Powered by Invision Power Board (http://www.invisionboard.com)
© Invision Power Services (http://www.invisionpower.com)