Автоматически отделять определения классов от объявлений? - PullRequest
3 голосов
/ 17 марта 2009

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

// foo.h
template<class T>
class Foo {
  Foo(){}
  void computeXYZ() { /* heavy code */ }
};
template<class T>
void processFoo(const Foo<T>& foo) { /* more heavy code */ }

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

Поскольку в качестве параметра шаблона я в любом случае использую только один или два типа, я планирую создать для каждого заголовочного файла библиотеки файл, содержащий только объявления , без тяжелого кода, например:

// NEW: fwd-foo.h
template<class T>
class Foo {
  Foo();
  void computeXYZ();
};
template<class T>
void processFoo(const Foo<T>& foo);

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

// NEW: foo.cpp
#include "foo.h"
template class Foo<int>;
template class Foo<double>;
template void processFoo(const Foo<int>& foo);
template void processFoo(const Foo<double>& foo);

Теперь я могу просто включить fwd-foo.h в мой код и иметь короткое время компиляции. Я свяжусь с foo.o в конце.

Недостатком, конечно, является то, что я должен сам создавать эти новые файлы fwd-foo.h и foo.cpp. И, конечно, это проблема обслуживания: когда выходит новая версия библиотеки, я должен адаптировать их к этой новой версии. Есть ли другие недостатки?

И мой главный вопрос:

Есть ли шанс, что я смогу создать эти новые файлы, особенно fwd-foo.h, автоматически из исходного foo.h? Я должен сделать это для многих библиотечных заголовочных файлов (может быть, 20 или около того), и автоматическое решение было бы лучше, особенно в случае выпуска новой версии библиотеки, и я должен сделать это снова с новой версией. Есть ли инструменты для этой задачи?

РЕДАКТИРОВАТЬ:

Дополнительный вопрос: Как в этом случае мне может помочь недавно поддерживаемое ключевое слово extern?

Ответы [ 4 ]

4 голосов
/ 17 марта 2009

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

Чтобы показать, как вы можете его использовать, обратите внимание на следующее:

// t.cc
#include "b.h"
#include "c.h"

template <typename T> 
class A {
  void foo () {
    C c;
    c.foo ();
    b.foo ();
  }
  B b;
}

Возьмите вышеуказанный файл и скопируйте его в файл 't.lzz'. Поместите любые директивы # include в отдельные блоки $ hdr и $ src по мере необходимости:

// t.lzz
$hdr
#include "b.h"
$end

$src
#include "c.h"
$end

template <typename T> 
class A {
  void foo () {
    C c;
    c.foo ();
    b.foo ();
  }
  B b;
}

Теперь, наконец, запустите lzz над файлом, указав, что он помещает определения шаблонов в исходный файл. Вы можете сделать это, используя $ pragma в исходном файле, или использовать параметр командной строки "-ts":

Это приведет к созданию следующих файлов:

// t.h
//

#ifndef LZZ_t_h
#define LZZ_t_h
#include "b.h"
#undef LZZ_INLINE
#ifdef LZZ_ENABLE_INLINE
#define LZZ_INLINE inline
#else
#define LZZ_INLINE       
#endif
template <typename T>
class A
{
  void foo ();
  B b;
};
#undef LZZ_INLINE
#endif

И

// t.cpp
//

#include "t.h"
#include "c.h"
#define LZZ_INLINE inline
template <typename T>
void A <T>::foo ()
          {
    C c;
    c.foo ();
    b.foo ();
  }
#undef LZZ_INLINE

Затем вы можете запустить их с помощью некоторых команд grep / sed, чтобы удалить вспомогательные макросы LZZ.

1 голос
/ 17 марта 2009

Попробуйте использовать предварительно скомпилированные заголовки. Я знаю, что GCC и MSVC поддерживают эту функцию. Однако использование зависит от продавца.

1 голос
/ 17 марта 2009

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

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

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

// foo.h
#define FOO_TEMPLATE template<typename T>
#define FOO_CLASS Foo<T>

FOO_TEMPLATE
class Foo {
  Foo();
  void computeXYZ();
};

// foo_impl.h
#include "foo.h"
FOO_TEMPLATE
FOO_CLASS::Foo(){}

FOO_TEMPLATE
void FOO_CLASS::computeXYZ() { /* heavy code */ }

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

РЕДАКТИРОВАТЬ: о ключевом слове extern в c ++ 0x

Я верю, что ключевое слово extern в c ++ 0x поможет, но оно не решит все волшебным образом!

Из этой статьи ,

Внешние шаблоны

Каждый модуль, который создает экземпляр шаблон по сути создает копию это в объектном коде. Тогда это компоновщику распоряжаться всеми избыточный объектный код в самый последний этап, тем самым замедляя критическое цикл редактирования-компиляции-ссылки, составляющий день программиста (или иногда грезы). Чтобы замкнуть это сборка мусора объектного кода, количество поставщиков компиляторов уже реализовано ключевое слово extern это может идти перед шаблонами. Это случай стандартизации кодификация существующей отраслевой практики (каламбур предназначен). На практике это осуществляется путем отправки уведомления компилятор в основном, чтобы "не создавать экземпляр это здесь ":

extern template class std::vector;
0 голосов
/ 17 марта 2009

C ++ 0x исправит ваши проблемы времени компиляции с внешними шаблонами. Хотя я не знаю автоматического способа сделать то, что вы просите.

...