6

Please note C++03! any C++11 solutions are not good for me, but do post them just for knowledge sake.

I know the preprocessor can do things like:

#define FOO 4
#if FOO == 4
    cout<<"hi"<<endl;
#endif

What I need is:

#define BAR(X)\
    #if X == 4\
       cout<<"hi"<<endl;\
    #endif

main.cpp

BAR(4)

I don't see why all the needed information wouldn't be available in preprocessor time.

So, Please tell me how to achieve this kind of behavior.


edit 1: A normal if condition won't work for my case, because I also do things like:

#define BAR(X)\
    #if X == 4\
       int poop;
    #elif
       double poop;
    #endif
Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
Gulzar
  • 23,452
  • 27
  • 113
  • 201
  • I am using an X-macro https://en.wikipedia.org/wiki/X_Macro, and I need a conditional compilation at its expansion – Gulzar Aug 24 '15 at 10:13
  • 1
    you might want to show more code / what you are trying to do. based on the code sample you gave, I think I might suggest to use a template for this part instead of giving a pure macro solution? depends on how complex it would make other things. – Chris Beck Aug 24 '15 at 10:15
  • A template solution won't work for me, because it involves too much of the code, and because it requires too many template types (variadic template aruments are not cupported in c++03). I am trying to define an X-macro expansion - the list has one parameter that makes the expansion conditional. I just need to know how to write it syntactically right – Gulzar Aug 24 '15 at 10:19
  • 1
    This will not work, you are essentially trying to evaluate preprocessor directives at runtime. – Christian Kiewiet Aug 24 '15 at 10:20
  • 1
    @ChristianKiewiet not correct. In main, the call is 'BAR(4)', which is very obiously completely static - type - free known before compile time. – Gulzar Aug 24 '15 at 10:24
  • If it is completely static you should be able to completely invert the preprocessor directive. i.e. `#define X 4; #if X == 4; #define [..]` – Christian Kiewiet Aug 24 '15 at 10:32
  • @πάντα ῥεῖ you marked this as dupelicate even though it is not the same question! the other guy wanted runtime values to effect his macro. I don't. I need to declare variables, and he only wanted executable statements. Please unmark it as a dupelicate – Gulzar Aug 24 '15 at 10:34
  • @ChristianKiewiet correct. I can indeed invert the preprocessor directive, but what you suggested doesn't have a parameter. The flow doesn't allow me to define and undefine my conditional (`X` in your example). **I need it to come from a parameter, which, again, is known statically** – Gulzar Aug 24 '15 at 10:37
  • Sounds like a design problem. Are you sure you cannot use template specializations w/ a typedef here? – Christian Kiewiet Aug 24 '15 at 10:40
  • @ChristianKiewiet There is indeed an inherent design problem. The reality right now makes it impossible for me to sove that design problem. That led me to use preprocessor magic. You are suggesting template specializations w/ typedef. Please expand on that, I would love to hear, because I don't understand you fully. – Gulzar Aug 24 '15 at 10:44
  • 2
    Going from your 1st edit to your question, wouldn't this to the trick? `template struct BAR { typedef double T; }; template<> struct BAR<4> { typedef int T; }; BAR<4>::T intPoop; BAR<1>::T doublePoop;` – Christian Kiewiet Aug 24 '15 at 10:48
  • @Gulzar The duplicate question asks for exactly the same thing, read again. It doesn't need to be worded the same, or have the same title, what counts is that the problem was already answered there. – πάντα ῥεῖ Aug 24 '15 at 10:49
  • 2
    @πάνταῥεῖ: What? Complete nonsense. Here you are, falsely dupehammering _again_. I have re-opened the question because you're simply wrong. `BAR(i)` and `BAR(4)` are entirely different use cases. – Lightness Races in Orbit Aug 24 '15 at 11:06

4 Answers4

4

As you've discovered, you can't do this in the way that you've attempted. Macro expansion simply doesn't have inline conditional evaluation, so you'd have to create multiple macros instead.

However, if you're just trying to "optimise" normal code flow, you can rely on your compiler's optimizations. Consider this:

if (true) {
   std::cout << "Hi\n";
}

The resulting program will not have any conditional checks in it, because true is always truthy.

Similarly:

if (false) {
   std::cout << "Hi\n";
}

The resulting program will not contain any code to produce output, because false is never truthy.

Similarly:

if (4 != 4) {
   std::cout << "Hi\n";
}

The program will still not contain the std::cout code.

In many cases, you may use this fact to keep your code simple and achieve your desired effect:

#define BAR(X) \
   if ((X) == 4) {
      std::cout << "hi" << std::endl;\
   }

The constraint here, of course, is that an if statement must be valid at the place you write BAR(5), or BAR(42) or BAR(999).

This is also flexible in that now you can use a runtime value (like BAR(i)) and, although the conditional can no longer be collapsed at compile-time, in such a case you'd have no reason to expect that anyway.

I take this approach in my logging macro: the macro, when called for LOG_LEVEL_DEBUG, expands to a conditional that is statically known never to match, in release builds.

The idea is to let the compiler do the optimising.

You're also going to want to consider using a little macro expansion trick to avoid problems with subsequent else clauses.

Community
  • 1
  • 1
Lightness Races in Orbit
  • 378,754
  • 76
  • 643
  • 1,055
  • Thank you. This is indeed nice for optimizing purposes, but this isn't the case here. I added the answer that suited my needs. – Gulzar Aug 24 '15 at 18:10
4

You can do this with the preprocessor if the domain of values for the conditional parameter is well-known (and preferably small). For example, supposing the parameter can only have the values 0 and 1:

#define DOIT_0(X)
#define DOIT_1(X) X
#define CONCAT_(X, Y) X ## Y
#define MAYBE(X) CONCAT_(DOIT_, X)

#define BAR(X) MAYBE(X)(  cout<<"hi"<<endl;  )

#define YESNO 0
BAR(YESNO)

Live on coliru.

Beware of unprotected commas in the argument to BAR.

For equality checks, again over a small range:

#define CONCAT3_(X,Y,Z) X ## Y ## Z
#define EQUAL_0_0(X) X
#define EQUAL_1_1(X) X
#define EQUAL_1_1(X) X
#define EQUAL_0_1(X)
#define EQUAL_0_2(X)
#define EQUAL_1_0(X)
#define EQUAL_1_2(X)
#define EQUAL_2_0(X)
#define EQUAL_2_1(X)
#define DO_IF_EQUAL(X, Y) CONCAT3_(EQUAL_, X, Y)

#define BAR(X) DO_IF_EQUAL(X, 2) ( std::cout << "hi\n"; )
rici
  • 234,347
  • 28
  • 237
  • 341
2

If you can use Boost, you could do this with Boost.Preprocessor:

#define BAR(X) BOOST_PP_EXPR_IF(BOOST_PP_EQUAL(X, 4), cout << "hi" << endl;)
Angew is no longer proud of SO
  • 167,307
  • 17
  • 350
  • 455
1

Some answers here were better than others. The one I accepted was posted by Christian Kiewiet in a comment, but it was the most accurate for my purpose. Here is the expanded version:

useCases.h

enum UseCases{
    useCase1=0,
    useCase2,
    useCaseNumber//always last for iterations
} 

specializer.h

#include "useCases.h"
<template UseCases theCase>
struct StaticCase{
    //empty, thus accidents calling from this can't happen
}

//specialization
template<>
class StaticCase<UseCases::useCase1>{
     typedef int T;
     static foo(T arg){cout<<"case1";};
}


template<>
class StaticCase<UseCases::useCase2>{
     typedef double T;
     static foo(){cout<<"case2";};
}

Now, I can do

#define BAR1(useCase) StaticCase<useCase>::foo(); 

or

#define BAR2(useCase) StaticCase<useCase>::T var;

and the call:

BAR1(UseCases::useCase1)//output - case1
BAR1(UseCases::useCase2)//output - case2
Gulzar
  • 23,452
  • 27
  • 113
  • 201