views:

1339

answers:

18

In my experience, not all macros have been evil. Along the course of my career, macros have been useful in a limited context. In MFC, message crackers provided a concise DSL-like description for message maps. Type-agnostic macros provided useful utility functions for a pre-template versions of C++, albeit dangerous.

However, I have seen many more cases where macros were used poorly. I recall one such occasion where I was helping a co-worker track down a GPF caused by an obscure function call. Offhand, I instructed them to simply examine the disassembly in the debugger to provide some more clues. To my surprise this revealed pages and pages of assembly code for this single function call. Further investigation reveled this “function” was simply a macro which called several other macros. As it turned out the source of the GPF was four macro levels deep, but was a huge headache to unravel.

It seems to me that macros are a thing of the past. Does anyone still use them for any new development? In light of the current language syntax for C++, are all macros now evil? Do other languages use macros and how do they deal with the pitfalls?

+3  A: 

Macros are not evil. Like anything, if you abuse them, they will be evil and bite you. They are still state of the art for header guards, for example. And they have proven to be quite useful for repeating tasks for me.

Johannes Schaub - litb
Macros are evil for the same reason C-style casts are evil: because they hack around the C++ type system and defeat most safeguards built in to the language. Sometimes you have to use them because the language doesn't provide everything it should-that only makes them the lesser of 2 evils not good.
John Dibling
they are not evil. there is no alternative to macros. but there is an equivalent sequence of c++ casts for every c style cast. so you cannot really compare that
Johannes Schaub - litb
and macros do not "hack around the c++ type system". they are merely a copy'n'paste system. you are hacking around it if you write evil macros.
Johannes Schaub - litb
I like John Diblings definition though. They are evil, but sometimes they're the the best you have. :)Just because they're evil doesn't mean they should never be used. ;)
jalf
i think we should retag the question with "religions" :p
Johannes Schaub - litb
Agreed that the debate whether macros are Good or Evil is pointless and un-winnable. I think we agree on the salient points, except that I don't agree that "there is no alternative to macros." In *almost* every case, there is.
John Dibling
@John, i'm reading your comments again right now, and i think i only read half of it back then. I think i agree with the second part of your original comment: You'll have to use them sometimes. My "there is no alternative to macros" was relative to your c-style cast example: What i was trying to show is that there is e.g no alternative to repeatable text insertion, whereas there is an alternative to c-style casting :) I agree with you that often there is a better alternative to macros for tasks.
Johannes Schaub - litb
A: 

Macros are not evil.

But Automake is.

Automake heavily uses macros, and everybody hates it.

The primary problem with macros imo, is in general use, they are almost impossible to debug.

Sure, if a macro language is designed right then it makes it easier, but I have yet to use a complicated macro language that didn't suck.

I've coded in povray, ( yes, coded ) which largely utilises a macro language, but the feature set is incredibly small, which limits the damage it can wield, and as far as its concerned, they're not really like macros, they're more like a really simple template language.

But that doesn't pass for "Complicated" ;)

Kent Fredric
+4  A: 

In general, macros are to be avoided if possible. But I wouldn't go so far as to say that all macros are to be avoided. There are just some things that can't be done by the C++ language.

The biggest problem with macros is that they don't obey normal rules of C++. For example:

#define ADD(x) x += x

What happens when you do this:

 int i = 1;
 ADD(i++);

Of course this gets expanded to:

 int i = 1;
 i++ += i++;

which probably isn't what was intended. If you use an inline function, you would get:

 int i = 1;
 i += i;
 i++;

Which is probably more like what was expected.

Jason Baker
A: 

I use macros frequently however I take care to undefine them once I'm finished. The main problem is Macros don't have namespacing so its very easy to clutter the Precompiler and have macro conflict which will be really hard to find if they do compile without warnings.

Say you define FOR_LOOP and someone else or a library you use also defined FOR_LOOP... now which one will get processed is not obvious without header tracking. This is why I define and undefine all my macros, so my library users don't have unpleasant surprises

Robert Gould
+7  A: 

I'll ignore macros that don't take parameters - as litb says, if you want to tell the preprocessor what code to process, such as in include guards, they're the business. For many other purposes, a constant does as well.

If you have an idea for a parameterized macro, but realise you can replace it with a function template, then do so. The same goes if you can replace it with a struct template, perhaps using a function as well.

If you can't replace it with templates (or, to be precise, if you can't figure out how to do so), then assess whether to use a macro, or just suck it up and write things out longhand each time.

The thing about macros is that you're mixing two languages (the preprocessor language with "proper" C++). This creates headaches, because the compiler only understands one of them at a time, and so will cheerfully allow function-style macros which behave totally unlike actual function calls, which they syntactically resemble at the point of use. But there are cases where it's worth it.

One example is the "verbose logging macro": it does something you can't do without the preprocessor or messy duplication:

#define LOG(x) (std::cout << "expression " #x " evaluates to " << (x) << "\n")

int x = 23;
LOG(23);
LOG(x);
LOG(x*x);

The same goes for writing macros that pass __FILE__ and __LINE__ into your real logging functions.

