18

I know the basic rules, use inline, enum and const instead of #define, that is not what I'm after with this question. What I want to know is what is considered an acceptable scenario in which you would use a #define macro, and how, in C++.

Please do not post question or links to "define vs const" questions or "preprocessor vs compiler", and I've already gone through Effective C++ by Scott Meyers and I know the advantages of one over the other.

However after hours and hours of surfing the net, I get the feeling #define is treated as some kind of underdog in C++, but I'm sure there must be a case in which it could be acceptable, even desirable, to use it.

To get the ball rolling I guess one scenario I could think of is to create a DEBUG macro that based on it enables prints and whatnot all over the code for debug purposes.

Brian Tompsett - 汤莱恩
  • 5,753
  • 72
  • 57
  • 129
gorba
  • 378
  • 2
  • 8
  • 1
    Header guards for those who are biased against `#pragma once`. Another good example of `#define` being used well is in Google Test. – Suedocode Feb 25 '14 at 07:25
  • @Aggieboy Does #pragma once have the exact effect of using the include guards? I find somehow using guards cumbersome but the code I work on already uses them so I haven't tried #pragma once. – gorba Feb 25 '14 at 07:40
  • 1
    @gorba #pragma once is supported by the many important compilers ([see here for example](http://en.wikipedia.org/wiki/Pragma_once)) but it is not part of the standard. – TobiMcNamobi Feb 25 '14 at 07:51
  • possible duplicate of [When are C++ macros beneficial?](http://stackoverflow.com/questions/96196/when-are-c-macros-beneficial) – OrangeDog Feb 25 '14 at 17:13

5 Answers5

10

Here are a few scenarios where using #define is a good solution:

Adding diagnostics information while preserving function signature:

#ifdef _DEBUG
#define Log(MSG)  Log((MSG), __FILE__, __LINE__);
#endif

Conditional compilation and include guards are also a good example (no example given, as you should understand this :)).

Boilerplate code is another example, but this can easily be abused. A good example of using macros for boilerplate code is the BOOST_AUTO_TEST_CASE macro in Boost.UnitTest (a worse example is the WinAPI macro set that maps Windows APIs to their CHAR or WCHAR macros).

Another good example is providing compiler-specific keywords and settings:

#if (defined _WIN32) && (defined LIB_SHARED)
#   ifdef LIB_EXPORTS
#       define LIB_EXPORT __declspec(dllexport)
#   else
#       define LIB_EXPORT __declspec(dllimport)
#   endif /* LIB_EXPORTS */
#else
#   define LIB_EXPORT extern
#endif /* _WIN32 && LIB_SHARED */

Usage:

// forward declaration of API function:
LIB_EXPORT void MyFunction(int);
utnapistim
  • 26,809
  • 3
  • 46
  • 82
8

The simple setup for debug/release or for crossplatform code. Here is a sample of my program:

void Painter::render()
{
    if (m_needsSorting)
    {
        _sort();
    }
    for (GameObject* o : m_objects)
    {
        o->render();
#ifdef _DEBUG
        o->renderDebug();
#endif
    }
}

and one more for win/ios:

#ifdef _WIN32

#include "EGL/egl.h"
#include "GLES2/gl2.h"
#include <Windows.h>

#ifdef _ANALYZE
#include <vld.h>
#endif

#else // IOS
#import <Availability.h>
#import <UIKit/UIKit.h>
#import <GLKit/GLKit.h>
#import <Foundation/Foundation.h>
#endif

the other thing is for libraries:

#ifdef VECTRY_INLINE
#define vinline inline
#else
#define vinline 
#endif

and some useful stuff like this:

#define MakeShared(T) \
    class T; \
    typedef shared_ptr<T> T##Ptr
