C Project - How to manage the feature list?

I have several features that can be enabled or disabled at build time in a project.

The current implementation uses declarations such as #define FEATURE_FOO

. Whenever I need to do something about a particular feature, I use a preprocessor directive such as #ifdef

.

Function definitions are stored inside a global header file.

This approach has two drawbacks:

  • This requires #include

    this global header in every file before any other header.
  • I cannot easily disable the C file:

This is not good:

// file: foo.c
#include <stdio.h>

#include "main_header.h"
#ifdef FEATURE_FOO
...
#endif

      

Because I prefer this:

// file: foo.c
#ifdef FEATURE_FOO

#include <stdio.h>
...
#endif

      

So, another approach to this problem is to declare all my functions at build time:

gcc -DFEATURE_FOO -c %< -o %@

      

What I don't like here is that I have to manually pass each function to my compiler.

A valid workaround would be to read a file features.list

that contains all the functions. In my Makefile I will have:

DEFINES=$(shell perl -ne 'print "-DFEATURE_$1 " if /(\w+)/' features.list)

%o: %c
    gcc $(DEFINES) -c %< -o $@

      

What's the best alternative I can find?

+3


source to share


2 answers


You can use the gcc option -include myheader.h

.



It adds content myheader.h

to the very beginning of the current source of the translation unit.

+5


source


I use the GNU make-based build process for most of my projects, and while this was not about features, I used methods that might help you here too.

First, the idea of ​​having a config file is very good, but why not just use it in the syntax make

and include

it?

I am using something like this

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

# read local configuration
-include defaults.mk

      

You can use this to have a list of functions, eg. indefaults.mk

FEATURES := foo bar baz

      

and then do something like



FEATUREDEFINES := $(addprefix -DFEATURE_, $(FEATURES))

      


There $(eval ...)

is a lot more black magic when using a function if you use a function $(eval ...)

- this can be a good alternative to exclude the source file from compilation entirely, depending on your settings. I am using this for platform implementation. For example, I included a Makefile to create a binary:

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

      

My P

is the current relative path in the source tree, T

is the target for the build, and PSEP

is just an auxiliary variable containing /

or \

to be compatible with windows. The rest should be obvious - for $(T)_PLATFORMSOURCES

, $(BINRULES)

looks in the relative path platform/$(PLATFORM)/

. It works like this:

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

      

All these double dollars exist because it $(eval ...)

will expand variables - which are desirable for $(T)

and $(P)

but not for everyone else, so they are protected by an additional dollar. I'm just quoting the part doing the magic to decide which files to compile here. If you decide to do something like this, see the complete example

+1


source







All Articles