【问题标题】:Automatically separate class definitions from declarations?自动将类定义与声明分开?
【发布时间】:2009-03-17 01:48:13
【问题描述】:

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

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

我的主要问题是:

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

编辑:

附加问题:在这种情况下,新支持的extern 关键字对我有何帮助?

【问题讨论】:

    标签: c++ templates compilation declaration instantiation


    【解决方案1】:

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

    【讨论】:

    • 这似乎是我要找的。虽然,我现在有点厌倦了实际使用它......
    【解决方案2】:

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

    【讨论】:

      【解决方案3】:

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

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

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

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

      外部模板

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

      extern template class std::vector;
      

      【讨论】:

        【解决方案4】:

        C++0x 将使用外部模板修复您的编译时间问题。不过,我不知道自动执行您要求的操作。

        【讨论】:

        • 它是如何工作的?我仍然需要将声明与定义分开,不是吗?
        • 'extern' 对编译时间没有帮助,它只是限制了在发生隐式实例化时实例化的内容。
        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 2012-01-04
        相关资源
        最近更新 更多