« Yes, your code sucks... | Main | Reference counting smart pointers are for retards. »

09/03/2012

Comments

Feed You can follow this conversation by subscribing to the comment feed for this post.

MikeNicolella

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);

Stephen Nichols

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!

MikeNicolella

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.

Stephen Nichols

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!

MikeNicolella

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 :)

Stephen Nichols

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.

MikeNicolella

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 :)

Stephen Nichols

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?

MikeNicolella

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.

Coder39

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) ?

Stephen Nichols

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!

Coder39

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).

Stephen Nichols

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?

Coder39

> 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.

The comments to this entry are closed.