自动将类定义与声明分开?

时间:2009-03-17 01:48:14

标签: c++ templates compilation declaration instantiation

我正在使用一个几乎完全由模板化的类和函数构成的库,如下所示:

// 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.hfoo.cpp文件。当然这是一个维护问题:当一个新的库版本发布时,我必须使它们适应新的版本。还有其他缺点吗?

我的主要问题是:

我是否有机会从原始fwd-foo.h创建这些新文件,尤其是foo.h自动?我必须为许多库头文件(大约20个左右)执行此操作,并且自动解决方案将是最好的,特别是在新版本库版本发布的情况下,我必须使用新版本再次执行此操作。有没有可用于此任务的工具?

编辑:

其他问题:在这种情况下,新支持的extern关键字如何帮助我?

4 个答案:

答案 0 :(得分: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帮助程序宏。

答案 1 :(得分:1)

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

答案 2 :(得分: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关键字会有所帮助,但它不会神奇地解决所有问题!

来自this article

  

外部模板

     

实例化a的每个模块   模板本质上创建了一个副本   它在目标代码中。然后,它就结束了   链接器处理所有的   最后的冗余目标代码   阶段,从而减缓了关键   编辑 - 编译 - 链接循环组成   程序员的一天(或有时候   白日梦)。为此短路   对象代码垃圾收集,a   编译器供应商的数量   已经实现了一个extern关键字   可以放在模板前面。   这是标准化的案例   编纂现有的行业惯例   (双关语)。在实践中,这是   通过发送通知实施   编译器基本上是“不实例化   这是“:

extern template class std::vector;

答案 3 :(得分:-1)

C ++ 0x将修复extern模板的编译时问题。不过,我不知道自动方式做你问的问题。