C++ macros

Started by
33 comments, last by Codarki 13 years, 1 month ago
Are macro style functions and constants actually good for anything in C++? It seems like anything they do can be done better with actual constants or templates. The only reasonable use I've seen is Boost Foreach, and that's obsolete as of C++0x.
I trust exceptions about as far as I can throw them.
Advertisement

Are macro style functions and constants actually good for anything in C++? It seems like anything they do can be done better with actual constants or templates. The only reasonable use I've seen is Boost Foreach, and that's obsolete as of C++0x.


Avoid macros where possible.
I use macros in places where the language doesn't do something as well as I would like. This would be a rare thing...

The only place I have needed it for is to provide a clean, one line class registration "function"

I have a factory class which can create an instance of a registered class from it's string name.
for ex: MyClass* pClass = (MyClass*)Factory::Get().CreateObject("MyClass");

then I add just one line to the MyClass.cpp file outside of any functions:
Register(MyClass, "MyClass")

the macro handles creating a static registrar, defining the class creation function and all that. Things that are tougher to do in a clean way without macros...
Couldn't you also do that with a template though?
I trust exceptions about as far as I can throw them.

I use macros in places where the language doesn't do something as well as I would like. This would be a rare thing...

The only place I have needed it for is to provide a clean, one line class registration "function"

I have a factory class which can create an instance of a registered class from it's string name.
for ex: MyClass* pClass = (MyClass*)Factory::Get().CreateObject("MyClass");

then I add just one line to the MyClass.cpp file outside of any functions:
Register(MyClass, "MyClass")

the macro handles creating a static registrar, defining the class creation function and all that. Things that are tougher to do in a clean way without macros...

Lets say that this can't be done with templates. Why not use the stringize operator '#'?

C++: A Dialog | C++0x Features: Part1 (lambdas, auto, static_assert) , Part 2 (rvalue references) , Part 3 (decltype) | Write Games | Fix Your Timestep!

I've had to use macros to avoid schedule runover with legacy code. I don't consider it good practice, and it made me feel dirty afterward, but it was faster to implement than alternatives.
I wouldn't say "avoid macros where possible"... I think it's more important to understand why people are so afraid of them and know when you can use them, and when you must use them.

Why people are so afraid of them:
-Because they're told to be. All over the web, in programming books, they say "avoid macros, where possible". Why? Probably because they're cryptic and unnecessary. When you come across a macro, whether it's just an uppercase constant, or if it's a macro function, it's not quite clear where it came from. In a corporate environment, or a big project, who knows where it came from. It could be defined in some header file that is directories away and you'd have to go grep for it. However, in your own project, if you know where it came from, and you can keep track of what they do, it's not as big of a deal.

When you can use them:
-To define a constant. In this case, I would just use a const var, rather than a preprocessor constant. It's cleaner, preferred, etc.
-If you're making some complicated function call that is 60 characters long, and you don't want to write it out over and over, and it's not really clear what it does just from looking at it, then you can define a macro function, and rather than calling "alien.legs.count() + alien.arms.count() + alien.tails.count(), etc", you could call a macro function COUNT_LIMBS, and the compiler would replace every instance of COUNT_LIMBS with your long set of repetitive calls. Some will argue that you can just make an "inline function", but there is NO guarantee that an inline function will actually be inlined by the compiler.
-An inline function. While the compiler does not guarantee that inline functions will actually be inlined, a preprocessor macro does. However, as mentioned above, it's more cryptic, less readability, etc.

When you MUST use them:
-Pasting tokens together. If you use a macro with the pound sign, it will surround something with quotes. If you use a macro with the double pound sign, it will "paste" the value of that macro in, which is quite handy to paste multiple tokens together.
-Compiler specific cases. I actually recently used this one in one of my work projects. When you develop software that a lot of projects/people depend on, it needs to be executable in more than just one binary. There needs to be a 32bit binary, a 64bit binary, your project needs to compile using the GNU compiler, and the intel compiler, etc, etc. You don't know who's going to be linking to your project, and who's going to need it in the future. I ran into a case where the intel compiler (ICC) supported code A, and then GNU compiler (GCC), supported code B, but GCC did not support A and ICC did not support B. I had to write a macro that basically chose a different line of code depending on whether the compiler was Intel or GNU.

As you can see, macros aren't really necessary unless you run into a rare case where you need them. I mean, realistically, when do you need to paste two tokens together? When do you need to use different code on different compilers? Yeah I did, but that's a once in a lifetime thing so far, and I've been coding C++ for 8 years. I strongly recommend that if you are not the only one working on your project, to use inline functions and runtime constants rather than preprocessor macros/constants. However, if your project isn't going to be too big, and the group (whether it just be you, or you and a few others) working on it will always be aware of how macros are being used, then it would be OK.

Couldn't you also do that with a template though?



Possibly. It has been years since I wrote this code, but I seem to remember trying templates and ran into a road block. Something like I would need a 2 line registration to do what I did with 1 line with a macro. The macro solution has worked out just fine ever since, so I never looked back...

[quote name='BuffaloJ' timestamp='1299607892' post='4783205']
I use macros in places where the language doesn't do something as well as I would like. This would be a rare thing...

The only place I have needed it for is to provide a clean, one line class registration "function"

I have a factory class which can create an instance of a registered class from it's string name.
for ex: MyClass* pClass = (MyClass*)Factory::Get().CreateObject("MyClass");

then I add just one line to the MyClass.cpp file outside of any functions:
Register(MyClass, "MyClass")

the macro handles creating a static registrar, defining the class creation function and all that. Things that are tougher to do in a clean way without macros...

Lets say that this can't be done with templates. Why not use the stringize operator '#'?
[/quote]


I do use the stringize operator in my reg code...I didn't use them as much as I could have because I wanted to have a different class name(which would be displayed in an editor) than the real class name. I put S in front of my engine classes so then I could have:
Register(SMyClass, "MyClass")

having quotes around the name makes it easier to remember which is the name is which is the class type when you copy/paste to a new object you want to register. I am showing a simplified version here anyway, my macro also takes other params like the base class name...

I've found myself using macros for debugging output and readability mostly.

note examples are not copy pasted and probably have typos.


int Log_fd = 0;
#define AS_STRING(val) #val
#define LOG_ERROR(msg, ...) fprintf(Log_fd, "ERROR " __FILE__ ":" AS_STRING(__LINE__) msg, __VA_ARGS__)
#define LOG_WARNING(msg, ...) fprintf(Log_fd, "Warning " __FILE__ ":" AS_STRING(__LINE__) msg, __VA_ARGS__)

// now you have

LOG_ERROR("This is an error\n");
LOG_WARNING("I like the number %d\n", 5);

// instead of

fprintf(Log_fd, "ERROR" __FILE__ ":%dThis is an error\n", __LINE__);
// or
fprintf(Log_fd, "ERROR %s:%dThis is an error\n", __FILE__, __LINE__);
// or
myWonderfullErrorFunc("This is an error\n", __FILE__, __LINE__);



The code is clean, and the output is more descriptive.

I'll admit most of my other uses of macros are superseded by newer conventions, but these have stuck with me. Also any time I feel the need to use the __FUNCTION__ preprocessor token... hrm there is a pattern here

TL;DR - Macros are good for using preprocessor tokens without having them clutter up everything.

This topic is closed to new replies.

Advertisement