2

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)
xtofl
  • 40,723
  • 12
  • 105
  • 192
  • First variant looks more clear to me. – Kirill V. Lyadvinsky Aug 12 '09 at 13:15
  • It sure does. That means it's a bad example. I'll try to come up with a better one. – xtofl Aug 12 '09 at 13:25
  • This should not be a macro, this should just be a template function like `template void log_type(const T value) { ... }`. So this use of macros is already replaced by another language construct since a long time. – sth Aug 12 '09 at 16:17
  • @Jla3ep: if there are 2 functions it's more clear. If there are 30 functions it's still more clear, but now there are 30 things that need to be reviewed / verified so they are bug-free. Error-checking is one reason to use macros. – Jason S Aug 12 '09 at 17:13
  • @Jla3ep: added some functions by copy-pasting. Find the error in the first variant. – xtofl Aug 12 '09 at 21:15
  • @sth: you can't do textual replacement with templates: you'll always end up typing "int" twice. – xtofl Aug 12 '09 at 21:15

8 Answers8

14
  • 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

Matthew Scharley
  • 127,823
  • 52
  • 194
  • 222
SadSido
  • 2,511
  • 22
  • 36
  • 2
    BEGIN_MESSAGE_MAP just hides coding horror in Microsoft message processing implementation in MFC. – Kirill V. Lyadvinsky Aug 12 '09 at 13:21
  • 1
    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 Aug 12 '09 at 13:35
  • 1
    +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 Aug 12 '09 at 14:08
  • 1
    Indeed. Macro's do good at that, but better design fixes the entire problem. – GManNickG Aug 12 '09 at 16:36
7

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ș
  • 14,123
  • 2
  • 39
  • 62
7

platform specific sections.

ie

#ifdef WINDOWS
#include "WindowsImplementation.h"
#elif defined( LINUX )
#include "LinuxImplementation.h"
#else
#error Platform undefined.
#endif
Goz
  • 61,365
  • 24
  • 124
  • 204
  • 4
    I think this should be handled by the build system, not by the language. – xtofl Aug 12 '09 at 12:49
  • @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 Aug 12 '09 at 13:01
  • I think he is referring to something like Visual Studio's "Exclude from build" functionality. The problem I have with such things is that you can end up working across multiple different compilers and so forth. I guess it could all be done via a makefile but I have worked on projects that needed to compile under GCC, Visual Studio and Code Warrior (ie PS2, X-Box & PC and Wii). Its doable in these circumstances but much easier to do the above IMO. – Goz Aug 12 '09 at 13:11
  • 3
    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 Aug 12 '09 at 13:18
  • @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 Aug 12 '09 at 14:38
  • Boost uses this all the time, to use platform-specific functions in template code. – bdonlan Aug 12 '09 at 14:52
  • Yes - boost uses a method very much like Goz's example. – Michael Burr Aug 12 '09 at 17:25
4

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__ );  \
}
Kirill V. Lyadvinsky
  • 97,037
  • 24
  • 136
  • 212
3

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
Fernando N.
  • 6,369
  • 4
  • 27
  • 30
3

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
  • 184,598
  • 164
  • 608
  • 970
  • +1 for mentioning the DRY principle _in combination_ with no better method being available. – xtofl Aug 12 '09 at 21:18
  • Would you need stringifying and concatenation for other than debug purposes? – xtofl Aug 12 '09 at 21:37
  • +1 for mentioning about the reduction of human error. I once wrote a program in javascript that wrote up a microsoft word monthly report. Took 5 seconds to run and never ever had an error. Before this is took humans a day (including proofreading). Sometimes even after proofreading the human generated report there were still errors. –  Apr 10 '12 at 01:34
0

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
  • 6,383
  • 10
  • 33
  • 31
0

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
  • 39,711
  • 30
  • 131
  • 179
  • 2
    As the question is about C++, why would anyone do this rather than use templates? –  Aug 12 '09 at 13:39
  • 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 Aug 12 '09 at 13:42
  • 3
    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 Aug 12 '09 at 13:58