Another is the "define a macro and include a header" hack:

data.h:

ENTRY(foo,1)
ENTRY(bar,2)
ENTRY(baz,3)

enum.h:

enum THINGY {
     #define ENTRY(A,B) thingy_##A = (B),
     #include "data.h"
     #undef ENTRY
};

switch.cpp:

switch(something) {
    #define ENTRY(A,B) case B: handle_##A (); break;
    #include "data.h"
    #undef ENTRY
}

And so on - you can do "one thing for each line in data.h", for any value of "thing" that can be written as a macro.

Steve Jessop
hey that's a neat idea. thanks!
Johannes Schaub - litb
So true with the logging macros, but if anyone actually used "ENTRY" in real code... I mean, come on, seriously...
Greg Rogers
Personally I'd work fairly hard to avoid it, but once all else had failed and I couldn't figure out a way to avoid having to touch 5 bits of code each time the list changed, I'd at least consider it. I don't think I've ever done it in C++, but I have in a macroised assembler language.
Steve Jessop
+3  A: 

Macros in C++ are not always evil, but usually something that you should avoid (like reinterpret_cast).

In lisp, macros are an incredibly powerful tool that are used extensively (definitely not evil). For a good rundown of what lisp macros are and why they're different from C/C++ macros, take a look at this page.

The Nature of Lisp

The difference can be put into two sentences: Lisp macros operate on the _language_, while the C preprocessor operates on the _text_. The Lisp macro language can use the whole Lisp language itself, while the C preprocessor has only limited options.
Svante
+2  A: 

Macros in C and C++ are very hackish... you should tend to avoid them. They aren't evil, much in the same way goto isn't evil - sure, if you use it properly, it can be fine (and, in assembly, 'goto' is the only way to branch). But it's easy to use improperly, and then it will really bite you.

This is off-topic, but Scheme / Lisp macros, as someone else mentioned, are pretty incredible. It's unfair to call these both 'macros' as they really are different things. (That's why I say this is off-topic, as you asked about C++ macros).

Claudiu
A: 

The best reason for me is that macros are good built in tool for code generation in C/C++. Using them in cases where a function or template function can be used however is a bad idea.

grepsedawk
A: 

Macros can be evil, but so can many other aspects of C/C++ when abused. You can often find a better solution than a macro, though. For example:

#define CONDITION 0
...
#if CONDITION
...
#endif

can in many cases be:

const bool CONDITION = false;
...
if (CONDITION)
{
    ...
}

Any good compiler will recognize that CONDITION is constant and will remove the dead code.

Another example:

#define MAX(a,b) ((a)>(b) ? (a) : (b))

is better replaced by:

template<typename T>
T Max(T a, T b)
{
    return a > b ? a : b;
}
Mark Ransom
Actually, `max` is one of the few cases where a macro has several advantages to the functional solution, which relate to C++' template type system. Notice that this is no longer true in C++0x (or in D, where you could specify the return type of the above as `T1 + T2`).
Konrad Rudolph
I don't agree with "just as easily" - in the second case, the code in the "if" has to compile even if CONDITION is false. In the former case, it doesn't. For example, it could use compiler extensions like 'long long', that don't have to exist so long as CONDITION is 0.
Steve Jessop
@Konrad - I can't think of any advantages to the macro for MAX, care to explain? @onebyone, you make a good point, thanks. Just goes to show there's still a place in the world for macros.
Mark Ransom
@Mark Ransom, i think he means that you have to pass exactly the same type to the two parameters. for example float f = Max(10, 10.); would fail, since one is int, and the other is double. macros won't fail with that. the ?: will find a common type.
Johannes Schaub - litb
this can be fixed by giving each parameter an own type, and then for the return type, figure out the common type that both fits into. That could for example be done with something like this: sizeof(figure<T, U>::call(0 ? get<T>() : get<U>()));...
Johannes Schaub - litb
... which will give you a number at compile time that says which type of them is the common type. using that number you could index into a mpl type vector. for int and double, this would give you the double type.
Johannes Schaub - litb
In c++0x, you could just say auto instead of the type, and then after the parameter list but before the body -> decltype(a > b ? a : b) .
Johannes Schaub - litb
here i have made you an example how you do it: http://codepad.org/l7RYTghY
Johannes Schaub - litb
+1  A: 

Macros have some uses that can't be supplied by templates.

The # and ## come to mind. They can be used to generate good debugging messages by showing you the name of the variable and it's value.

something like this.

#define SHOW_VARIABLE(X) cout << "Variable " << #X << " <" << (X) << ">\n";

My rule of thumb is that if you can do it with a template, that is the way to go, otherwise be careful and do it with a macro.

EvilTeach
+1  A: 

For modern C++, macros should absolutely not be used as shorthand when inline functions would suffice. The tinyXml answer, for example, is probably an example of needless macro abuse.

However, macros are still somewhat useful for code generation. Consider using template traits where all of the native types should be defined by default. You might have some code like this:

template <typename T> struct my_traits {
    const static bool is_specialized = false;
    const static int type_id = 0;
};

