17

I feel, every time I read a C or C++ program, that half or more of it is just macros. I understand that macros can be cool but they are hard to track, debug, etc. Not to mention that most programming languages do not even define something like macros (although Perl6 will have something of the sort).

I personally always have found a way to write my code without using macros, whether it be with templates, multiple inheritance, etc. I have even felt I am not a good programmer because all the pros use macros and I try to avoid them as much as I can.

The question is, are there problems which cannot be solved without macros? Are macros ultimately a good/bad practice? When should I consider using a macro?

Dominic Rodger
  • 97,747
  • 36
  • 197
  • 212
Sambatyon
  • 3,316
  • 10
  • 48
  • 65
  • 2
    Well, you can't use templates in C, so that's one reason. – detly Aug 13 '10 at 09:07
  • 9
    What code have you been reading if you feel that "all the pros use macros"? All sane C++ code I know of tries pretty hard to avoid them. In C, it's a bit different because you don't have many alternatives. Incidentally, why is this tagged C at all? Your question *sounds* pretty specific to C++, mentioning templates and inheritance. C and C++ are different languages, you know. – jalf Aug 13 '10 at 10:23
  • Exactly what @jalf said - they might have some similarities, but when it comes to code generation, they completely different planets. For example, the [X-macro/table generation idiom](http://stackoverflow.com/questions/2927245/looking-for-a-good-explanation-of-the-table-generation-macro-idiom) – detly Aug 13 '10 at 10:37
  • Yes, I am sorry for mixing them. However I have to say that in both languages I have seen code in both languages which rely so much on macros that one cannot read or follow the code. – Sambatyon Aug 13 '10 at 14:03
  • @Sambatyon - what I mean is that the answer to "are the problems which cannot be solved without macros" is basically 90% yes in **C** and 90% no in **C++**. The question is inherently different for the respective languages you ask about. – detly Aug 14 '10 at 07:52
  • 5
    A significant number of people who call themselves C++ programmers are actually just writing C within a class framework. They are unaware that C++ has a distinct design idiom so their code is full of arrays, printf's, and macros. Incidentally, they are often the same ones who refer to both languages as a conjugate: "C/C++". – Amardeep AC9MF Aug 14 '10 at 13:17

13 Answers13

18

Yes, here's one. When you need to add tracing code to your program in such a way that one configuration contains it and the other completely omits you have to use macros.

Something like:

#ifdef WITH_LOGGING
    #define LOG( x ) DoLog( x )
#else
    #define LOG( x )
#endif

now you use it this way:

LOG( L"Calling blahblahblah with " + getSomeStringHardToCompute() );

and in the configuration with WITH_LOGGING you have that code and otherwise it is completely omitted - not even present in the binary, and therefore

  • it doesn't help others analyze your program
  • you get a smaller binary
  • the program doesn't waste time fo logging at all
  • the compiler can produce better optimized code.
sharptooth
  • 167,383
  • 100
  • 513
  • 979
  • Also often used for adding code that runs in debug-mode but not in release-mode code. – Dominic Rodger Aug 13 '10 at 08:03
  • That's an example where a macro has come in useful, but not an example of something that couldn't be done at all without a macro. You could put an #ifdef block around each call to DoLog() for example, if you understand what I mean. – thomasrutter Aug 13 '10 at 08:03
  • 1
    That's template or inline function. No need for macros. – Basilevs Aug 13 '10 at 08:03
  • 2
    @thomasrutter: Yes, you can, but that's horrible - you will pollute useful code severely. – sharptooth Aug 13 '10 at 08:06
  • 3
    @Basilevs: Functions require parameters computation. In the example above `getSomeStringHardToCompute()` will be called anyway if a function call is used. The macro is the only way to eliminate that. – sharptooth Aug 13 '10 at 08:07
  • I think in this case you can get around without macros, although adding the overhead of a function call. – Sambatyon Aug 13 '10 at 08:10
  • @Sambatyon: You really don't get it. In case of macro `getSomeStringHardToCompute()` will be eleminated completely - it will not be called and will not be computed. If there's a function call instead of macro that `getSomeStringHardToCompute()` will be called and consume time. Plus all those debug strings in the binary. – sharptooth Aug 13 '10 at 08:26
  • There are several solution to this problem that don't require you to program macros. I think the most elegant way for this task would be to use AspectC++. – fhd Aug 13 '10 at 08:29
  • Would an argument still be computed for this function: tempate void LOG(const T &) {/*Release mode*/} ? Note empty argument declaration. I think minimal optimization would eliminate parameter computation. (might be wrong) – Basilevs Aug 13 '10 at 08:36
  • 1
    @Basilevs: you are wrong. The parameter would have to be evaluated *unless* the compiler can be absolutely sure that doing so will not have any side effects. Another reason to make this a macro is that it allows you to insert the line number from the call site automatically – jalf Aug 13 '10 at 10:25
  • Compiler knows everything about templates. – Basilevs Aug 13 '10 at 13:13
  • 2
    @Basilevs: the compiler knows everything about every type it compiles. Why is that relevant? If evaluating the parameter has side effects, it can't be optimized away or the compiler would change the semantics of the program. – jalf Aug 13 '10 at 15:42
  • 1
    With logging the `__FILE__` and `__LINE__` macros are also very useful. – Tony Jun 16 '11 at 07:30
10

You've been looking at some bad C++ code. The places I use macros are limited to:

  • header guards
  • very occasional conditional compilation
  • a general exception throwing macro
  • a general debugging/logging output macro

I don't think those four can be avoided.

Dominic Rodger
  • 97,747
  • 36
  • 197
  • 212
  • 3
    Especially since for logging / throwing you generally wish to automatically extract the file name and line number. – Matthieu M. Aug 13 '10 at 14:01
10

Straight from Scott Myer's Effective C++ -> 1

Given the availability of consts and inlines, your need for the preprocessor is reduced, but it's not completely eliminated. The day is far from near when you can abandon #include, and #ifdef/#ifndef continue to play important roles in controlling compilation. It's not yet time to retire the preprocessor, but you should definitely plan to start giving it longer and more frequent vacations.

DumbCoder
  • 5,696
  • 3
  • 29
  • 40
4

Debug behaviour may be controlled with constant flags or debug functions. So here is my list of unavoidables:

  • Multiple inclusion protection.
  • Macros are the only way of symbol stringification. assert macro, compact realization of const string & stringify(enum category value);

Example:

const char* stringify(enum category value)
{
    #define c(x) case x: return #x;
    switch(value) {
        c(CIRCLE)
        c(RECTANGLE)
        c(TRIANGLE)
        default: return "UNKNOWN";
    }
    #undef c // the most important part
}
Basilevs
  • 22,440
  • 15
  • 57
  • 102
2

Macros, of course, are also useful when you want to generate code during preprocessing. While this can be avoided using templates (see this SO question and discussion - Are C++ Templates just Macros in disguise?), you can use macros if it makes the life of your users easier - see how the 'googletest' project (https://github.com/google/googletest/) uses macros effectively. You obviously don't want to use macros to generate code that needs debugging, use templates instead.

Community
  • 1
  • 1
tathagata
  • 478
  • 3
  • 12
1

I think that C++'s templates and inline functions make macros pretty much avoidable.

The ubiquitousness of macros is probably due to the fact that there are many C++ programmers that used to be C programmers. Such people will probably be proficient at using macros (because it sometimes really is the best or only solution in pure C) and might not see any point in learning the more complicated C++ features if they already know how to solve the problem. At least in the open source world, there are many C converts, so you naturally meet C paradigms. I don't think that you're a bad programmer if you avoid such a feature, many people do, just like GOTOs.

C (and therefore C++) is an extremely flexible programming language. This is great, because everyone can develop his own distinct style and solve most problems in several different ways. This, however, can also be considered a problem. In my opinion not a problem that should be solved by the language but by establishing conventions.

There are many features in C++ that can be safely ignored. Maybe there are weird special occasions where such a feature would really be the best approach, but in most cases, you can live without:

  • Friend classes
  • Macros
  • GOTOs And more.

IMO, a senior C++ programmer should be able to at least read them all fluently - yet I expect a good programmer to consider carefully when and if to use an infamous feature.

fhd
  • 3,998
  • 2
  • 23
  • 18
1

There are many problems that I can't solve without macros. For instance, serialization/deserialization of some structs

#define STRUCT_DESCRIPTION structname(MyStruct) member(int,a) member(double,b) member(long, c)
#include "declare_serializable_struct.h" // declares struct itself and generates serialization/deserializaton code
#undef STRUCT_DESCRIPTION

( BOOST_PP_SEQUENCE may also be used) Another example - dispatching a messages using message map, i.e. generating switch like this:

switch(message_type)
{
case msg1: on_msg1(msg); break;
case msg2: on_msg2(msg); break;
...
}

and generate handler method declarations on_msgX(msg) in the same time using some message description table ("map")

Personally, I try to avoiod macros when possible, but I didn't succeed in this way.

However, lambdas in c++0x allows to inline arbitrary code into "user-or-library-defined languge statements" such a foreach loops, so macro realm lose a significant part :)

user396672
  • 3,106
  • 1
  • 21
  • 31
1

Macros are a solution for conditional compiling (by ifdef and ifndef). Here is the examples:

1)

