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.

4
  • 1
    Header guards for those who are biased against #pragma once. Another good example of #define being used well is in Google Test. Commented Feb 25, 2014 at 7: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. Commented Feb 25, 2014 at 7:40
  • 1
    @gorba #pragma once is supported by the many important compilers (see here for example) but it is not part of the standard. Commented Feb 25, 2014 at 7:51
  • possible duplicate of When are C++ macros beneficial? Commented Feb 25, 2014 at 17:13

5 Answers 5

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); 
Sign up to request clarification or add additional context in comments.

Comments

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 

9 Comments

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.
Unfortunately that's what macros usually do - bring mess to our debug.
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.
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?
@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 :)
|
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.

Comments

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_ */ 

2 Comments

Although you shouldn't be using reserved names for the guards.
Not just "shouldn't" but must not. Using reserved names gets this a downvote when it should have got an upvote.
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); } 

4 Comments

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.
some compilers support #pragma once as an inclusion guard, which is more efficient (as the compiler doesn't need to open double included headers)
@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).
@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

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.