Отменить включаемые файлы в C ++ - PullRequest
23 голосов
/ 28 ноября 2010

Предположим, у меня есть следующий код (буквально) в исходном файле C ++:

// #include <iostream> // superfluous, commented-out
using std::cout;
using std::endl;

int main()
{
    cout << "Hello World" << endl;
    return 0;
}

Я могу скомпилировать этот код, даже если #include <iostream> закомментирован:

g++ -include my_cpp_std_lib_hack source.cpp

Где my_cpp_std_lib_hack - это файл в каком-то центральном месте, который включает в себя все файлы стандартной библиотеки C ++:

#include <ciso646>
#include <climits>
#include <clocale>
...
#include <valarray>
#include <vector>

Конечно, я могу использовать надлежащие параметры компиляции для всех нужных мне компиляторов (это MS VisualStudio и, возможно, несколько других), и я также использую предварительно скомпилированные заголовки.

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

  • Быстрая компиляция (потому что вся Стандартная библиотекапрекомпилировано)
  • Нет необходимости добавлять #include s, когда все, что я хочу, - это добавить какой-нибудь отладочный вывод
  • Нет необходимости постоянно вспоминать или искать, где объявлен хек std::max
  • Ощущение, что STL магически встроен в язык

Так что мне интересно: я что-то здесь не так делаю?

Будет ли этот хак сломатьсяпри написаниикрупные проекты?

Может быть, все остальные уже используют это, и никто не сказал мне?

Ответы [ 7 ]

23 голосов
/ 28 ноября 2010

Итак, я задаюсь вопросом: я что-то здесь не так делаю?

Да.Конечно, ваши заголовки предварительно скомпилированы, но компилятор все равно должен выполнять такие вещи, как поиск имен по всей включенной массе, что замедляет компиляцию.1008 *

Да, это в значительной степени проблема.Кроме того, если кто-то еще посмотрит на этот код, он будет удивлен, откуда взялся std::cout (ну, предположим, это пользовательский тип).Без #include они вообще не будут знать.

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

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

15 голосов
/ 28 ноября 2010

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

Другим программистам не нужно ломать голову над вашими Make-файлами (или файлами Ant, или рабочими пространствами Eclipse, или чем-то еще), чтобы выяснить, как все работает.

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

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

4 голосов
/ 28 ноября 2010

Забудьте об ускорении компиляции - насколько я слышал, предварительно скомпилированный заголовок с шаблонами на самом деле не "предварительно скомпилирован", за исключением имени и анализа. Я не поверю в скорость компиляции, пока не увижу ее в тестах. :)

Что касается полезности:

Я предпочитаю иметь IDE, которая обрабатывает мои включения для меня (это все еще плохо для C ++, но Eclipse уже добавляет известные включения с помощью ctrl + shift + n с ... ну, приемлемая надежность:)).

2 голосов
/ 29 ноября 2010

Из руководства GCC :

-include file

Обрабатывать файл так, как будто #include «file» появилось в первой строке первичный исходный файл. Тем не менее Первый каталог для поиска файла рабочий каталог препроцессора вместо каталога, содержащего основной исходный файл. Если не найден там это ищется в остаток от #include поиска "..." цепь как обычно.

То, что вы делаете, по сути эквивалентно началу каждого файла со строки

#include "my_cpp_std_lib_hack"

, что делает Visual Studio, когда собирает часто включаемые файлы в stdafx.h. В этом есть некоторые преимущества, описанные другими, но ваш подход скрывает это, включая процесс сборки, чтобы никто, кто смотрел непосредственно на один из ваших исходных файлов, не знал об этой скрытой магии. Делать ваш код непрозрачным таким способом не кажется мне хорошим стилем, поэтому, если вы заинтересованы во всех преимуществах предварительно скомпилированных заголовков, я предлагаю вам явно включить ваш файл взлома.

2 голосов
/ 29 ноября 2010

Выполнение «тайных» включений также может затруднить тестирование.Вы хотите скомпилировать минимально возможное подмножество кода при тестировании определенного компонента.Выяснить, что это подмножество является , было бы трудно, если бы заголовки / источники не были честны относительно своих зависимостей, поэтому вы, вероятно, просто перетаскиваете свой my_cpp_std_lib_hack в каждый модульный тест.Это увеличит время компиляции ваших тестовых наборов lot .Установленные кодовые базы часто имеют в три раза больше тестового кода, чем обычный код, поэтому это может стать проблемой по мере роста вашей кодовой базы.

0 голосов
/ 29 ноября 2010

Мы стараемся не включать неиспользуемые или даже редко используемые вещи, например, в VC ++ есть

#define WIN32_LEAN_AND_MEAN //exclude rarely used stuff

и что мы ненавидим в MFC, так это то, что если вы хотите создать простое приложение, вы создадите большой исполняемый файл со всей библиотекой (если он статически связан), поэтому не очень хорошая идея, если вы хотите использовать только cout а другой нет ??

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

#pragma (comment, "xxx.lib")

, чем использовать его в командной строке, это напоминает мне, по крайней мере, с каким файлом я хочу

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

0 голосов
/ 29 ноября 2010

Вы делаете что-то очень неправильно. Вы эффективно включаете много заголовков, которые могут не понадобиться. В общем, это очень плохая идея, потому что вы создаете ненужные зависимости, и изменение любого заголовка потребует перекомпиляции всего. Даже если вы избегаете этого, используя предварительно скомпилированные заголовки, вы все равно ссылаетесь на множество объектов, которые вам могут не понадобиться, делая ваш исполняемый файл намного больше, чем нужно.

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

...