#ifndef MY_HEADER_HPP
#define MY_HEADER_HPP

//...

#endif

2)

#ifdef __cplusplus
#define BEGIN extern "C" {
#define END }
#define NULL (0);
#else
#define BEGIN
#define END
#define NULL ((void*)0);
#endif

//-------------------------

BEGIN

void my_function(char* str);

END

//-------------------------

void my_function(char* str)
{
    if(str != NULL)
    {
        //...
    }
}

But inline functions and templates replaces other usages of macros in C++.

Sadeq
  • 7,795
  • 4
  • 34
  • 45
1

I tend to avoid using macros as much as possible because of their obvious safety / debugging issues, however there are times when macros offer something that no other facility within the language does as elegantly, in which case I prefer to use a macro just because it makes my life (and those of my fellow developers) easier.

For example, I have created an Enum class, which wraps an enum in a struct (scope) and adds some functionality:

  • possibility of iteration (which implies an order of the values)
  • conversion to / from string (handy to read/write to a file, write to logs)

In order to create the enum, I use a macro which will automatically generate the converter (to and from) and the vector for iteration.

Of course I could do without one, after all the macro is only for code generation. But doing without one would mean violating DRY, and in my little own preferences "DRY" > "Don't use macros". Because once debugged the macro is safe, whereas a DRY violation is a nightmare for maintenance.