Teivaz
  • 5,462
  • 4
  • 37
  • 75
  • 9
    The last piece is some of the code which can be argued as macro abuse. Provided extra convenience is minimal, but understaning the code becomes harder (to track a related bug, it's not enough to know C++ syntax, one must hunt down the macro and then do macro expansion in ones head to even begin thinking if code is correct). Might be ok when it's a well established and documented part of a library, but even then it is one extra thing to learn to be able to use the library. – hyde Feb 25 '14 at 07:34
  • Unfortunately that's what macros usually do - bring mess to our debug. – Teivaz Feb 25 '14 at 07:38
  • 2
    Well, what else would we use if not macros? I feel like multiple `inline` functions would have similar readability problems with more overhead, and full functions are overkill in some situations imho. I'm not aware of anything else that could replace macros. – cf- Feb 25 '14 at 07:45
  • I don't see how inline functions would cause any overhead. – Joel Falcou Feb 25 '14 at 07:46
  • Indeed my understanding is that in modern c++ compilers function calls are optimized and even more so the inline functions, meaning there is no down side vs using a macro. – gorba Feb 25 '14 at 07:48
  • 1
    Learned something new today, then. Thanks @JoelFalcou and @gorba . But would `inline` functions be considered more readable than macros? It seems to me that mental code expansion would still be required. Or is the point that you'd just disable the inlining for debug builds, and re-enable it on release? – cf- Feb 25 '14 at 07:53
  • In case with librararies you can configure how to use them - you want inline or not (though compiler will do its own way, no matter you specify as inline or not) or you want explcict constructors or some switches like in sigslot: `//#define switches` `//SIGSLOT_PURE_ISO`, or jsoncpp: `#define JSON_USE_EXCEPTION 0` – Teivaz Feb 25 '14 at 08:03
  • 1
    @hyde that's why there is the option to export the macro expanded code, do a reformat on that and compile that in your debug build for easy debugging :) – ratchet freak Feb 25 '14 at 12:17
  • Your `MUTATOR_MOVE` macro has a bug. Its body should be `x = std::move(value);` – Petter Feb 25 '14 at 13:17
6

Sometimes, you want to generate code without having to repeat the neverending boilerplate, or without having to use another language to do so. From time to time, templates will not be not enough, and you will end up using Boost.Preprocessor to generate your code.

One example where macros are "required" is Boost.TTI (type traits introspection). The underlying mechanism somehow abuses the language to create a couple of powerful metafunctions, but needs great amounts of boilerplate. For example, the macro BOOST_TTI_HAS_MEMBER_FUNCTION generates a matefunction that checks whether a class has a given member function. Doing so requires to create a new class and cannot be short without a macro (examples of non-macro solutions to tackle the problem here).

There are also some times when you will need to use X-macros to generate your code. It is pretty useful to bind things at compile time. I am not sure whether they can be totally replaced or not as of today, but anyway, you can find some really interesting examples of applications here.

To sum up, macros can be a powerful tool to generate code, but they need to be used with caution.

Community
  • 1
  • 1
Morwenn
  • 21,684
  • 12
  • 93
  • 152
5

I think when C was introduced then C didn't use to have consts, so #defines were the only way of providing constant values. But later on #define was not much used since consts took the place(or in better words we can say that consts were more readily used). But I would say include guards is still one area where they are used. And they are used since your code is more readable.

Header inclusion guards is one area where you cannot use consts

And example:

#ifndef GRANDFATHER_H
#define GRANDFATHER_H

struct foo {
    int member;
};

#endif /* GRANDFATHER_H */

You may also check Why would someone use #define to define constants?

One more thing to add that #defines don't respect scopes so there is no way to create a class scoped namespacewhereas const variables can be scoped in classes.(I know you know the difference but thought to add it as it is important.)

Also to show one example where #define is used:

static double elapsed()
{ ... }
#define ELAPSED '[' << std::fixed << std::setprecision(2) << elapsed() << "] "

// usage:
for (vector<string>::iterator f = files.begin(); f != files.end(); f++) {
    cout << ELAPSED << "reading file: " << *f << '\n';
    process_file(*f);
}
Community
  • 1
  • 1
Rahul Tripathi
  • 168,305
  • 31
  • 280
  • 331
  • I would probably go more for something like implementing an inline function called PrintFixedPrecision2() which encapsulates what that macro does and calling it like PrintFixedPrecision2("reading file", *file...), any particular reason why you would prefer the macro in this case? Not trying to demerit your implementation, just want to understand where you're coming from. ....I tend to stay away from macros as much as I can. – gorba Feb 25 '14 at 07:55
  • some compilers support `#pragma once` as an inclusion guard, which is more efficient (as the compiler doesn't need to open double included headers) – ratchet freak Feb 25 '14 at 12:23
  • 1
    @ratchetfreak, the efficiency argument is bogus, the compiler doesn't need to re-open a header with include guards if it remembers it has include guards (most modern compilers do that). – Jonathan Wakely Feb 25 '14 at 16:10
  • @JonathanWakely the compiler still needs to recognize that they are include guards and (more importantly) they need to be unique across all headers, all that is fixed with `#pragma once` – ratchet freak Feb 25 '14 at 16:13
5

One of the few usefull cases in C++ are include guards:

// A.h
#ifndef _A_H_
#define _A_H_

class A
{ /* ... */ };

#endif /* _A_H_ */
TNA
  • 2,595
  • 1
  • 14
  • 19