tags:

views:

537

answers:

8

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)
+7  A: 

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 { ... };
Cătălin Pitiș
Exporting is supported with GCC.
Michaël Larouche
I was referring to Windows environment, not necessary to Visual C++.
Cătălin Pitiș
+12  A: 
  • 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

SadSido
BEGIN_MESSAGE_MAP just hides coding horror in Microsoft message processing implementation in MFC.
Kirill V. Lyadvinsky
Exactly. That is what I call "to make code more readable". I use "BEGIN_MESSAGE_MAP() ... END_MESSAGE_MAP()" instead of declaring some huge inlined function, consisting of switch-statements.
SadSido
+1: Building static tables is one place where macros will always be hard to beat. When you can specify a name once and then have it used several times for different things this can actually improve the robustness of code. For example, "#define BUILDROW(X) { #X, doGet##X () }". This is exactly what ????_MESSAGE_MAP takes advantage of too.
Richard Corden
jla3eo's point is that the macro is a band-aid.
Dustin Getz
Indeed. Macro's do good at that, but better design fixes the entire problem.
GMan
+6  A: 

platform specific sections.

ie

#ifdef WINDOWS
#include "WindowsImplementation.h"
#elif defined( LINUX )
#include "LinuxImplementation.h"
#else
#error Platform undefined.
#endif
Goz
I think this should be handled by the build system, not by the language.
xtofl
@xtofl I've never seen a build system do this, but it sounds interesting. Have you got any examples of this in a C or C++ project? Thanks
Glen
Goz
Portability should be achieved with a layered architecture. You have on one side an interface without #ifdef and on the other side implementations for this interface. In some cases (say Windows/Unix), it is better to use the build environment to choose the right files (you can end up by having a platform version of the interface as well if the private part need some platform specific types). In other (says Unix variants), there is so much in common that handling with conditional compilation make sense.
AProgrammer
@Glen - I've sometimes seen this done by having, for example, a 'platform.h' header - one for Windows and one for Linux in different directories and the build system will use different include file paths depending on the target platform. So the C source file just needs to `#include "platform.h"`. However, I personally prefer that `#if` still be used to select the differences, as it's difficult to 'see' the build system's configuration, especially for more complex options than the platform target.
Michael Burr
Boost uses this all the time, to use platform-specific functions in template code.
bdonlan
Yes - boost uses a method very much like Goz's example.
Michael Burr
+4  A: 

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__ );  \
}
anon
+1. This is the only use with which I agree.
Kirill V. Lyadvinsky
@Jla3ep, not even include guards?
AProgrammer
`#pragma once` is a non-standard but widely supported preprocessor directive designed to cause the current source file to be included only once in a single compilation.
Kirill V. Lyadvinsky
Widely? Not supported on 3 out of 4 targets here.
AProgrammer
Ok, widely enough for me. cl and g++ supports it.
Kirill V. Lyadvinsky
A: 

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.

Matthias Wandel
I don't think you can replace it without having to type every typename twice.
xtofl
+3  A: 

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
fnieto
A: 

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.

Robert S. Barnes
As the question is about C++, why would anyone do this rather than use templates?
anon
I guess I just think in C. I actually would love to see template functions back ported to C to get rid of this kind of stuff. I don't supposed you know of a C compiler that supports that?
Robert S. Barnes
X and Y could be functions with a lot of computation. When using macro they will be called twice. When using template function they will be called only once.
Kirill V. Lyadvinsky
+2  A: 

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.

Jason S
+1 for mentioning the DRY principle _in combination_ with no better method being available.
xtofl
Would you need stringifying and concatenation for other than debug purposes?
xtofl