The C preprocessor is a macro preprocessor (allows you to define macros) that transforms your program before it is compiled. These transformations can be the inclusion of header files, macro expansions, etc. All preprocessing directives begin with a # symbol. For example, #define PI 3.14.
Explanation: True, After preprocessing all the macro in the program are removed.
The gcc option -D NAME defines a preprocessor macro NAME from the command line. If the program above is compiled with the command-line option -DTEST , the macro TEST will be defined and the resulting executable will print both messages: $ gcc -Wall -DTEST dtest.
__COUNTER__
is useful anywhere you need a unique name. I have used it extensively for RAII style locks and stacks. Consider:
struct TLock
{
void Lock();
void Unlock();
}
g_Lock1, g_Lock2;
struct TLockUse
{
TLockUse( TLock &lock ):m_Lock(lock){ m_Lock.Lock(); }
~TLockUse(){ m_Lock.Unlock(); }
TLock &m_Lock;
};
void DoSomething()
{
TLockUse lock_use1( g_Lock1 );
TLockUse lock_use2( g_Lock2 );
// ...
}
It gets tedious to name the lock uses, and can even become a source of errors if they're not all declared at the top of a block. How do you know if you're on lock_use4
or lock_use11
? It's also needless pollution of the namespace - I never need to refer to the lock use objects by name. So I use __COUNTER__
:
#define CONCAT_IMPL( x, y ) x##y
#define MACRO_CONCAT( x, y ) CONCAT_IMPL( x, y )
#define USE_LOCK( lock ) TLockUse MACRO_CONCAT( LockUse, __COUNTER__ )( lock )
void DoSomething2()
{
USE_LOCK( g_Lock1 );
USE_LOCK( g_Lock2 );
// ...
}
But don't get hung up on the fact I called the objects locks - any function(s) that need to get called in matching pairs fit this pattern. You might even have multiple uses on the same "lock" in a given block.
I've never used it for anything but a DEBUG macro. It's convenient to be able to say
#define WAYPOINT \
do { if(dbg) printf("At marker: %d\n", __COUNTER__); } while(0);
I've used it in a compile-time assertion macro to have the macro create a name for a typedef that will be unique. See
if you want the gory details.
It's used in the xCover code coverage library, to mark the lines that execution passes through, to find ones that are not covered.
I'm interested to learn whether anyone's ever used it,
Yes, but as you can see from many examples in this Q&A, __LINE__
, which is standardized, would also be sufficient in most cases.
__COUNTER__
is only really necessary in cases where the count must increase by one each time, or it must have continuity over several #include
files.
and whether it's something that would be worth standardising?
__COUNTER__
, unlike __LINE__
, is very dangerous because it depends on which header files are included and what order. If two .cpp
files (translation units) include a header file that use __COUNTER__
, but the header file obtains different count sequences in the different instances, they may use different definitions of the same thing and violate the one-definition rule.
One-definition rule violations are very difficult to catch and potentially create bugs and security risks. The few use-cases of __COUNTER__
don't really outweigh the downside and lack of scalability.
Even if you never ship code that uses __COUNTER__
, it can be useful when prototyping an enumeration sequence, saving you the trouble of assigning names before the membership is concrete.
If I'm understanding the functionality correctly, I wished I had that functionality when I was working in Perl, adding an Event Logging function into an existing GUI. I wanted to ensure that the needed hand testing (sigh) gave us complete coverage, so I logged every test point to a file, and logging a __counter__
value made it easy to see what was missing in the coverage. As it was, I hand coded the equivalent.
It is used by Boost.Asio to implement stackless coroutines.
See this header file and examples.
Resulting coroutines look like this:
struct task : coroutine
{
...
void operator()()
{
reenter (this)
{
while (... not finished ...)
{
... do something ...
yield;
... do some more ...
yield;
}
}
}
...
};
If you love us? You can donate to us via Paypal or buy me a coffee so we can maintain and grow! Thank you!
Donate Us With