C项目 - 如何管理功能列表?

时间:2015-07-27 15:29:19

标签: c c-preprocessor

我有多个功能可以在项目的构建时启用或禁用。

当前实现使用$(document).ready(function () { 'use strict'; //cards $(".card").each(function () { //capture the data-* sets var img = this.dataset.img, alt = this.dataset.alt, heading = this.dataset.heading, text = this.dataset.text, url = this.dataset.url, //set the template template = '<img src="' + img + '" alt="' + alt + '"> <h3>' + heading + '</h3> <p>' + text + '</p>'; //append the template to the parent this.appendChild(template); }); }); 等声明。每当我需要执行与特定功能相关的操作时,我都会使用预处理器指令,例如 Creates a new array with the specified component type and length. * Invoking this method is equivalent to creating an array * as follows: * <blockquote> * <pre> **** int[] x = {length};*** * Array.newInstance(componentType, x); * </pre> * </blockquote> * * <p>The number of dimensions of the new array must not * exceed 255. * * @param componentType the {@code Class} object representing the * component type of the new array *** @param length the length of the new array**

功能定义存储在全局头文件中。

这种方法有两个缺点:

  1. 在任何其他标头之前,它需要#define FEATURE_FOO每个文件中的此全局标头。
  2. 我无法轻松禁用C文件:
  3. 这不是很好:

    #ifdef

    因为我更喜欢这个:

    #include

    因此,解决此问题的另一种方法是在构建时声明我的所有功能:

    // file: foo.c
    #include <stdio.h>
    
    #include "main_header.h"
    #ifdef FEATURE_FOO
    ...
    #endif
    

    我不喜欢的是我手动需要将每个功能传递给我的编译器。

    可接受的解决方法是阅读包含所有功能的// file: foo.c #ifdef FEATURE_FOO #include <stdio.h> ... #endif 文件。在我的Makefile中,我将:

    gcc -DFEATURE_FOO -c %< -o %@
    

    我能找到更好的选择吗?

2 个答案:

答案 0 :(得分:5)

您可以使用gcc的选项-include myheader.h

它将myheader.h的内容添加到当前翻译单元源的最开头。

答案 1 :(得分:1)

我在大部分项目中都使用基于GNU make的构建过程,虽然到目前为止它并不是关于功能,但我也使用了可以帮助你的技术。

首先,拥有配置文件的想法非常好,但为什么不用make语法和include呢?

我使用类似的东西

# default configuration
CC := gcc
DEBUG := 0
GCC32 := 0
USELTO := 1

# read local configuration
-include defaults.mk

您可以使用它来获取功能列表,例如在defaults.mk

FEATURES := foo bar baz

然后执行类似

的操作
FEATUREDEFINES := $(addprefix -DFEATURE_, $(FEATURES))

当您使用$(eval ...)函数时,GNU make可以提供更多 black magic - 这可能是根据您的设置从编译中完全排除源文件的一个很好的选择。我将其用于特定于平台的实现。例如,我有这个包含用于构建二进制文件的Makefile:

P:= src
T:= csnake

csnake_SOURCES:= csnake.c utils.c game.c board.c snake.c food.c screen.c
csnake_PLATFORMSOURCES:= ticker.c
csnake_LDFLAGS:= -lm
csnake_posix_LDFLAGS:= -lcurses
csnake_dos_LDFLAGS:= -Wl,-Bstatic -lpdcurses
csnake_win32_LDFLAGS:= -static-libgcc -Wl,-Bstatic -lpdcurses \
    -Wl,-Bdynamic -lwinmm
csnake_win32_RES:= res$(PSEP)csnake.rc

$(eval $(BINRULES))

我的P是源树中的当前相对路径,T是要构建的目标,PSEP只是包含/或{{1}的辅助变量为了与Windows兼容。其余部分应该是不言自明的 - 对于\$(T)_PLATFORMSOURCES在相对路径$(BINRULES)中查找。它的工作原理如下:

platform/$(PLATFORM)/

所有这些双倍的美元都在那里,因为define BINRULES BINARIES += $$(BINDIR)$$(PSEP)$(T)$$(EXE) $(T)_SOURCES_FULL := $$(addprefix $(P)$$(PSEP),$$($(T)_SOURCES)) ifneq ($$(strip $$($(T)_PLATFORMSOURCES)),) $(T)_SOURCES_FULL += $$(addprefix \ $(P)$$(PSEP)platform$$(PSEP)$$(PLATFORM)$$(PSEP), \ $$($(T)_PLATFORMSOURCES)) endif [...] (... further rules ... ) endef 会扩展变量 - 这是$(eval ...)$(T)所需要的,但不是所有其他变量,所以它们受到额外的美元保护。我只是引用了为决定在这里编译哪些文件而魔术的部分。如果你考虑做这样的事情,see the full example