cplusplus.co.il

Overloading macros

Posted on: 31/08/2010

The feature of function overloading can prove to be pretty useful: it allows us to define a few versions of the same function, which differ in argument types, or even in Arity (ignoring variadic functions for the moment). Unfortunately, the C\C++ pre-processor does not allow overloading macros in the same way; It treats such attempts as redefinitions.

While we do not really need to overload a macro in order to handle different argument types (since macros ignore type information), many times it would be desired to overload a macro such that each version is able to handle a different number of arguments. This goal can actually be achieved through invocation of the VA_NUM_ARGS macro mentioned in my last post, as we will briefly demonstrate (the idea has also been mentioned under the comment section in the aforementioned post).

Suppose we would like to create a macro which is to return the MAX of its arguments. Such feature is supported both by std::max and by the (in)famous MAX macro. The extra requirement we shall make here, other than insisting on a macro solution, is that the implementation should work for any positive number of arguments up to a certain limit, and as long as there’s at least one argument.

In this post we will provide a solution which computes the MAX of upto three arguments, but we will put an emphasis on providing a solution which is easy to scale to work with any number of arguments.

One approach, cleverly raised by a colleague of mine, suggests the following:

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

#define MAX(a, ...) MAX_IMPL(a, __VA_ARGS__, a, a)
#define MAX_IMPL(a, b, c, ...) MAX2(a, MAX2(b, c))

This should clearly work for the MAX macro we are after, but certainly not for every function we would like to implement; For instance, if we were after a macro which can compute the average of its arguments, this approach would clearly not work.

The generic solution to macro overloading, and to our case in specific, is by employing the VA_NUM_ARGS construct along with the following set of macros:

#define macro_dispatcher(func, ...) \
            macro_dispatcher_(func, VA_NUM_ARGS(__VA_ARGS__))
#define macro_dispatcher_(func, nargs) \
            macro_dispatcher__(func, nargs)
#define macro_dispatcher__(func, nargs) \
            func ## nargs

The macro_dispatcher mechanism actually implements, like its name suggests, a kind of a dispatch mechanism (which operates through calculating the number of arguments being passed to the macro) that is actually able to simulate an overloading mechanism. Thus, enabling the following implementation for our desired max macro:

#define max(...) macro_dispatcher(max, __VA_ARGS__)(__VA_ARGS__)

#define max1(a) a
#define max2(a, b) ((a)>(b)?(a):(b))
#define max3(a, b, c) max2(max2(a, b), c)
// ...

// to verify, run the preprocessor alone (g++ -E):
max(1, 2, 3);

In this scenario, the macro_dispatcher mechanism transforms the max(1, 2, 3) call to max3(1, 2, 3) invocation, which yields the desired result.

Needless to say, that the proposed technique can be utilized easily just about anywhere where macro overloading would be useful.

Advertisements

Leave a Reply

Fill in your details below or click an icon to log in:

WordPress.com Logo

You are commenting using your WordPress.com account. Log Out / Change )

Twitter picture

You are commenting using your Twitter account. Log Out / Change )

Facebook photo

You are commenting using your Facebook account. Log Out / Change )

Google+ photo

You are commenting using your Google+ account. Log Out / Change )

Connecting to %s

%d bloggers like this: