Объединение заголовочных файлов C ++ - PullRequest
7 голосов
/ 07 декабря 2011

Существует ли автоматизированный способ взять большое количество заголовочных файлов C ++ и объединить их в один?

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

По сути, я ищу что-то, что позволило бы мне распределить мою библиотеку в двух файлах (libfoo.h, libfoo.a),вместо текущей связки включаемых файлов + двоичная библиотека.

Ответы [ 7 ]

9 голосов
/ 07 декабря 2011

Как ваш комментарий говорит:

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

Тогда вы могли бы просто потратить некоторое время, включая все ваши заголовки в заголовке «оболочки», в правильном порядке. 50 заголовков не так уж много. Просто сделайте что-то вроде:

// libfoo.h
#include "header1.h"
#include "header2.h"
// ..
#include "headerN.h"

Это не займет много времени, если вы сделаете это вручную.

Кроме того, добавление новых заголовков позже - считанные секунды, чтобы добавить их в этот «заголовок оболочки».

На мой взгляд, это самое простое, чистое и работающее решение.

2 голосов
/ 07 декабря 2011

То, что вы хотите сделать, звучит для меня "javascriptish" :-). Но если вы настаиваете, всегда есть «кошка» (или эквивалент в Windows):

$ cat file1.h file2.h file3.h > my_big_file.h 

Или, если вы используете gcc, создайте файл my_decent_lib_header.h со следующим содержимым:

#include "file1.h"
#include "file2.h"
#include "file3.h"

и затем используйте

$ gcc -C -E my_decent_lib_header.h -o my_big_file.h

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

Что касается того, насколько это автоматически для вашего порядка файлов, ну, это совсем не так; Вы должны решить заказ самостоятельно. На самом деле, я был бы удивлен, узнав, что для C / C ++ может быть создан инструмент, который корректно упорядочивает зависимости заголовка во всех случаях.

1 голос
/ 07 декабря 2011

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

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

  • GCC создает генератор зависимостей опции препроцессора как -M -MD -MF и т. Д., С другой ручной скрипт, сортирующий их
  • дорогие коммерческие инструменты статического анализа, такие как coverity
  • настройка компилятора через плагины или (для GCC 4.6) MELT extensions

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

1 голос
/ 07 декабря 2011

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

Если нет, то вы должны сами его написать или попробовать наугад.Автоматическое отслеживание зависимостей в C ++ - hard .Как в «давайте посмотрим, вызывает ли этот экземпляр объекта неявное создание класса аргумента» трудно.Единственный автоматизированный способ, который я вижу, - это перемешать включаемые файлы в случайном порядке, посмотреть, скомпилируется ли весь пакет, и переставлять их до тех пор, пока он не скомпилируется.Который возьмет!время, возможно, вам лучше написать файл, включающий файл вручную.

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

0 голосов
/ 04 декабря 2017

Немного поздно, но вот оно.Я только недавно наткнулся на эту же проблему и сам написал это решение: https://github.com/rpvelloso/oneheader

Как это работает?

  1. Папка вашего проекта сканируется на CЗаголовки / C ++ и создан список найденных заголовков;

  2. Для каждого заголовка в списке он анализирует свои директивы #include и собирает граф зависимостей следующим образом:

    1. Если включенный заголовок не находится в папке проекта, он игнорируется (например, если это системный заголовок);

    2. Если включенный заголовокнаходится в папке проекта, затем на графике зависимостей создается ребро, связывающее включенный заголовок с текущим анализируемым заголовком;

  3. Граф зависимости топологическиотсортировано, чтобы определить правильный порядок объединения заголовков в один файл.Если на графике обнаружен цикл, процесс прерывается (т. Е. Если это не DAG);

Ограничения:

  1. В настоящее время это толькообнаруживает однострочные директивы #include (например, #include);
  2. Не обрабатывает заголовки с одинаковыми именами в разных путях;
  3. Это только дает вам правильный порядок объединения всех заголовков, вам все равно нужно объединить их (возможно, вы захотите удалить или изменить некоторые из них перед объединением).

Компиляция:

g ++ -Wall -ggdb -std = c ++ 1y -lstdc ++ fs oneheader.cpp -o oneheader [.exe]

Использование:

. / Oneheader [.exe] project_folder /> file_sequence.txt

0 голосов
/ 07 декабря 2011

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

Чтобы упростить этот скрипт / программу, полезно иметь структуру заголовков и включить гигиену в верхнюю форму.

  • Ваша программа / сценарий должны будут знать ваши пути обнаружения (подсказка: по возможности уменьшите количество путей поиска до одного).

  • Запустите сценарий или программу (которую вы создаете), чтобы заменить директивы include содержимым заголовочного файла.

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

  • Хорошо иметь фазу / трансляцию сборки, которая включает в себя только заголовок и выдает нулевые предупреждения или ошибки (предупреждения как ошибки).

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

0 голосов
/ 07 декабря 2011

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

Краткий ответ на ваш главный вопрос:

  • номер

Мои предложения:

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

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

  • не выстраивайте глубоко вложенную иерархию включений. Это крайне затрудняет обзор содержимого каждого включенного вами бита. Пользователь вашей библиотеки заглянет в заголовок, чтобы узнать, как его использовать. И он, вероятно, не сможет отличить соответствующий код от неактуального на первый взгляд. Вы хотите максимизировать соотношение релевантного кода к общему коду в главном заголовке вашей библиотеки.

EDIT

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

import glob
with open("convenience_header.h", 'w') as f:
  for header in glob.glob("*.h"):
    f.write("#include \"%s\"\n" % header)
...