What are preprocessor macros good for? - c++

After reading another question about the use of macros, I wondered: What are they good for?
One thing I don't see replaced by any other language construct very soon is in diminishing the number of related words you need to type in the following:
void log_type( const bool value ) { std::cout << "bool: " << value; }
void log_type( const int value ) { std::cout << "int: " << value; }
...
void log_type( const char value ) { std::cout << "char: " << value; }
void log_type( const double value ) { std::cout << "int: " << value; }
void log_type( const float value ) { std::cout << "float: " << value; }
as opposed to
#define LOGFN( T ) void log_type( const T value ) { std::cout << #T ## ": " << value; }
LOGFN( int )
LOGFN( bool )
...
LOGFN( char )
LOGFN( double )
LOGFN( float )
Any other 'irreplaceables'?
EDIT:
trying to summarize the reasons-why encountered in the answers; since that's what I was interested in. Mainly because I have a feeling that most of them are due to us still programming in raw text files in, still, poorly supporting environments.
flexibility of code-to-be compiled (e.g. #ifdef DEBUG, platform issues) (SadSido, Catalin, Goz)
debug purposes (e.g. __LINE__, __TIME__); I also put 'stringifying' under this reason (SadSido, Jla3ep, Jason S)
replacing e.g. PHP's require vs. include feature (#pragma once) (SadSido, Catalin)
readability enhancement by replacing complicated code (e.g. MESSAGEMAP, BOOST_FOREACH) (SadSido, fnieto)
DRY principle (Jason S)
an inline replacement (Matthias Wandel, Robert S. Barnes)
stringifying (Jason S)

compile different code under different conditions ( #ifdef __DEBUG__ );
guards to include each header once for each translation unit ( #pragma once );
__FILE__ and __LINE__ - replaced by the current file name and current line;
structuring the code to make it more readable (ex: BEGIN_MESSAGE_MAP() );
See interesting macro discussion at gotw here:
http://www.gotw.ca/gotw/032.htm
http://www.gotw.ca/gotw/077.htm

Most useful - header file guarding:
#ifndef MY_HEADER_GUARD
#define MY_HEADER_GUARD
// Header file content.
#endif
Later add [Windows only]
Exporting classes to DLL:
#ifdef EXPORT_MY_LIB
#define MY_API __declspec( dllexport)
#else
#define MY_API __declspec( dllimport)
#endif
Sample class:
class MY_API MyClass { ... };

platform specific sections.
ie
#ifdef WINDOWS
#include "WindowsImplementation.h"
#elif defined( LINUX )
#include "LinuxImplementation.h"
#else
#error Platform undefined.
#endif

I've posted this before, but of course cannot now find it. If you want to access the __FILE__ and __LINE__ macros, then another macro is by far the most convenient way to go - for example:
#define ATHROW(msg) \
{ \
std::ostringstream os; \
os << msg; \
throw ALib::Exception( os.str(), __LINE__, __FILE__ ); \
}

For doing cool magic tricks like in BOOST_FOREACH, injecting variables into an ambit.
BOOST_FOREACH( char c, "Hello, world!" )
{
... use char variable c here ...
} // c's scope ends here
// if there's an outer c defined, its scope resumes here

For don't-repeat-yourself (DRY) reasons. Things that involve repeated constructs at compile-time which cannot be abstracted away in other methods (templates or what have you). If you are finding you're repeating the same code constructs 20 times, that's a potential source of human error -- which hopefully can be abstracted away using templates but sometimes not. It's always a balance between the advantages of seeing raw code that can be type-checked and reviewed clearly, vs. the advantages of using macros for arbitrary substitution patterns (that generally can't be checked by automatic programming tools).
Stringifying and concatenation (the # and ## preprocessor patterns) can't be performed by templates.
Of course, at some point you may be better off using a tool (whether custom or off-the-shelf) for automatic code generation.

Modern languages take the philosophy that needing a proeprocessor ins a sign of a missing language feature, so they define all kinds of language features that the preprocessor took care of very simply back in the old K&R style C.
Your code example above could be simplified via an inline function, for example.
Personally, the most indispensable aspect of a preprocessor is to make it clear that some things are done compile time right in the source code. The java approach of eliminating dead code paths at compile time is just not as obvious when reading the code.

One of their uses is basically as a poor mans ( inlined ) template function.
For example:
#define MIN(X,Y) ((X) < (Y) ? : (X) : (Y))
This allows you to generate a custom MIN function for any types supporting these operators which is effectively inline at the point of useage. Of course there is no type checking and it's easy to end up with weird syntax errors or incorrect behavior if you don't get the parens just right.

Related

What advantages does C++20's std::source_location have over the pre-defined macros __FILE__, __LINE__ and __FUNCTION__?

In my current project, I have used:
Log(__LINE__, __FUNCTION__, message);
But the new C++20 utility class std::source_location
comes with functions line(), column(), file_name(), function_name(), which do the same thing. So, the new C++20 way would be:
log(std::string message,const std::source_location& location = std::source_location::current())
{
std::cout << "Debug:"
<< location.file_name() << ':'
<< location.line() << ' '
<< message << '\n';
}
What is the advantage of the new, C++20 way over the old standard double-underscore macros, __LINE__, __FILE__, __func__, which have already been standard C++ for a long time?
I'm trying to decide if the advantage(s) are so great as to justify modifying the code in my current projects to use the new std::source_location object in preference to the macros.
Your example is the best motivation I can think of. In the past __LINE__ and _FILE__ were the best excuse to use macros for debug logs. Now this does not hold anymore.
In the past you had basically two choiches. Either let the caller pass info on where the logging happens:
log(__LINE__,__FILE__,"Hello World");
or use a macro to get same output via
log("Hello World");
//^--- log must be a macro if we want this __LINE__ in the log
Both is very inconvenient, because either the user has to pass parameters that have to be the same anyhow on each call, or we need to use a macro with all the known downsides. With source_location we can get log("Hello World"); without any macros involved, but still include line and file of the call, because default parameters are substituted at the call site. For example:
#include <source_location>
#include <string>
#include <iostream>
void log(std::string message,const std::source_location& location = std::source_location::current())
{
std::cout << "Debug:"
<< location.file_name() << ':'
<< location.line() << ' '
<< message << '\n';
}
int main() {
log("hello");
log("world");
}
Output:
Debug:/app/example.cpp:15 hello
Debug:/app/example.cpp:16 world
Pre-C++20 you had to choose between being verbose (passing __LINE__, __FILE__, __func__ to each call manually) and using a macro to do that for you.
std::source_location gives you a nice calling syntax without a macro. There are no other hidden advantages.
Note that both Clang, GCC, and MSVC have the same non-standard extension that lets you do it pre-C++20: __builtin_LINE(), __builtin_FILE(), and __builtin_FUNCTION() that can be used as default arguments to achieve the same effect. You should be able to write your own class source_location using them as well.
The main advantage of std::source_location is mostly convenience, because it represents a source location with a single, easily storable structure.
It has also the advantage of not being based on preprocessor magic, so for the language it is not just an integer literal but a valid type, which is much nicer from the point of view of type-safety.
If you are fine with the older approach, there's no reason to change good, already working code. This is generally true for most new features though, not only for std::source_location.

ASSERT that works on Release mode too? [duplicate]

This question already has answers here:
How to put assert into release builds in C/C++
(7 answers)
Closed 1 year ago.
Where can I find an ASSERT similar to the assert(...) macro from the standard C++ library (the one defined in <cassert>), but that works on Release mode too? Or how should I write one?
I like assert(...) because it automatically prints the line my source file where the assertion failed, as well as the assertion expression. I expect those features (if possible) in the Release mode ASSERT as well.
Basically assert is a macro that evaluates the expression and if it fails prints something and then aborts. It's not that hard to write something similar, something like.
#define ASSERT(x) do { if( !(x) ) { printfunc( #x ); abort(); } while(0)
Then you can modify that in order to suit your requirements. For example you might want not to abort in release mode. You could also adjust your printouts (to include just the information that you think is useful), in order to get file and line information you would use __FILE__ and __LINE__ macros (btw the #x in the define expands to a string literal containing the expression x).
In your release build, don't define NDEBUG, and assert will work.
You can undefine NDEBUG
#undef NDEBUG
near the assert statement
or you can define your own assert
#define assert(x) printf(...)
I've rolled out my own assertions that always fire in release mode too, based on this article:
This is the GIST of it, with me not providing the actual implementation (of AbstractAsserter, which is based on the article), but you get the idea:
#include <iostream>
struct AbstractAsserter
{
virtual void doAssert(const char* expr, const char* file, int line, const char* function) const
{
std::cout << "Asserting now at " << expr << ", " << file << ", " << line << ", " << function << std::endl;
}
};
struct Local
{
const char* function_;
const char* expr_;
const char* file_;
int line_;
Local( const char* f, const char* ex, const char* file, int line )
: function_( f ), expr_( ex ), file_( file ), line_( line )
{ }
Local operator << ( const AbstractAsserter& impl )
{
impl.doAssert( expr_, file_, line_, function_ );
return *this;
}
};
// More to be added as required...
#if defined( __GNUC__ )
# define WE_FUNCTION __PRETTY_FUNCTION__
#elif defined(__STDC_VERSION__) && (__STDC_VERSION__ >= 199901)
# define WE_FUNCTION __func__
#else
# define WE_FUNCTION "null_func()"
#endif
#define WE_ASSERT( expr )\
if( expr );\
else Local( WE_FUNCTION, #expr, __FILE__, __LINE__ )\
<< AbstractAsserter();
int main() {
WE_ASSERT(!"bad BAD BOY!");
return 0;
}
With this structure, your implementation may also do some critical saving of state etc (if you deem your program state trustworthy...debatable).
Using the std assert in release mode (where, by the comments, you would like compiler optimizations enabled), isn't a problem per se, as is established by the other answers. Writing a custom one has been done, so there's not much problem there.
However, to quote the Dr. Dobb's article:
Do use assertions liberally throughout your code; they are watchful, reliable guards that protect you (your program) from insanity
If you are worried to have assertion + compiler optimizations, then these two thing are a bit add odds conceptually:
Asserts should and can be used liberally, with the intention of finding bugs early, and mostly so that it doesn't matter whether the assert code hurts performance.
If you want/need to run with optimizations, you may not like the performance hit the additional (now optimized, but still) assert code gives you.
So, in summary, I see the use of a relase mode assert, but make sure to keep it separate from the "normal" assert, where most people would assume it to be off in release mode.

Way to toggle debugging code on and off

I was programming a manchester decoding algorithm for arduino, and I often had to print debug stuff when trying to get things working, but printing to serial and string constants add a lot of overhead. I can't just leave it there in the final binary.
I usually just go through the code removing anything debug related lines.
I'm looking for a way to easily turn it on and off.
The only way I know is this
#if VERBOSE==1
Serial.println();
Serial.print(s);
Serial.print(" ");
Serial.print(t);
Serial.print(" preamble");
#endif
...
#if VERBOSE==1
Serial.println(" SYNC!\n");
#endif
and on top of the file I can just have
#define VERBOSE 0 // 1 to debug
I don't like how much clutter it adds to single liners. I was very tempted to do something very nasty like this. But yeah, evil.
Change every debug output to
verbose("debug message");
then use
#define verbose(x) Serial.print(x) //debug on
or
#define verbose(x) //debug off
There's a C++ feature that allows me to just do this instead of preprocessor?
At the risk of sounding silly: Yes, there is a C++ feature for this, it looks like this:
if (DEBUG)
{
// Your debugging stuff here…
}
If DEBUG is a compile-time constant (I think using a macro is reasonable but not required in this case), the compiler will almost certainly generate no code (not even a branch) for the debugging stuff if debug is false at compile-time.
In my code, I like having several debugging levels. Then I can write things like this:
if (DEBUG_LEVEL >= DEBUG_LEVEL_FINE)
{
// Your debugging stuff here…
}
Again, the compiler will optimize away the entire construct if the condition is false at compile-time.
You can even get more fancy by allowing a two-fold debugging level. A maximum level enabled at compile-time and the actual level used at run-time.
if (MAX_DEBUG >= DEBUG_LEVEL_FINE && Config.getDebugLevel() >= DEBUG_LEVEL_FINE)
{
// Your debugging stuff here…
}
You can #define MAX_DEBUG to the highest level you want to be able to select at run-time. In an all-performance build, you can #define MAX_DEBUG 0 which will make the condition always false and not generate any code at all. (Of course, you cannot select debugging at run-time in this case.)
However, if squeezing out the last instruction is not the most important issue and all your debugging code does is some logging, then the usual pattern lokks like this:
class Logger
{
public:
enum class LoggingLevel { ERROR, WARNING, INFO, … };
void logError(const std::string&) const;
void logWarning(const std::string&) const;
void logInfo(const std::string&) const;
// …
private:
LoggingLevel level_;
};
The various functions then compare the current logging level to the level indicated by the function name and if it is less, immediately return. Except in tight loops, this will probably be the most convenient solution.
And finally, we can combine both worlds by providing inline wrappers for the Logger class.
class Logger
{
public:
enum class LoggingLevel { ERROR, WARNING, INFO, … };
void
logError(const char *const msg) const
{
if (COMPILE_TIME_LOGGING_LEVEL >= LoggingLevel::ERROR)
this->log_(LoggingLevel::ERROR, msg);
}
void
logError(const std::string& msg) const
{
if (COMPILE_TIME_LOGGING_LEVEL >= LoggingLevel::ERROR)
this->log_(LoggingLevel::ERROR, msg.c_str());
}
// …
private:
LoggingLevel level_;
void
log_(LoggingLevel, const char *) const;
};
As long as evaluating the function arguments for your Logger::logError etc calls does not have visible side-effects, chances are good that the compiler will eliminate the call if the conditional in the inline function is false. This is why I have added the overloads that take a raw C-string to optimize the frequent case where the function is called with a string literal. Look at the assembly to be sure.
Personally I wouldn't have a a lot of #ifdef DEBUG scattered around my code:
#ifdef DEBUG
printf("something");
#endif
// some code ...
#ifdef DEBUG
printf("something else");
#endif
rather, I would wrap it in a function:
void DebugPrint(const char const *debugText) // ToDo: make it variadic [1]
{
#ifdef DEBUG
printf(debugText);
#endif
}
DebugPrint("something");
// some code ...
DebugPrint("something else");
If you don't define DEBUG then the macro preprocessor (not the compiler) won't expand that code.
The slight downside of my approach is that, although it makes your cod cleaner, it imposes an extra function call, even if DEBUG is not defined. It is possible that a smart linker will realize that the called function is empty and will remove the function calls, but I wouldn't bank on it.
References:
“Variadic function” in: Wikipedia, The Free Encyclopedia.
I also would suggest to use inline functions which become empty if a flag is set. Why when it is set? Because you usually want to debug always unless you compile a release build.
Because NDEBUG is already used you could use it too to avoid using multiple different flags. The definition of a debug level is also very useful.
One more thing to say: Be careful using functions which are altered by using macros! You could easily violate the One Definition Rule by translating some parts of your code with and some other without debugging disabled.
You might follow the convention of assert(3) and wrap debugging code with
#ifndef NDEBUG
DebugPrint("something");
#endif
See here (on StackOverflow, which would be a better place to ask) for a practical example.
In a more C++ like style, you could consider
ifdef NDEBUG
#define debugout(Out) do{} while(0)
#else
extern bool dodebug;
#define debugout(Out) do {if (dodebug) { \
std::cout << __FILE__ << ":" << __LINE__ \
<< " " << Out << std::endl; \
}} while(0)
#endif
then use debugout("here x=" << x) in your program. YMMV. (you'll set your dodebug flag either thru a gdb command or thru some program argument, perhaps parsed using getopt_long(3), at least on Linux).
PS. Remind that the do{...}while(0) is an old trick to make a robust statement like macro (suitable in every position where a plain statement is, e.g. as the then or else part of an if etc...).
You could also use templates utilizing the constexpr if feature in C++17. you don't have to worry about the preprocessor at all but your declaration and definition have to be in the same place when using templates.

Do pre-processor macros for debug log statements have a place in C++?

Recently I have been reading Effective C++ Second Edition by Scott Meyers to improve on C++ best practices. One of his listed items encourages C++ programmers to avoid pre-processor macros and 'prefer the compiler'. He went as far as saying there are almost no reasons for macro in C++ aside from #include and #ifdef/#ifndef.
I agree with his reasoning, as you can accomplish the following macro
#define min(a,b) ((a) < (b) ? (a) : (b))
with the following C++ language features
template<class T>
inline const T & min(const T & a, const T & b) {
return a < b ? a : b;
}
where inline gives the compiler the option to remove the function call and insert inline code and template which can handle multiple data types who have an overloaded or built in > operator.
EDIT-- This template declaration will not completely match the stated macro if the data type of a and b differ. See Pete's comment for an example.
However, I am curious to know if using macros for debug logging is a valid use in C++. If the method I present below is not good practice, would someone be kind to suggest an alternative way?
I have been coding in Objective-C for the last year and one of my favorite 2D engines (cocos2d) utilized a macro to create logging statements. The macro is as follows:
/*
* if COCOS2D_DEBUG is not defined, or if it is 0 then
* all CCLOGXXX macros will be disabled
*
* if COCOS2D_DEBUG==1 then:
* CCLOG() will be enabled
* CCLOGERROR() will be enabled
* CCLOGINFO() will be disabled
*
* if COCOS2D_DEBUG==2 or higher then:
* CCLOG() will be enabled
* CCLOGERROR() will be enabled
* CCLOGINFO() will be enabled
*/
#define __CCLOGWITHFUNCTION(s, ...) \
NSLog(#"%s : %#",__FUNCTION__,[NSString stringWithFormat:(s), ##__VA_ARGS__])
#define __CCLOG(s, ...) \
NSLog(#"%#",[NSString stringWithFormat:(s), ##__VA_ARGS__])
#if !defined(COCOS2D_DEBUG) || COCOS2D_DEBUG == 0
#define CCLOG(...) do {} while (0)
#define CCLOGWARN(...) do {} while (0)
#define CCLOGINFO(...) do {} while (0)
#elif COCOS2D_DEBUG == 1
#define CCLOG(...) __CCLOG(__VA_ARGS__)
#define CCLOGWARN(...) __CCLOGWITHFUNCTION(__VA_ARGS__)
#define CCLOGINFO(...) do {} while (0)
#elif COCOS2D_DEBUG > 1
#define CCLOG(...) __CCLOG(__VA_ARGS__)
#define CCLOGWARN(...) __CCLOGWITHFUNCTION(__VA_ARGS__)
#define CCLOGINFO(...) __CCLOG(__VA_ARGS__)
#endif // COCOS2D_DEBUG
This macro provides for incredible utility which I will want to incorporate in my C++ programs. Writing a useful log statement is as simple as
CCLOG(#"Error in x due to y");
What is even better, is that if the COCOS2D_DEBUG is set to 0, then these statements never see the light of day. There is no overhead for checking a conditional statement to see if logging statements should be used. This is convenient when making the transition from development to production. How could one recreate this same effect in C++?
So does this type of macro belong in a C++ program? Is there a better, more C++ way of doing this?
First, Scott's statement was made at a time when macros were
considerably overused, for historical reasons. While it is
generally true, there are a few cases where macros make sense.
Of of these is logging, because only a macro can automatically
insert __FILE__ and __LINE__. Also, only a macro can
resolve to absolutely nothing (although based on my experience,
this isn't a big deal).
Macros such as you show aren't very frequent in C++. There are
two usual variants for logging:
#define LOG( message ) ... << message ...
which allows messages in the form " x = " << x, and can be
completely suppressed by redefining the macro, and
#define LOG() logFile( __FILE__, __LINE__ )
where logFile returns a wrapper for an std::ostream, which
defines operator<<, and permits such things as:
LOG() << "x = " << x;
Done this way, all of the expressions to the right of LOG()
will always be evaluated, but done correctly, no formatting will
be done unless the log is active.
There are "right" things to use macros for and there are bad uses of macros. Using macros where functions work is a bad idea. Using macros where functions DON'T do the same thing is perfectly good in my book.
I quite often use constructs like this:
#defien my_assert(x) do { if (!x) assert_failed(x, #x, __FILE__, __LINE__); } while(0)
template<typename T>
void assert_failed(T x, const char *x_str, const char *file, int line)
{
std::cerr << "Assertion failed: " << x_str << "(" << x << ") at " << file << ":" << line << std::endl;
std::terminate();
}
Another trick using the stringizing "operator" is something like this:
enum E
{
a,
b,
c,
d
};
struct enum_string
{
E v;
const char *str;
};
#define TO_STR(x) { x, #x }
enum_string enum_to_str[] =
{
TO_STR(a),
TO_STR(b),
TO_STR(c),
TO_STR(d),
};
Saves quite a bit of repeating stuff...
So, yes, it's useful in some cases.

Advantage of macro over in-line in C++

We know that in-line are favorable as they are checked by the compiler and same operation ( like ++x ) does not evaluate more than once when passed as an argument as compared to macros.
But in an interview I was asked the specific advantages or the circumstances when a macro is more favorable to inline in C++.
Does anyone know the answer or can give a thought on this question ?
The only thing I can think of is there are some tricks that you can do with a macro that can't be done with an inline function. Pasting tokens together at compile-time, and that sort of hackery.
Here is a specific situation where macros are not only preferred, they are actually the only way to accomplish something.
If you want to write a logging function which logs not only some message, but the file & line number of where the instance occured, you can either call your function directly, typing in the file & line values (or macros) directly:
LogError("Something Bad!", __FILE__, __LINE__);
...or, if you want it to work automatically, you must rely on a macro (warning: not compiled):
#define LogErrorEx(ERR) (LogError(ERR, __FILE__, __LINE__))
// ...
LogErrorEx("Something Else Bad!");
This cannot be achieved using templates, default parameters, default construction, or any other device in C++.
Sometimes you want to extend the language in ways that aren't possible with any other method.
#include <iostream>
#define CHECK(x) if (x); else std::cerr << "CHECK(" #x ") failed!" << std::endl
int main() {
int x = 053;
CHECK(x == 42);
return 0;
}
This prints CHECK(x == 42) failed!.
In C++ specifically, one usage of MACROs that seem pop up very often (except for the debug print with file and line) is the use of MACROs to fill in a set of standard methods in a class that cannot be inherited from a base class. In some libraries that create custom mechanisms of RTTI, serialization, expression templates, etc., they often rely on a set of static const variables and static methods (and possibly special semantics for some overloaded operators that cannot be inherited) which are almost always the same but need to be added to any class that the user defines within this framework. In these cases, MACROs are often provided such that the user doesn't have to worry about putting all the necessary code (he only has to invoke the MACRO with the require info). For example, if I make a simple RTTI (Run-Time Type Identification) system, I might require that all classes have a TypeID and be dynamically castable:
class Foo : public Bar {
MY_RTTI_REGISTER_CLASS(Foo, Bar, 0xBAADF00D)
};
#define MY_RTTI_REGISTER_CLASS(CLASSNAME,BASECLASS,UNIQUEID) \
public:\
static const int TypeID = UNIQUEID;\
virtual void* CastTo(int aTypeID) {\
if(aTypeID == TypeID)\
return this;\
else\
return BASECLASS::CastTo(aTypeID);\
};
The above could not be done with templates or inheritance, and it makes the user's life easier and avoids code repetition.
I would say that this kind of use of MACROs is by far the most common in C++.
As already said, macros can use preprocessor directives: __FILE__, __LINE__ for instance, but of course #include and #define can also be useful to parameter behaviour:
#ifdef __DEBUG__
# define LOG(s) std:cout << s << std:endl
#else
# define LOG(s)
#endif
Depending wether __DEBUG__ is defined or not (via #define or via compiler options), the LOG macro will be active or not. This is an easy way to have debug info everywhere in your code that can be easily de-activated.
You can also think of changing the way memory is allocated (malloc will be redefined to target a memory pool instead of the standard heap for instance, etc...).
Inline functions are, as the name indicates, restricted to functional tasks, execution of some code.
Macros have a much broader application they may expand e.g to declarations or replace entire language constructs. Some examples (written for C and C++) that can't be done with functions:
typedef struct POD { double a; unsigned b } POD;
#declare POD_INITIALIZER { 1.0, 37u }
POD myPOD = POD_INITIALIZER;
#define DIFFICULT_CASE(X) case (X)+2 :; case (X)+3
#define EASY_CASE(X) case (X)+4 :; case (X)+5
switch (a) {
default: ++a; break;
EASY_CASE('0'): --a; break;
DIFFICULT_CASE('A'): a = helperfunction(a); break;
}
#define PRINT_VALUE(X) \
do { \
char const* _form = #X " has value 0x%lx\n"; \
fprintf(stderr, _form, (unsigned long)(X)); \
} while (false)
In the context of C++, Boost has a lot of more examples that are more involved and more useful.
But because with such macros you are in some sort extending the language (not strictly, the preprocessor is part of it) many people dislike macros, particularly in the C++ community, a bit less in the C community.
In any case, if you use such constructs you should always be very clear in what the should achieve, document well, and fight against the temptation to obfuscate your code.
A macro is just like a text replacement definition.
These essential differences that come into my mind are:
It must not be function-like. I mean it must not necessarily contain some consistent set of brackets for example.
It can be used elsewhere. Like in a class declaration scope or even in the global scope. So it must not be in the scope of another function.
You must use them if you want to perform actions that are impossible to be performed using functions:
initializing complicated tables (makes core more readable)
ease declaration of some special members like event IDs or tag classes (used a lot in MFC IMPLEMENT_DYNAMIC)
squeeze repetitive declarations at the beginning of functions
the already mentioned use of __LINE__, __FILE__, ... for logging
#include <stdio.h>
#define sq(x) x*x
int main()
{
printf("%d", sq(2+1));
printf("%d", sq(2+5));
return 0;
}
The output for this code are 5 and 17. Macros expand textually. Its not like functions.
Explanation for this example:
sq(2+1) = 2+1*2+1 = 2+2+1 = 5
sq(2+5) = 2+5*2+5 = 2+10+5 = 17
I would add two uses:
MIN and MAX, until C++0x, because the return type had to be declared by hand, mixed min and max as inlined functions would have been nightmarish, while a simple macro did it in the blink of an eye.
privacy: you can always undef the macro before exiting your header, you cannot "undeclare" an inline function (or another symbol). This is due to the absence of proper modularity in C and C++ languages.