tags:

views:

146

answers:

6

Given a C project that needs to support multiple environments, how do I use the preprocessor to enforce that exactly one environment is defined?

I can already do

    #if defined PROJA
    (blah blah blah)
    #elif defined PROJB
    (etc)
    #else
    #error "No project defined"
    #endif

All that does, though, is tell me if 0 projects are defined. If some helpful soul defines both project A and project B, the preprocessor will assume only project A. However, the correct behavior, from my perspective, is to flag an error.

Granted, with only 2 projects defined this problem is trivial. How do I solve it with 200?

+3  A: 

Maybe have different files

include_proja.h
include_projc.h

and then use your Makefile or whatever to include the right file. You can then generate 200 distinct files with the code and include the right ones at compile-time.

This kind of thing is what build systems are for. If you are doing something weird like this with macros ... find a better way outside the source code.

Each file can do (Excuse the verbosity here)

#define A_PROJECT_INCLUDE_WAS_INCLUDED

And then do

#ifndef A_PROJECT_INCLUDE_WAS_INCLUDED
    #error "No project include"
#endif

But some missing symbols will break it anyway in all likelihood.

Good Luck

Aiden Bell
"This kind of thing is what build systems are for. If you are doing something weird like this with macros ... find a better way outside the source code."You are totally right.
JXG
+3  A: 

something like this:

#if defined PROJA
  #ifdef HAVE_PROJ
    #error 
  #endif

  #define HAVE_PROJ
#endif

#if defined PROJB
  #ifdef HAVE_PROJ
    #error 
  #endif

  #define HAVE_PROJ
#endif

#ifndef HAVE_PROJ
  #error No project selected (you need to define PROJA, PROJB, or ...)
#endif
Shay Erlichmen
I think this will get grim quickly and isn't really something that should be done in macros imho.
Aiden Bell
A: 

The problem with nested ifs is that you'll end up with n^2 different tests for n projects.

You just need some expression that will give a compile-time error in this case. Perhaps:

#ifdef PROJA
#define PROJA-TEST "
#else
#define PROJA-TEST ""
#endif

and so on for B, C, etc.

Then:

const char *test_only_one_project = PROJA-TEST PROJB-TEST PROJC-TEST " "More than one project is defined!";

EDIT: ... of course this only tests that an odd number of projects are defined. But this should work:

#ifdef PROJA
#define PROJA-TEST (
#endif

and so on, then

const char *test_only_one_project = PROJA-TEST PROJB-TEST PROJC-TEST "More than one project is defined!" );
Jacob B
My first thought was, "Yeah, that's how I'd do it too." My second was, "So is this why no one likes C anymore?"
Jeff Mc
Exactly. The preprocessor is pretty damn evil.
Jacob B
A: 

perhaps you can do something like:

#if PROJ==A
(blah blah blah)
#elif PROJ==B
(etc)
#else
#error "No project defined"
#endif
hiena
+2  A: 

Try this

#define ENV_UNKNOWN 0
#define ENV_MACOSX  1
#define ENV_LINUX   2
#define ENV_WIN32   3
/* and so on */


#ifndef ENVIRONMENT
/* no environment given, default to something (perhaps) */
#define ENVIRONMENT ENV_UNKNOWN
#endif

/* and now the environment specific parts */
#if (ENVIRONMENT == ENV_MACOSX)
#include "macosx_port.h"
#endif

#if (ENVIRONMENT == ENV_LINUX)
#include "linux_port.h"
#endif

#if (ENVIRONMENT == ENV_WIN32)
#include "win32_port.h"
#endif

#if (ENVIRONMENT == ENV_UNKNOWN)
#error You have to specify the ENVIRONMENT.
#endif

Now you can specify the environment you want to compile for on the command line, like this:

cc -DENVIRONMENT=2 ...

Another way is to include/link different modules from your build system depending on the environment you're compiling for.

Markus Schnell
+2  A: 
#if defined PROJA
bool one_project_defined = true;
#endif

#if defined PROJB
bool one_project_defined = true;
#endif

#if defined PROJC
bool one_project_defined = true;
#endif

#if defined PROJD
bool one_project_defined = true;
#endif

one_project_defined; // Won't compile in wrong builds
Daniel Daranas
This is short and simple. It does strike me as kind of hacky, though.
Brian
Until some helpful co-op doing maintenance work comes along and thinks, "Hey, this variable isn't being used any more..."
David