7

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
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. Commented Aug 24, 2015 at 10:15
  • 1
    This will not work, you are essentially trying to evaluate preprocessor directives at runtime. Commented Aug 24, 2015 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. Commented Aug 24, 2015 at 10:24
  • 2
    Going from your 1st edit to your question, wouldn't this to the trick? template<int> struct BAR { typedef double T; }; template<> struct BAR<4> { typedef int T; }; BAR<4>::T intPoop; BAR<1>::T doublePoop; Commented Aug 24, 2015 at 10:48
  • 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. Commented Aug 24, 2015 at 11:06

4 Answers 4

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.

Sign up to request clarification or add additional context in comments.

1 Comment

Thank you. This is indeed nice for optimizing purposes, but this isn't the case here. I added the answer that suited my needs.
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"; )

2 Comments

This is a very nice yet not so pretty nor scalable solution, but +1 because it answers the question. Thanks
@Gulzar FWIW, that's pretty much how Boost.Preprocessor is implemented internally. Except that the range of values is generally 0..256, and somewhat configurable.
3

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

2 Comments

How is it implemented there?
@Gulzar You don't want to know. You really don't want to know ;-) But if you still do, you can look for yourself - it's open-source, after all. I'm afraid there's no easy way to summarise it - it's preprocessor black magic, pretty much.
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

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.