C ++ цепочка зависимостей заголовок-реализация-заголовок-реализация - PullRequest
3 голосов
/ 25 сентября 2011

Я пытаюсь создать простой инструмент инкрементной сборки C ++ с определителем зависимостей. Я запутался в одной проблеме с процессом сборки cpp. Представьте, что у нас библиотека состоит из нескольких файлов:

// h1.h
void H1();

// s1.cpp
#include "h1.h"
#include "h2.h"
void H1(){ H2(); }

// h2.h
void H2();

// s2.cpp
#include "h2.h"
#include "h3.h"
void H2(){ /*some implementation*/ }
void H3(){ /*some implementation*/ }

// h3.h
void H3();

В коде клиента, включая h1.h

// app1.cpp
#include "h1.h"
int main()
{
  H1();
  return 0;
}

существует неявная зависимость реализации s2.cpp: our_src -> h3 -> s1 -> h2 -> s2. Поэтому нам нужно связать два файла obj:

g++ -o app1 app1.o s1.o s2.o

В отличие от включенного h3.h

// app2.cpp
#include "h3.h"
int main()
{
  H3();
  return 0;
}

есть только одна исходная зависимость: our_src -> h3 -> s2

Поэтому, когда мы включаем h3.h, нам нужен только скомпилированный s2.cpp (несмотря на включение s1.cpp -> h2.h):

g++ -o app2 app2.o s2.o

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

Итак, мой вопрос: есть ли способ или инструменты, чтобы выяснить, какое включение заголовка можно было бы опустить при проверке зависимостей (без разбора CPP)?

Буду признателен за любой ответ.

Ответы [ 3 ]

2 голосов
/ 25 сентября 2011

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

Кстати, что касается языка, нет разницы между тем, что вы можете поместить в заголовочный файл или в файл реализации. Директива #include не работает на уровне C ++, это просто текстовая макрофункция без какого-либо понимания языка. Более того, даже анализ «просто» объявлений C ++ является настоящим кошмаром (трудная часть синтаксиса C ++ - это объявления, а не операторы / выражения).

Может быть, вы можете использовать результат gccxml , который анализирует файлы C ++ и возвращает структуру данных XML, которую можно проверить.

0 голосов
/ 07 апреля 2014

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

Примеры

Включение в класс потока файла

Thread.h нуждается в потоке.o во время ссылки

#ifdef __WAND__
dependency[thread.o]
target[name[thread.h] type[include]]
#endif

Реализация класса потока в windows (thread-win32.cpp)

Этот файл должен компилироваться, только если Windows является целевой платформой

#ifdef __WAND__
target[name[thread.o] type[object] platform[;Windows]]
#endif

Реализация класса потока в GNU / Linux (thread-linux.cpp)

Этот файл должен компилироваться, только если GNU / Linux является целевой платформой.В GNU / Linux для связывания необходима внешняя библиотека pthread.

#ifdef __WAND__
target
    [
    name[thread.o] type[object] platform[;GNU/Linux]
    dependency[pthread;external]
    ]
#endif

Плюсы и минусы

Плюсы

  • Палочка может быть расширена для работы с другимиязыки программирования
  • Wand сохранит все необходимые данные, необходимые для успешного связывания новой программы, просто введя команду wand
  • В файле проекта не нужно упоминать никаких зависимостей, поскольку они хранятся в исходном коде.files

Cons

  • Wand требует дополнительных директив в каждом исходном файле
  • Этот инструмент еще не широко используется авторами библиотек
0 голосов
/ 25 сентября 2011

Это не простая проблема.Просто пара вещей, которые делают это трудным:

  1. Что если один заголовочный файл реализован в N> 1 исходных файлах?Например, предположим, что class Foo определено в foo.h, но реализовано в foo_cotr_dotr.cpp, foo_this_function.cpp и foo_that_function.cpp.
  2. Что если одна и та же возможность реализована в нескольких исходных файлах?Например, предположим, что Foo::bar() имеет реализации в foo_bar_linux.cpp, foo_bar_osx.cpp, foo_bar_sunos.cpp.Используемая реализация зависит от целевой платформы.

Одним из простых решений является создание разделяемой или динамической библиотеки и связывание этой библиотеки.Позвольте инструментальной цепочке разрешить эти зависимости.Проблема № 1 полностью исчезает, а проблема № 2 исчезает, если у вас достаточно умный make-файл.

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

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...