3

我正在使用一个几乎完全由头文件中的模板类和函数组成的库,如下所示:

// 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 回答 4

4

我们使用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 辅助宏。

于 2009-03-17T11:01:26.043 回答
1

尝试使用预编译的头文件。我知道 GCC 和 MSVC 支持这个功能。但是,使用是特定于供应商的。

于 2009-03-17T03:25:27.417 回答
1

很长一段时间以来,我一直在研究同样的问题。在您提出的解决方案中,您定义了两次模板类。如果它定义了相同的东西(以相同的顺序)就可以了,但是你迟早会遇到问题。

我想出的是反过来考虑这个问题。只要您不专门化您的实现,它就可以正常工作。

它使用两个宏,这避免了必须在实现文件中更新模板参数(但是,如果你想向类添加默认模板参数,请小心)。

// 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 */ }

通过这样做,您基本上可以像使用非模板类一样工作(当然,您可以使用模板函数做同样的事情)。

编辑:关于 c++0x 中的 extern 关键字

我相信 c++0x 中的 extern 关键字会有所帮助,但它不会神奇地解决所有问题!

这篇文章中

外部模板

每个实例化模板的模块本质上都会在目标代码中创建它的副本。然后,由链接器在最后阶段处理所有冗余目标代码,从而减慢构成程序员一天(或有时是白日梦)的关键编辑-编译-链接周期。为了缩短这个目标代码垃圾收集,许多编译器供应商已经实现了一个可以放在模板前面的 extern 关键字。这是一个将现有行业实践(双关语)编纂成标准化的案例。在实践中,这是通过向编译器发送通知来实现的,基本上是“不要在此处实例化它”:

extern template class std::vector;
于 2009-03-17T07:11:17.000 回答
-1

C++0x 将使用外部模板修复您的编译时间问题。不过,我不知道一种自动的方式来做你问的事情。

于 2009-03-17T03:22:59.920 回答