How to Test If Preprocessor Symbol Is #Define'D But Has No Value

How to test if preprocessor symbol is #define'd but has no value?

Soma macro magic:

#define DO_EXPAND(VAL)  VAL ## 1
#define EXPAND(VAL) DO_EXPAND(VAL)

#if !defined(MYVARIABLE) || (EXPAND(MYVARIABLE) == 1)

Only here if MYVARIABLE is not defined
OR MYVARIABLE is the empty string

#endif

Note if you define MYVARIABLE on the command line the default value is 1:

g++ -DMYVARIABLE <file>

Here the value of MYVARIABLE is the empty string:

g++ -DMYVARIABLE= <file>

The quoting problem solved:

#define DO_QUOTE(X)        #X
#define QUOTE(X) DO_QUOTE(X)

#define MY_QUOTED_VAR QUOTE(MYVARIABLE)

std::string x = MY_QUOTED_VAR;
std::string p = QUOTE(MYVARIABLE);

How to tell if a #define has a value or not

do I have to specify in documentation which to use?

Technically no, you don't. But you should.

You can easily normalise on checking for false by doing this:

#ifndef DISABLE_FEATURE
#define DISABLE_FEATURE false
#endif

// later
#if DISABLE_FEATURE == false
...

Normalising on checking for definedness is a bit trickier because you cannot compare an empty macro with a value. But it is possible. You can use macro magic such as this (expansion magic borrowed from here):

#define DO_EXPAND(VAL)  VAL ## 1
#define EXPAND(VAL) DO_EXPAND(VAL)

#if defined(DISABLE_FEATURE) \
&& (EXPAND(DISABLE_FEATURE) == false)
#undef DISABLE_FEATURE
#endif

// later
#ifdef DISABLE_FEATURE
...

I recommend instead to stick to one way and to document that. In particular I recommend checking for definedness, and to ignore the value entirely.

Check if a constructed constant is #define'd

If we assume that include guards always looks like

#define NAME /* no more tokens here */

