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

Jac*_*ack 7 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
Run Code Online (Sandbox Code Playgroud)

现在包含这个文件,比方说100个其他头文件,各种#define用于启用或禁用某些仅限于1-2个文件的代码部分.

每次#define更改一个单元时,整个项目似乎都在重建(我正在研究Xcode 5.1),这是有道理的,因为它必须在代码周围完全替换,编译器无法知道它的使用位置.

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

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

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

// common..cpp
bool Enables::feature = false;
Run Code Online (Sandbox Code Playgroud)

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

utn*_*tim 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
    
    Run Code Online (Sandbox Code Playgroud)

客户端代码:

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

#define PARSEQUUX_SAFE
#include <parsers.h> // use safe (but slower) Quux parser
Run Code Online (Sandbox Code Playgroud)

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

这是你的第二个问题。

在 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
}
Run Code Online (Sandbox Code Playgroud)

客户端代码:

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

// ...

auto quux = QuuxFactory::CreateQuux(10); // creates a FeatheredQuux in this case
Run Code Online (Sandbox Code Playgroud)

这样做有以下优点:

  • 它很简单并且不使用宏

  • 它是可重复使用的

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

  • 它不使用宏(如“完全”)

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

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