Now you have to specialize for every supported type, but the text is nearly identical in all specializations:

template <> struct my_traits<bool> {
    const static bool is_specialized = true;
    const static int type_id = 1;
};
template <> struct my_traits<unsigned char> {
    const static bool is_specialized = true;
    const static int type_id = 2;
};

// and so on...

I would much rather see this code...

#define DECLARE_MY_TRAITS(TypeId, Type)      \
template <> struct my_traits<Type> {         \
    const static bool is_specialized = true; \
    const static int type_id = TypeId;       \
}
DECLARE_MY_TRAITS(1, bool);
DECLARE_MY_TRAITS(2, unsigned char);
// and so on...
#undef DECLARE_MY_TRAITS

It's definitely a niche use-case, but when it helps, it helps a lot. Still, most macros I run into are the "bad" kind, but those are generally easy enough to refactor.

Tom
Add `#undef DECLARE_MY_TRAITS` at the end and nobody gets hurt. ;-)
Konrad Rudolph
A: 

I thought most macros were evil until I met "unlikely" and "likely" in the linux kernel. They have their place. As with anything, use wisely and they will treat you in kind.

Greg Rogers
A: 

Yes, all macros are evil primarily for two reasons. For one, you subvert the language's type system and ddefeat much of the compiler's ability to find mistakes in your code. So code that uses a lot of macros is more susceptible to having subtle defects that will take a lot of time to identify & fix. For another, by making extensive use of macros to generate code in places where templates or inline'd functions would be better suited for the task, you create your own little secret language within the C++ language that nobody but you understands and is very difficult to debug & maintain.

That said, sometimes macros are the lesser of two evils and still have to be used. The most common ones have been mentioned, but even then you should try to use them only where necesarry. Take for example the verbose logging function mentioned multiple times here. Most people write the function like this:

#define LOG(x) cout << __FILE__ << ":" << __LINE__ << " expression: " << x << endl;

The only thing you need to use the macro for in the above is to generate the FILE and LINE strings for the place where LOG() is actually invoked. A better implementation of this functionality takes the code that doesn't have to be in the macro and puts it in another construct, such as:

inline void Log(const char* file, const char* line, const char* expression)
{
  cout << file << ":" << line << " expression: " << x << endl;
}

#define LOG(x) Log(__FILE__, __LINE__, x);
John Dibling
Or for the macro I suggested, template<typename T> void Log(const char *file, const char *line, const char *expression, T value). Fair point.
Steve Jessop
A: 

C++ is lacking.

macros are one way to make it stronger albeit a very crappy one. a good approach instead would be to do code generation like with protobuf.

however, sometimes macros can be the lesser of evils. if you have to work with some other crappy code they can occasionally help you.

// (based on a true story)
// crap alert - C naming style in C++ and "proper accessors" will this a pain:
if (src_entry.ZugaVehicle_get_name_by_ref() != NULL)
  dst_entry.ZugaVehicle_set_name(src_entry.ZugaVehicle_get_name());
if (src_entry.ZugaVehicle_get_license_number_by_ref() != NULL)
  dst_entry.ZugaVehicle_set_license_number(src_entry.ZugaVehicle_get_license_number());
if (src_entry.ZugaVehicle_get_weight_kg_by_ref() != NULL)
  dst_entry.ZugaVehicle_set_weight_kg(src_entry.ZugaVehicle_get_weight_kg());
// ...

turns into

// crap alert - still crap but a little less..
// macro should have descriptive name so you won't have to need to parse it
#define COPY_IF_HAVE(X) if(src_entry.ZugaVehicle_get_##X##_by_ref() != NULL) dst_entry.ZugaVehicle_set_##X(src_entry.ZugaVehicle_get_##X());
COPY_IF_HAVE(name);
COPY_IF_HAVE(license_number);
COPY_IF_HAVE(weight_kg);
// ...
#undef COPY_IF_HAVE

both are ugly, and ideally you would never have to interface with code that forces crap on you.

btw, in linux you can use the "cpp" tool to see what the macros expand to.

yairchu
+1  A: 

Yes, macros are evil, for certain definitions of evil. That doesn't mean you should never use them, just that there are pitfalls and you should try to find a better way first.

Fred Larson
+2  A: 

At least BOOST_FOREACH is not evil :)

Sergey Skoblikov
even better is to #define BOOST_FOREACH foreach. :)
Rob
+1  A: 

The only macros I use are:

  • Header guards.
  • Windows specific pre-compiled header macros (WINVER, _WIN32_WINNT, etc.).
  • All-configuration /D macros (_CRT_SECURE_NO_WARNINGS, etc.).

All others have been replaced by functions or templates.

Rob
+1  A: 

All macros are evil except multiple inclusion guards...

Check up the C++ FAQ if you don't believe me, and yes I truly believe this ;)

Thomas Hansen
Shame, 'cos the BOOST_FOREACH macro is about a million times more useful than the plain old std::foreach template. If only it wasn't evil, it would improve our code, but since it's evil I guess it doesn't ;-)
Steve Jessop