and if, as you said, any compile time error (rather than #error exclusively) is acceptable, then you can do following:

#define THENAMESPACE BLA
#define BLA_API_H // Comment out to get a error.

#define CAT(x,y) CAT_(x,y)
#define CAT_(x,y) x##y

#define NAMESPACE(x) static int CAT(UNUSED_,__LINE__) = CAT(CAT(THENAMESPACE,CAT(_,x)),+1);

NAMESPACE(API_H)

Here, NAMESPACE(API_H) tries to concatenate BLA_API_H and + using ##.

This results in error: pasting "BLA_API_H" and "+" does not give a valid preprocessing token except if BLA_API_H is #defined to 'no tokens'.

In presence of #define BLA_API_H, NAMESPACE(API_H) simply becomes

static int UNUSED_/*line number here*/ = +1;

If you settle for a less robust solution, you can even get nice error messages:

#define THENAMESPACE BLA
#define BLA_API_H // Comment out to get a error.

#define TRUTHY_VALUE_X 1
#define CAT(x,y) CAT_(x,y)
#define CAT_(x,y) x##y

#define NAMESPACE(x) CAT(CAT(TRUTHY_VALUE_,CAT(THENAMESPACE,CAT(_,x))),X)

#if !NAMESPACE(API_H)
#error "namespace not properly defined"
#endif

Here, if BLA_API_H is defined, then #if !NAMESPACE(API_H) expands to #if 1.

If BLA_API_H is not defined, then it expands to #if TRUTHY_VALUE_BLA_API_HX, and TRUTHY_VALUE_BLA_API_HX evaluates to false due to being undefined.

The problem here is that if TRUTHY_VALUE_BLA_API_HX accidentally turns out to be defined to something truthy, you'll get a false negatie.

Is there a way to both check a macro is defined and it equals a certain value at the same time

This may not work for the general case (I don't think there's a general solution to what you're asking for), but for your specific example you might consider changing this sequence of code:

#if(DEBUG_PRINT == 1)
printf("%s", "Testing");
#endif

to:

if (DEBUG_PRINT == 1) {
printf("%s", "Testing");
}

It's no more verbose and will fail to compile if DEBUG_PRINT is not defined or if it's defined to be something that cannot be compared with 1.

The C define with any value for non zero

A generic solution is likely impossible, but you can hack something together:

#define CAT(x, ...) CAT_(x, __VA_ARGS__)
#define CAT_(x, ...) x##__VA_ARGS__

#define CHECK_VALUE_CHECK_0 )(

#define CHECK_VALUE_FALSE(...) CHECK_VALUE_TRUE
#define CHECK_VALUE_TRUE() 1

#define CHECK_VALUE CHECK_VALUE_(CAT(CHECK_VALUE_CHECK_, VALUE))
#define CHECK_VALUE_(...) CHECK_VALUE_FALSE(__VA_ARGS__)

#if CHECK_VALUE
#error Value is 0.
#else
#error Value is not 0.
#endif

Now, if VALUE is defined to 0, the macro CHECK_VALUE will expand to 1. Otherwise it will expand to CHECK_VALUE_TRUE, which as an unknown identifier, is considered falsey by #if.

This solution is hacky:

  • If VALUE starts with 0,, its causes a hard error.
  • If VALUE starts with something other than a letter or digit or _ (e.g. (), it causes a hard error.
  • ...

Set macro to value, iff empty

If you know that only the values 0, 1 or empty can occur you could use something like

#define some_impossible_macro 1
#define some_impossible_macro0 0
#define some_impossible_macro1 0

#define IS_EMPTY(X) some_impossible_macro ## X

Otherwise, for the general case this is a bit more complicated but doable. You could use P99_IS_EMPTY from P99.

Is #if preprocessor macro running #ifdef behind the scenes in Objective-C?

It's not recommended to use #if VARIABLE without checking that VARIABLE has been defined.

When #if evaluates the expression, it does the following:

  1. It expands all macros in the expression (which means that an symbol which has not been #defined will be unchanged.)

  2. It parses the result as an integer arithmetic expression, replacing any identifier with the integer 0.

So if DEBUG has not been #defined as a macro,

#if DEBUG

will be the same as

#if 0

which will also have the same effect as #ifdef DEBUG.

If you define DEBUG on the command-line with -DDEBUG, then there is an implicit

#define DEBUG 1

before the file is preprocessed, with the result that

#if DEBUG

is the same as

#if 1

which is also the same as #ifdef DEBUG.

However. If you did the following:

#define DEBUG
#if DEBUG
// ... stuff
#endif

the the #if will expand to:

#if

and you'll get an error message:

file.m:2:6: error: #if with no expression

As a result, it is unwise to use #if VARIABLE, unless you know for sure that VARIABLE has been defined as an integer.

Issues with using #ifdefs in C#

If you are using Visual Studio is pretty simple to do preprocessor directives.

  1. Select Project
  2. Properties
  3. Build
  4. On 'Conditional compilation symbols' put your wanted symbol and you should have access in all the assembly

If you are not using Visual Studio just follow this topic.

For learning purpose about preprocessor directives you can go to Tutorialspoint C# website.

To write code in C# using C++ approach you can use the following syntax:

#define Name_Value

#if (Name_value)
{
//code
}

If this doesn't fit your needs you can go with a configuration file where you can stack in all your key name - value and access them by ConfigurationManger.AppSettings["Key"]

Another way around this is to have a global enum and assign values.

 public enum DemoEnum
{
Name = Value
}

if(passedEnum == DemoEnum.Name)
{
// code
}

The solution you gave with the constants is not very flexible, because if your code changes you will have to create new constants and the image gets too blurry.

Using readonly properties you can switch values of 'const' in your approach.

public class Global
{
public readonly string Name;

public Global()
{
if(condition)
Name = 10;
else
Name = 30;
}
}

If I forgot an option feel free to comment :)



Related Topics



Leave a reply



Submit