Now, I am all for ditching this macro as soon as I find how not to violate DRY. Ideas are obviously welcome... and an external script is NOT better ;)

My 2 cents.

Matthieu M.
  • 287,565
  • 48
  • 449
  • 722
1

I try to avoid macros too, but to expand on the debugging, I have not found a way to print file name, function name, and line number when debugging.

I typically have a header file called DebugLog.h with the following Macro

#define DEBUG(debugMessage) \
   printf("%s | %s [%d] - %s\n", __FILE__, __PRETTY_FUNCTION___, debugMessage);

Using: DEBUG("Test") will output something like:

main.cpp | foo(void)[20] - Test

You can adjust the macro for C++, and other debugging statements. It's also possible to modify the macro to send the resulting string to a logger.

Dennis Miller
  • 952
  • 1
  • 9
  • 22
1

I've started working at a telecom company. The product code base is about 20 years old, and has to support many legacy products, while also trying to avoid duplicate code. the language used is C++03. I find lots of contstructs similar to the following

ClassA::methodA(...)
{
   // Common code
   ...

#if defined(PRODUCT_A) || defined(PRODUCT_B)
   // Code for Product A or Product B
   ...
#elif defined(PRODUCT_C)
   // Code for product C
   ...
#endif

  // Common code
  ...
}

Horrible stuff, I agree. So far, we haven't been able to find a better solution. At least with this approach, we can understand what the code is supposed to do by simple code-reading.

Paul
  • 443
  • 8
  • 18
-1

The question is, are there problems which cannot be solved without macros?

No.

are macros ultimately a good/back practice? When should I consider to use a macro?

In languages which don't support or honor the inline keyword, macros are a great way to re-use code, but at the same time avoid the overhead of a function call in any code that is tightly looped enough for that to make a huge difference.

Your rant about code being littered with macros is probably justified. There are indeed hard to debug and in some cases to read. But they do come in useful in the very small number of cases where optimisation like this is truly warranted.

Note that as of C99, C can now do explicit inline functions using the inline keyword, which reduces the need for macros and even has advantages over using macros.

thomasrutter
  • 114,488
  • 30
  • 148
  • 167
  • 2
    Being picky, you need macros for include guards and conditional compilation, and in many frameworks to avoid the cost of logging if logging is disabled. – David Rodríguez - dribeas Aug 13 '10 at 08:07
  • 1
    Agree with David. There are problems that can only be solved elagantly with macros ( elegantly being a relative term). – Martin York Aug 13 '10 at 08:17
  • No, because C++ with and without macro is Turing complete? :) But there are a lot of cases when only macros (not templates or inlines) allow to avoid code duplication. I agree that avoiding macros when possible should be an intention,but the honest answer must be"yes". – user396672 Aug 13 '10 at 09:54
  • @thomasrutter: if you can do anything without macros, please show me how to write a function that logs the file name/line number from the call site, along with a custom log message. It's trivial to do with a macro. Impossible without. – jalf Aug 13 '10 at 10:28
  • @jalf: doesn't `log(__FILE__, __LINE__) << "Custom Message: " << i;` works ? Of course it's ugly... – Matthieu M. Aug 13 '10 at 15:34
  • No, because the function doesn't log the file name and line number. It logs the arguments you pass to it, and then it's up to you to pass it the file name and line number. I can write a macro that gives me the line number of the invocation site. I can't write a function which does that. – jalf Aug 13 '10 at 15:39
-3

Programming language macros are good for what all macros are good for: avoiding typing the same things over and over again. So if you find yourself writing same pieces of code in many places, why not make a macro out of it? Especially if you're writing a library, using macros can make life easier for someone trying to use that library. Take a look at almost any GUI toolkit (Qt being one example). They all make extensive use of macros.

teukkam
  • 4,267
  • 1
  • 26
  • 35