If you're programming in C / C++ and aren't taking advantage of preprocessor macros in your code then (at least) one of the following are true:
- You're an idiot.
- Your code sucks.
- You're writing "Hello World."
Alright, I may be slightly overstating this... but only slightly. Like so many things in coding, some dogmatic positions have formed around the use of macros. Some coders love them and others hate them. Some are even shamefully ambivalent about them. But, the bottom line is that MACROS RULE.
Why do macros rule? Simple. They allow the experienced coder to create amazing amounts of reusable code in a short period of time. Properly used, they save tons of time and enable serious magic to take place. But, like a gun in the hands of a child, they can also blow your proverbial head off.
This crybaby thinks macros are too hard...
Okay, before you crybabies start whining about why macros are bad, let me head you off at the pass. Here's the three standard reasons I hear that macros are the devil's work and should never be used:
They Make Code Hard To Read
In the hands of the less experienced and infirm, macros are a dangerous tool. Sure, they can be abused to create all kinds of nearly impossible-to-read code. Which is why, of course, they are so heavily utilized by coders participating in the "Obfuscated C Contest." If you've never checked out that site (and the fascinating code therein) then you should go over there right away. You will flex your coding muscle big-time by working through those programs. I remember one brilliant entry that implemented a fully functional BASIC interpreter in 25 lines of C code using macros. Genius!
Macros are powerful stuff. But are they any worse than variables? Both of them can be abused to make terrible code. You'd never dream of stopping using variables, right? That'd be daffy. So, don't be so wary of macros just because they can be abused. In your soon-to-be experienced hands macros can be the things of legend!
They Make Code Hard To Debug
Coders that raise this objection are clearly incompetent debuggers. Either that or they're dealing with inherited code where the original author(s) had macro madness! Well, even in the latter case, if they were competent debuggers then they'd work through the code methodically until they understood it.
Be careful when using macros, folks, because once you get a sense of their awesome power you may fall prey to macro madess too. This unfortunate mental condition stems from the fact that macros can save you tremendous amounts of coding time. They automate boilerplate code in a way that is efficient and easy to use. Indeed, they empower you to make magical code that no sane coder would ever type by hand. Once a coder gets their first taste of this, they are susceptible to falling into the trap adding too many macros to their code.
If you're an avid single-step debugger then you're going to have a hard time with code that's stuffed with macros. Debugging tools generally don't play nice with stepping through expanded macros. Instead, they step over the entire bit of code. This is one of the pitfalls with using macros if you can't debug at the assembly language level.
There's still hope for you if you don't know assembly language on your target platforms. You need to make sure your macros are bullet proof. There are two ways to do that:
- Write and test your macro code in normal C++. Once it works and you're satisfied then (and only then) macro-ise it. Keep your unrolled original version around for future debugging and testing too.
- Keep your macro code super-simple.
If you're in the unfortunate situation of having to understand code generated under macro madness, that's okay. Don't panic. Remember that you can direct your compiler to generate an intermediate file with all macros expanded. Then pretty print that intermediate file. It's not ideal, but it can help you single step through the code to better understand it.
Better yet, though, don't rely on those crutches. Go out and find the nastiest piece of macro-laden code you can and work with it until you understand it. Work that macro and debugging muscle until you can handle anything crazy coders can throw at you. You'll thank yourself later if you don't go insane first!
They Break The Language
This is a favorite of the language purists out there. It's also utter crap. Macros are part of the language. They allow experienced programmers to perform metaprogramming miracles. Take a look at the wonders unleashed by the creators of "luabind" if you don't believe that. In fact, I'd go so far as to say they make an otherwise tedious and error-prone language a thing of beauty. I'd be wasting away with carpal tunnel syndrome if it weren't for macros.
Once you master macros, it's rainbows and unicorns...
The Ultimate-ish Guide to C++ Macros
I could go on and on about how awesome macros are and why the purists and code-pussies are wrong. I'll cut that short for now and focus instead on how you can become better at macro metaprogramming. If you're living under a rock and don't know what metaprogramming is, here's the definition from wikipedia:
Metaprogramming is the writing of computer programs that write or manipulate other programs (or themselves) as their data, or that do part of the work at compile time that would otherwise be done at runtime. In some cases, this allows programmers to minimize the number of lines of code to express a solution (hence reducing development time), or it gives programs greater flexibility to efficiently handle new situations without recompilation.
Dig into that concept. I particularly like the part about "allows programmers to minimize the number of lines of code to express a solution." That sounds like less work for more money to me! Let's get to it, folks!
Basic Substitution
Macros are simple to write and easy to mess up. Let's start with the basics. Macros are defined using the keyword: "#define" The preprocessor takes your #define and uses it to substitute symbols in your source code before compiling it. Example:
#define HELLO_WORLD printf("Hello World\n")
Using this basic macro, the preprocessor will substitute HELLO_WORLD with a fancy print statement saying "Hello World." Easy enough, right?
Substitution With Variables
Basic substitution macros like the one above are really powerful. But, things get better than that when you add variables to the mix. Macros can take an arbitrary number of variables that can be used during substitution. Example:
#define PRINT_NUMBER(x) printf("%d\n", x)
This macro is a tiny bit more complicated than before, but the intention is clear. It simply prints whatever number you like using printf. We're cooking now.
Conditionals
The C preprocessor has all kinds of handy conditionals (#if, #else, #elif, #endif, #ifdef, etc.) that allow you to control macro expansion. This is exceptionally useful for including optional debugging or platform-specific code, but it also has other more advanced uses. Example:
#ifdef DEBUG
#define TRACE_LINE printf("%s(%d)",__FILE__, __LINE__)
#else
#define TRACE_LINE
#endif
Assuming you have the DEBUG symbol defined, you can use TRACE_LINE to print the current file and line number in the code. Marginally useful but instructive.
Stringizing
If you write enough macros, you'll find times when you want to convert one of your macro variables into a string. Yeah, you'll want to use it both as a variable and a string parameter elsewhere. The C preprocessor includes a handy stringizing operator ('#') to accomplish this. Example:
#define CHECK_NULL(x) if((x) == NULL) printf("OMG, " #x " is NULL!\n")
Notice how this macro has "(x) == NULL" in it? Well, this format is required to handle 'x' having arbitrary expressions in it. It ensures the compiler evaluates the entire expression referenced by 'x' before checking for NULL.
Token Pasting
When using the C preprocessor for metaprogramming, the token pasting operator ('##') is invaluable. This handy operator merges two tokens to create a third new token. Its power comes into play when you need to programmatically create new variable / function / method names in your code. Example:
#define DECLARE_PROPERTY(t,n) protected: t _##n; public: t GetN() { return _##n; } void SetN(t Value) { _##n = Value; }
This macro declares a basic property of type "t" named "_n" and then proceeds to create default accessors for said property. Not too useful by itself, but such macros are the basis of metaprogramming-based network replication systems!
Macros Over Multiple Lines
That last macro is getting pretty ugly. We've got several functions all declared on a single line. Gross. Thankfully the C preprocessor provides a method for implementing multi-line macros. Consider the same macro above reworked using this mechanism:
#define DECLARE_PROPERTY(t,n) \
protected: t _##n; \
public: \
t GetN() { return _##n; } \
void SetN(t Value) { _##n = Value; }
Spreading your macros over multiple lines is a must when they start getting more complex. Get a fork and stab your eye out if you make fat one-line macros.
Putting It All Together
With just the small tools outlined above, you can work basic miracles and write tons of boilerplate code in record time. Let me give you one of my favorite uses for macros, creating type safe delegates. Consider this macro:
#define _MAKE_DELEGATE(N,C,F,D,A,R) \
class N \
{ \
typedef R (C::*FuncPtr)D; \
C *_self; \
public: \
N(C *ptr) \
{ \
_self = ptr; \
} \
R operator()D \
{ \
FuncPtr f = &C::F; \
return (_self->*f)A; \
} \
}
//
// SNICHOLS: Declares a functor class that can call a particular method of an object.
//
// TargetClass - Class that holds the function you want to call.
// TargetFunction - Name of the function you want to call.
// DeclareArgs - Declaration of arguments (int a, bool b)
// Args - Arguments (a,b)
// ReturnValue - Type of the return value
//
#define DECLARE_DELEGATE(TargetClass,TargetFunction,DeclareArgs,Args,ReturnValue) _MAKE_DELEGATE(TargetFunction##Delegate,TargetClass,TargetFunction,DeclareArgs,Args,ReturnValue)
This is a good starting point for a reasonably generic type safe delegate. With a little TLC you could easily work this into a fully functional deferred function callback mechanism. I'll leave that as an exercise for the reader.
More Advanced Topics
I strongly suggest any interested coder study the boost preprocessor library to see some truly advanced usage of macros in metaprogramming. Using their implementations for your advanced metaprogramming building blocks isn't necessary, but it sure can save you some time.
Until next time!
Hi!
C++11 has turned your example macro into real syntax (lambda functions):
struct MyClass
{
float rcp(int x) const { return 1.0f / x; }
};
MyClass obj;
std::function func = [&obj](int x){ return obj.rcp(x); };
float rcp = func(12);
Posted by: MikeNicolella | 09/12/2012 at 08:23 PM
Yeah, I love the C++11 changes (especially move semantics)! Sadly, not everyone is able to use the latest and greatest.
Here's another good example of macros saving me some typing, lifted right out of some tile map code I'm using in my latest project:
#define EMIT_VERT(a,b) VertPtr->x = TOPIXEL((a)); VertPtr->y = TOPIXEL((b)); VertPtr++; *IndexPtr++ = IndexCount++;
This little macro simply does some boilerplate vertex emission and pointer management. It's used in the guts of a composite tile renderer, so avoiding function call overhead is nice. It also makes the code quite a bit nicer to read.
But, man, do I ever love lambdas!
Posted by: Stephen Nichols | 09/12/2012 at 08:37 PM
Indeed, move sematics are handy. On what platform are you seeing a lack of C++11? It's surprisingly prevalent. Microsoft is trailing the pack with their compiler supporting a bit less than GCC/Clang, but even VC2010 supports rvalue refs + lambdas and a handful of other handy bits.
"composite tile renderer" - are you talking CPU renderer? Or is this generating quads to be sent to the GPU? If the latter, I would suggest experimenting with compute shaders and see if you can't move more of the work to the GPU. You can also easily use compute shaders to do vertex buffer generation, and consume it later in the frame (I've done this for things like particle emitters, with the emission code completely on the GPU)
Is your opinion that, assuming lambda function support, your EMIT_VERT macro would be better off as a lambda? Just curious. I tend to only use macros if there's absolutely no other way to express what I want. Typically templates/lambdas do the trick.
Posted by: MikeNicolella | 09/12/2012 at 11:33 PM
That macro is from a bit of code that's generating verts / indices to be sent off to the GPU. It's targeting various mobile devices (including GLES1) so utilizing shaders would require another implementation. And, man, I just don't like to implement things twice if I can avoid it.
The cross platform middleware solution that I'm using to base my current projects on supports a wide variety of compilers (VS2008, VS2010, VS2012, XCode, GCC ARM & MIPS). Thus, taking advantage of the latest compiler features is pretty tricky for us.
We could require our developers to use VS2010+ on Windows and that would be a fine solution. Until we end up targeting the ARM / MIPS devices. The versions of GCC included in our middleware package are rather outdated and don't support all of C++11's features.
Yeah, it's a matter of the lowest common denominator when targeting these multiple platforms (Windows, Mac, iOS, Android) and the various compiler flavors. I'm looking forward to the day that the middleware company updates GCC support and deprecates VS2008.
Now, do I think lambdas are a good replacement for the EMIT_VERT macro? I don't think so. At least not any more so than just replacing it with a function that takes the variables to modify. Of course, there's no guarantee that the compiler will inline such functions (be they lambda or regular). And, in my tight inner loops, I'm not leaving my inlining choices to the compiler.
Let me give you another example of how macros make my life easier: composing objects. In some cases this means adding members to a class. In others it means encapsulating the boilerplate code that is required to instantiate complex objects.
I use this macro to add scheduled member function support to my classes. It relies on an existing class "Scheduler" that invokes raw function pointers at a specified future time (in ms). I found myself making static functions to adapt this Scheduler class to member function pointers. This macro saves me tons of typing:
//
// DECLARE_SCHEDULED_METHOD(c,x)
//
// This adds several methods to your class "c" based on the root method name
// "x" to make scheduling a callback
// easier. Here's what it adds:
//
// virtual void x(); // implement this to handle your scheduled callback
// static void Dispatch_x(void *Context); // shim that calls your x()
// void Schedule_x(int32 Delay); // schedules your x() to be called
// void Cancel_x(); // cancels your previously scheduled x();
//
// Scheduler::SmartCallbackHandle x_Handle; // handle to your callback
//
#define DECLARE_SCHEDULED_METHOD(c,x) \
Scheduler::SmartCallbackHandle x##_Handle; \
virtual void x(); \
static void Dispatch##x(void *Context) { ((c *)Context)->x##_Handle.Clear(); ((c *)Context)->x(); } \
void Schedule_##x(int32 Delay) { Scheduler::Schedule(x##_Handle, Delay, Dispatch##x, this); } \
void Cancel_##x() { x##_Handle.Cancel(); }
Pardon the terrible formatting. The comment editor here leaves much to be desired. :)
This turns a rather annoying manual typing affair into this:
struct Foo
{
DECLARE_SCHEDULED_METHOD(Foo,OnDoSomething)
}
InstanceOfFoo->Schedule_OnDoSometing(250);
It successfully hides all of the boilerplate gunk so I don't have to look at it or worry about it. I love it!
I can envision some other solutions to this problem, but it still seems that macros rule when it comes to composing objects. In my book, they are the go-to technique for metaprogramming. Templates have their place (and I do love me some templates too), but they still don't solve this problem very nicely.
I'd be interested in seeing some ways that you use macros in your production code. It sounds like you avoid them like the plague. ;)
Long live macros!
Posted by: Stephen Nichols | 09/13/2012 at 08:32 AM
I can see how not having access to lambdas makes you lean on macros for a lot of this stuff. I guess I've already been spoiled for a while :)
For your scheduler, I might have tried boost::bind() first, and I don't really like the idea of changing a class definition to be able to play with the scheduler. How do you do deferred execution of functions in a 3rd party lib? Need to wrap it with your stuff?
My scheduler looks like:
auto jobFn = some lambda function object
//launch a simple job
JobId id = jobMgr.RunJob(jobFn);
//wait on completion
jobMgr.WaitForJobComplete(id);
there's also functions for managing job dependencies, but the actual code that's executed by the scheduler is something compatible with std::function, which is free functions with no params that return void, function objects with operator(), or lambdas (which is just syntax sugar for function objects)
-------------
I do have a couple of uses of macros in my home-grown engine. One of them is an assert macro - of course, since I want to print the expression you passed in. Most of the rest is dealing with making the code portable (putting compiler platform-specific nonsense behind macros), and the only 'real' usage is the boilerplate for GameObject components:
#define DEFINE_COMPONENT(TypeName) \
virtual char const* ComponentName() { return #TypeName; } \
TypeName( GameObject* obj ) \
:Component(obj) \
{}
and it's used as such:
struct Camera : Component
{
DEFINE_COMPONENT(Camera);
...
};
now, what I really *want* to do is decorate this struct with an attribute (C++11 has attributes) and write a compiler plugin that generates the necessary boilerplate at compile-time.
Unfortunately there is no standard C++ compiler plugin architecture, but I would be happy to assume Clang.
The other nice thing about being a compiler plugin is that with a simple annotation, I can trigger the compiler to do things like generate script bindings, serialization code, whatever other boilerplate that's really only made a little more tolerable by employing macros.
Alternatively, I might accept writing C++ that gets run at compile time to do metaprogramming. That would require a standard that defines an API for a lot of internal compiler structures (ie if my function were invoked on a class definition, I want a way to get the class name, ways to get at all members, and ability to mutate all of that for the later stages of the compiler.)
I don't think that's going to happen very soon. For now I'll continue ramping up on Clang :)
Posted by: MikeNicolella | 09/13/2012 at 12:18 PM
All valid points!
It seems like your scheduler is focused on threading. I do have a sweet task-oriented threading library similar to that. The scheduler that I'm using is just for delayed execution within the single thread. A very useful thing in games, to be sure.
It's similarly exposed to my Lua code as well:
Scheduler:Add(250, function()
print("Hello World!")
end)
This makes things all kinds of problems super easy to tackle.
I'm all for compiler plugins! Bring em on! But, until they are widespread, I'll stick with this approach. Because it works in a cross-platform manner. But, man, a programmable compiler would be the bee's knees!
Interestingly enough, when using languages like C# without macro support, I've been known to write code generators to get my boilerplate code done. They plug into the compiler fairly nicely. As a matter of fact Portalarium's entire C# backend is based on integrated code generators.
Posted by: Stephen Nichols | 09/13/2012 at 12:46 PM
Indeed, my scheduler is for spreading work across cores. Everyone should be doing this nowadays, even crappy little phones have multiple cpu cores.
When I get some time I very much want to change the 'main game loop' in my engine away from the traditional mega-function (which is really just an explicit single-threaded scheduler), and turn it into a quick little function that launches a bunch of jobs and sets the dependencies between them. This will more automatically make systems parallel with each other when possible and I think it's the right mentality 'going forward' to think of your software as a 'sea of jobs', big and small, and writing sequential operations is more about setting dependencies between jobs and describing how the data flows between them. I just bought a shiny new 6-core (12 thread) CPU at home so I have something to play with :)
Posted by: MikeNicolella | 09/13/2012 at 03:11 PM
Oh, man, don't get me started on the evils of threading. I believe in utilizing multiple processors, but threads are just asking for broken shit.
Before you retort, I've been writing all kinds of threaded code for years. The bottom line is that most programmers do threading badly and it overly complicates what should be simple code.
Let me make a note of that for an upcoming rant. Because threading is bad, mmmkay?
Posted by: Stephen Nichols | 09/13/2012 at 03:17 PM
You're probably thinking of all the bad newbie shit that people do. When I think of parallel code, 'threads' are simply used to run code on every core. If you're thinking of a 'lock', we're on separate pages. I've written lots of parallel code with zero locks. And I don't mean dealing with silly stuff like "lock free trees", either. I mean organize your data (most important!) and the dependencies of your code so that it's safe to go wide. On PS3 we had six ultra fast SIMD monsters - we had to be highly parallel, we were, and it was good, and simple.
Posted by: MikeNicolella | 09/13/2012 at 03:49 PM
I love macros.
I think i started to understand them as powerful tools on reading the contiki source code (which was then able to run on a C64) : http://www.contiki-os.org/.
The protothreads library especially is enlightning.
Now, there are still situations where i think i'm asking too much to macros.
For example, i'm in love with a C++ capability called function template. This is a very nice way of producing several almost equivalent functions with subtle changes, typically critical for optimisations.
C, unfortunately, do not offer the same capability. And when i have to write in C, which is quite often, trying to achieve the equivalent of function template leads to massive macros, which i do not like.
My typical macros are short, mostly one liners. They may combine several other macros, and therefore expand to quite a bit of code, but the main idea is : keep them short, for readability.
Unfortunately, macros to emulate function template are quite far from being short.
So, i would like to ask : do you know if there is a better/clever way to achieve function template emulation in plain C (C89 if possible, C99 if not) ?
Posted by: Coder39 | 09/25/2012 at 03:20 PM
Yeah, macros are not a good substitution for templates. They both have their place, to be sure. There are a certain class of problems that can't be solved by templates where macros shine. But, I did just write about that.
So, as for you question about emulating templates in C89/C99. Sorry to say, there's not a super-easy way to do that. Using macros to emulate templates is probably the Devil's work. However, there is an alternative you might consider: code generation!
I've used code generation over and over during my career. If you're poor (or cheap) you can write your own code generator that, based on some template syntax you invent, will generate the boilerplate code you need for your project.
If you don't mind spending some money (or don't want to roll your own), there are a few decent options out there:
1. "CodeSmith Generator." http://www.codesmithtools.com This is Windows-only and costs $299.
2. "GNU AutoGen" http://www.gnu.org/software/autogen/ Free, and a steep learning curve!
3. "Actifsource" http://www.actifsource.com Both free and paid versions. Cross platform.
You can do some good research here as well:
http://en.wikipedia.org/wiki/Comparison_of_code_generation_tools
Happy hunting!
Posted by: Stephen Nichols | 09/25/2012 at 05:20 PM
Thanks, very interesting reading.
Code generation seems a bit overkill for my problems. It seems targeted at a much higher abstraction layer, while i'm merely trying to optimise my code by pre-computing some parts within the function (and avoiding branches).
My own solution to this problem has been to use
#include "coreFunction.c"
The #include is preceded by a serie of #define, which trigger one specific path into the code.
For several functions with almost the same content, it's enough to include the same file several times, with just some different #define (it generates a different function name each time).
I don't like it much either, because the code within "coreFunction" file is, obviously, external and therefore not immediately reachable by eye, and it is not directly compilable either. From a debugging and reviewing perspective, it's less satisfying than templates.
Still, it does the job properly, and looks better than macros (although i've been advised to use macros for this scenario every once and a while).
Posted by: Coder39 | 09/26/2012 at 07:03 AM
It's funny to me how you say that code generation is overkill and then describe a system of code generation based on include files. Perhaps you meant that third-party code generation systems are too complicated to be useful in your limited use case? Dunno...
I've used the #include method you describe to solve several problems. It's handy, for sure. It's actually a sanctioned and useful method for C++ template-based metaprogramming too.
So, if that works for you, go for it. Code generation would get you to the same point and may actually generate code that you can read / debug. It's a balance, right?
Posted by: Stephen Nichols | 09/26/2012 at 10:58 AM
> Perhaps you meant that third-party code generation systems are too complicated to be useful in your limited use case?
Yes, something along this line.
Autogen in particular, i remember a bad experience, and it was in no way related to code optimisation. But to be fair that was long ago. Tools can improve. As well as programmer's experience.
Posted by: Coder39 | 09/26/2012 at 12:42 PM