Overloading macros

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 previous 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 up to 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 (example supporting up to three arguments):

#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 for macro overloading, and specifically for our case, is by employing the VA_NUM_ARGS construct 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.

2 thoughts on “Overloading macros

  1. This is very cool stuff. Thanks for writing about this.

    I have been experimenting with the new C11 _Generic preprocessor feature which essentially lets you construct type safe function overloading capabilties sort of like C++, but without all the name mangling problems or other C++ baggage. But _Generic only directly works on functions with the same number of parameters. But combining C11 _Generic with your C99 macro techniques, I can now achieve full blown type safe function overloading with variable number of arguments.

    I put this to use in my new hash table library for C, LuaHashMap. (http://playcontrol.net/opensource/LuaHashMap). Feel free to check out the documentation and source code about it and let me know what you think.

  2. Problem Summary: In our project number of C++ applications are running at Solaris and windows and use the same libaray code. Recently we have introduced new Log level tracing at Solaris apps and modified the common library code. This change is impactiting the windows app’s performance. Instead of ignoring these new changes at run-time want to ignore at Static time (compile) time.

    Solution:

    enum LogTraceLevel { LOW, MEDIUM, HIGH, VERYHIGH };

    void displayMessage(const char* message)
    {
    std::cout << "\n\t [ ] \t" << message << std::endl;
    }

    void displayMessage(const char* message, LogTraceLevel lTL)
    {
    // if ( lTL < logLevel) ……
    std::cout << "\n\t [ ] \t" << message << std::endl;
    }

    #define MACRO_VAR_NUM_ARGS_IMPL(_1,_2, N,…) N
    #define MACRO_VAR_NUM_ARGS_IMPL_(tuple) MACRO_VAR_NUM_ARGS_IMPL tuple

    #define displayMessageOne(var1) displayMessage(var1)
    #define displayMessageTwo(var1, var2) //displayMessage(var1,var2)

    #define displayMessage(…) MACRO_VAR_NUM_ARGS_IMPL_((__VA_ARGS__, displayMessageTwo(__VA_ARGS__), displayMessageOne(__VA_ARGS__)))

    int main()
    {
    displayMessage("Lakshmana Maddineni");
    displayMessage("Lakshmana Kumar Maddineni", HIGH);
    }

    Now HIGH log displaymessages are not appearing at windows app's and this check is doing at compile time rather than runtime.

    Optional Parameters with C++ Macros

    Thanks to Syphorlate

Leave a Reply

Your email address will not be published. Required fields are marked *

You may use these HTML tags and attributes: <a href="" title=""> <abbr title=""> <acronym title=""> <b> <blockquote cite=""> <cite> <code> <del datetime=""> <em> <i> <q cite=""> <strike> <strong>