如何避免公共头中#define的长编译时间

时间:2014-06-12 13:18:38

标签: c++ c c-preprocessor

我想知道是否有一种优雅的方法来解决这个问题。假设有一个共同的标题,例如

// common.h
#ifndef COMMON_H
#define COMMON_H

#define ENABLE_SOMETHING
//#define ENABLE_SOMETHING_ELSE
#define ENABLE_WHATEVER
// many others

#endif

现在包含此文件,让我们说100个其他头文件,各种#define用于启用或禁用仅限于1-2个文件的代码部分。

每次更改一个#define时,整个项目似乎都在重建(我正在使用Xcode 5.1),这很有意义,因为必须在代码周围完全替换,编译器可以&# 39;不知道它在哪里被使用过。

我试图找到一种更好的方法来管理它,以避免长时间的编译,因为这些定义确实已经多次改变。将每个定义拆分到相应的文件/文件中可能是一种解决方案,但我喜欢将所有内容打包在一起的实用方法。

所以我想知道是否有一种通常用来解决这个问题的模式,我在想

// common.h
class Enables
{
  static const bool feature;
};

// common..cpp
bool Enables::feature = false;

在编译优化二进制文件时,这在语义上是否等效? (例如,false enable中的代码将完全消失)。

2 个答案:

答案 0 :(得分:2)

这里有两个不同的问题:

  

将相应文件/文件中的每个定义拆分可能是一个解决方案,但我希望将所有内容打包在一起的实用方法。

这是你的第一个问题。如果我没有正确理解,如果你有多个功能区域,你不必为每个功能区域包含一个标题(但是所有内容都有一个标题)。

应用以下步骤:

  • 按功能划分代码到不同的标头中;每个标题应包含(最多)单个#define FEATURESET启用的内容(并且与FEATURESET宏的存在完全无关)。

  • 确保每个标头只编译一次(在每个功能标题文件的开头添加#pragma once

  • 根据您定义的功能添加一个执行#if#ifdef的便捷标题文件,并根据需要包含功能文件:

    // parsers.h
    // this shouldn't be here: #pragma once
    
    #ifdef PARSEQUUX_SAFE
    #include <QuuxSafe.h>
    #elif defined PARSEQUUX_FAST
    #include <QuuxFast.h>
    #else
    #include <QuuxSafe.h>
    #endif
    
    // eventually configure static/global class factory here
    // see explanation below for mentions of class factory
    

客户代码:

#include <parsers.h> // use default Quux parser

#define PARSEQUUX_SAFE
#include <parsers.h> // use safe (but slower) Quux parser
  

所以我想知道是否存在通常用于解决此问题的模式

这是你的第二个问题。

通过C ++中的功能启用功能的规范方法是根据基类,类工厂和通用接口编程来定义功能API。

// common.h
#pragma once
#include <Quux.h> // base Quux class

struct QuuxFactory
{
    enum QuuxType { Simple, Feathered };
    static std::unique_ptr<Quux> CreateQuux(int arg);

    static QuuxType type;
};

// common.cpp:

#include <common.h>
#include <SimpleQuux.h> // SimpleQuux: public Quux
#include <FeatheredQuux.h> // FeatheredQuux: public Quux

std::unique_ptr<Quux> QuuxFactory::CreateQuux(int arg)
{
    switch(type) {
    case Simple:
        return std::unique_ptr<Quux>{new SimpleQuux{arg}};
    case Feathered:
        return std::unique_ptr<Quux>{new FeatheredQuux{arg}};
    };
    // TODO: handle errors
}

客户代码:

// configure behavior:
QuuxFactory::type = QuuxFactory::FeatheredQuux;

// ...

auto quux = QuuxFactory::CreateQuux(10); // creates a FeatheredQuux in this case

这具有以下优点:

  • 它很简单,不使用宏

  • 可以重复使用

  • 它提供了足够的抽象级别

  • 它不使用任何宏(如“在所有”中一样)

  • 假设Quux功能的实际实现仅包含在一个文件中(作为实现细节,仅编译一次)。你可以在任何你想要的地方包含common.h,它根本不包括SimpleQuux.h和FeatheredQuux.h。

作为通用指南,您应该编写代码,这样它就不需要运行宏。如果这样做,您会发现要添加的任何宏都很容易添加。相反,如果您从一开始就依赖宏来定义您的API,那么代码将无法使用(或接近无法使用)。

答案 1 :(得分:0)

有一种方法可以拆分定义,但仍然使用一个中央配置标题。

main_config.h (它必须没有包含警戒或#pragma once,因为如果在一次编译中多次包含main_config.h,则会导致奇怪的结果单元):

#ifdef USES_SOMETHING
#include "something_config.h"
#endif

#ifdef USES_WHATEVER
#include "whatever_config.h"
#endif

something_config.h (由于与main_config.h相同的原因,不得包含警戒):

#define ENABLE_SOMETHING

所有源文件和文件头文件#includemain_config.h,但在包含之前,他们必须声明它们所引用的部分:

<强> some_source.cpp

#define USES_SOMETHING
#include "main_config.h"

<强> some_other_file.h

#define USES_WHATEVER
#include "main_config.h"