我正在使用一个几乎完全由模板化的类和函数构成的库,如下所示:
// 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
关键字如何帮助我?
答案 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模板的编译时问题。不过,我不知道自动方式做你问的问题。