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:
#de
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.
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;)
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
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"; )