Preprocessor

B

buchtak

Hi,

is there a way to test if a variable is defined by a preprocessor
directive? Suppose, for example, I want to simplify this code by using
two macros:
Timer t1;
t1.start();
.... // some action
t1.stop();
std::cout << "..." << endl;

#define TIMER_START(x) // defines timer tx and starts timing
#define TIMER_STOP(x) // stops timer tx and prints out the elapsed
time

However, the problem arises when I try to call TIMER_START(1) twice in
the same block since there's redefinition of t1. Is there a way to
extend the TIMER_START(x) macro such that it would first test if the
timer tx exists to avoid compiler errors?
 
P

Pascal J. Bourguignon

Hi,

is there a way to test if a variable is defined by a preprocessor
directive? Suppose, for example, I want to simplify this code by using
two macros:
Timer t1;
t1.start();
... // some action
t1.stop();
std::cout << "..." << endl;

#define TIMER_START(x) // defines timer tx and starts timing
#define TIMER_STOP(x) // stops timer tx and prints out the elapsed
time

However, the problem arises when I try to call TIMER_START(1) twice in
the same block since there's redefinition of t1. Is there a way to
extend the TIMER_START(x) macro such that it would first test if the
timer tx exists to avoid compiler errors?

It would be better if you defined a scoping couple of macros.
Assume you want expanded code such as:

{
Timer tXYZ;
try{
tXYZ.start();

[BODY]

tXYZ.stop();
}catch(...){
tXYZ.stop();
throw;
}
}

Then it wouldn't matter if you used the same name in an embedded version:


{
Timer tXYZ;
try{
tXYZ.start();

{
Timer tXYZ;
try{
tXYZ.start();

[BODY]

tXYZ.stop();
}catch(...){
tXYZ.stop();
throw;
}
}

tXYZ.stop();
}catch(...){
tXYZ.stop();
throw;
}
}


you would still have two different timers, and the references to each
are well scoped, lexically.


#define WITH_TIMER_BEGIN(TIMEOUT) \
do{ \
Timer CURRENT_TIMER; \
try{ \
CURRENT_TIMER.start(TIMEOUT); \
{ \
int CURRENT_TIMER=0; /* hides the real timer */

#define END_WITH_TIMER \
} \
CURRENT_TIMER.stop(); \
}catch(...){ \
CURRENT_TIMER.stop(); \
throw; \
} \
}while(0)


and write:

WITH_TIMER_BEGIN(Minute(3)){
do_something_slow();
}END_WITH_TIMER;

WITH_TIMER_BEGIN(Minute(3)){
do_something_slow();
WITH_TIMER_BEGIN(Second(15)){
do_something_not_too_slow();
}END_WITH_TIMER;
do_something_slow();
}END_WITH_TIMER;



(There's also a way to play tricks with for and if to avoid having to
define a END_XYZ macro, see for example boost::foreach, but it's
rather tricky and not always feasible.)
 
B

buchtak

Thx Pascal. I guess that determining whether particular non-
preprocessor variable exists in the scope, which would elegantly solve
the problem, is impossible via the preprocessor. The problem with your
solution is, that the timer couldn't be stopped in an if-block for
example. I don't know about boost::foreach, but using it in this
particular case seems to undermine the basic reason of using macros,
which is to simplify things. But thx anyway for ideas.
 
J

James Kanze

It would be better if you defined a scoping couple of macros.
Assume you want expanded code such as:
{
Timer tXYZ;
try{
tXYZ.start();

[BODY]

tXYZ.stop();
}catch(...){
tXYZ.stop();
throw;
}
}
Then it wouldn't matter if you used the same name in an
embedded version:
{
Timer tXYZ;
try{
tXYZ.start();

{
Timer tXYZ;
try{
tXYZ.start();
tXYZ.stop();
}catch(...){
tXYZ.stop();
throw;
}
}
tXYZ.stop();
}catch(...){
tXYZ.stop();
throw;
}
}

you would still have two different timers, and the references
to each are well scoped, lexically.
#define WITH_TIMER_BEGIN(TIMEOUT) \
do{ \
Timer CURRENT_TIMER; \
try{ \
CURRENT_TIMER.start(TIMEOUT); \
{ \
int CURRENT_TIMER=0; /* hides the real timer */
#define END_WITH_TIMER \
} \
CURRENT_TIMER.stop(); \
}catch(...){ \
CURRENT_TIMER.stop(); \
throw; \
} \
}while(0)
and write:

WITH_TIMER_BEGIN(Minute(3)){
do_something_slow();
WITH_TIMER_BEGIN(Second(15)){
do_something_not_too_slow();
}END_WITH_TIMER;
do_something_slow();
}END_WITH_TIMER;

Putting mismatched braces in a macro is a nice trick to render
the code unreadable. There are very few cases where it is
justified (although admittedly, this may be one). Why not just
use a class, which reads the timer in the constructor and the
destructor? (Presumably, you'll also want to capture the
results somewhere, so the constructor needs a reference to
that.)

Having said that... All his macro gains him is one line, that
which declares the variable. IMHO, it's not worth it.
 
P

Pascal J. Bourguignon

James Kanze said:
Putting mismatched braces in a macro is a nice trick to render
the code unreadable. There are very few cases where it is
justified (although admittedly, this may be one). Why not just
use a class, which reads the timer in the constructor and the
destructor? (Presumably, you'll also want to capture the
results somewhere, so the constructor needs a reference to
that.)

Having said that... All his macro gains him is one line, that
which declares the variable. IMHO, it's not worth it.

Yes, I must admit that if there is only this functionality to abstract
away, such a couple of macro may be overdoing it. However, the
technique is still useful in more complex cases.


Notice that we are not after the line count, but after a syntactic
abstraction: we want to hide what is inside the macro.


Sometimes functional abstraction or data abstraction (objects) is
enough to hide the details, but unfortunately, there are occurences
where functional abstraction is not enough (for example, when you have
to remind using try/catch around every occurences of your functional
abstraction), and were a good macro makes miracle in readability and
bug avoidance.



But given the need the OP expressed later, to stop the timer inside
the body, in his case indeed a RAII pattern would be better.

try{
Timer timer(Minute(3));
doSomethingSlow();
if(needMoreTime){
timer.stop();
doSomethingEvenSlower();
}
}catch(Timeout& e){
notEnoughTimeToCompleteTheTask(e);
}
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

Forum statistics

Threads
473,755
Messages
2,569,536
Members
45,015
Latest member
AmbrosePal

Latest Threads

Top