1

I would like to define lots of functions dispatchers. Based on a flag, I will call one or the other. Flag checking is always the same, and namings also.

This is an example code:

int myfunction(int a,int b)
{
    if (flag)
        return myfunction_flag1(a, b);
    else
        return myfunction_flag0(a, b);
}

Since this code will repeat for each my functions (actual usecase uses more lines than just this if else but it was simplified for the question purpose), I would like to write it as a MACRO.

#define DISPATCHER(function_type, function_name, ...) \
function_type function_name(__VA_ARGS__)              \
{                                                     \
    if (flag)                                         \
        return function_name ## flag1(__VA_ARGS__);   \
    else                                              \
        return function_name ## flag0(__VA_ARGS__);   \
}                                                     \

And then have a lot of :

DISPATCHER(int, myfunction, int a, int b)
DISPATCHER(int, myfunction2, int a, int b, int c)
DISPATCHER(int, myfunction3, int a)

...

However, I can't call function_name ## flag1(\__VA_ARGS__) as \__VA_ARGS__ contains the arguments types. Is there a way to do this another way ?

Paul Ogilvie
  • 25,048
  • 4
  • 23
  • 41
Charles B
  • 160
  • 1
  • 12
  • there is 2017 ... why not use C++ ? – Jacek Cz Oct 27 '17 at 15:53
  • 3
    Why do you want it as a macro? Why not use an inline function? (trust your compiler to optimize unnecessary function calls) – Ivan Rubinson Oct 27 '17 at 15:54
  • Flag can change on runtime. I would like to expose a clean function to my users. And then in backend check my flag and call the appropriate functions (that have same signatures as original one). – Charles B Oct 27 '17 at 15:58
  • 1
    I believe, if sticking to macros, you would have to follow macro definition similar to one described [here](https://stackoverflow.com/a/12540675/7063478), and redo it to have calling convention like: `DISPATCHER(int, myfunction, int, a, int, b) DISPATCHER(int, myfunction2, int, a, int, b, int, c)` and starting macro like `#define DISPATCHER(rettype, fnname, type1, var1, ...)` – nowaqq Oct 27 '17 at 16:27
  • @JacekCz Because it’s a different language, and even if everybody agreed it was better (I certainly do, but others don’t), migration isn’t always as trivial as it seems it should be. – Daniel H Oct 27 '17 at 16:41
  • 1) not a good idea to use a global `flag` suggest passing a first argument. 2) the macro definition should not have a back slash `\\` at the end of the last line. – user3629249 Oct 27 '17 at 18:51

2 Answers2

0

One possibility is to change the macro call syntax to something more like DISPATCHER(int, myfunction2, int, a, int, b, int, c) where variable names and types are passed as separate macro parameters. The macro expansion can then use the type info when building the prototype and just use the parameter names when calling the sub-functions. This requires you to access all of the individual parameters by name, so you'd have to know the number of arguments in advance. Lots of functions with different argument counts would make this method rather awkward.

Another option is to refactor the functions to use varargs instead of individual parameters. The macro expansion then won't need to process an argument list, just pass its own va_list to each of the sub-functions (which can now be hardcoded). This avoids the macro issues, but you're just trading them for the problems associated with varargs.

The last few times I've needed to do something like this, I've just written a short script that can pre-process a source file and generate the desired code. Languages with better text-processing capabilities (like Python or Ruby) make this a much easier job. The preprocessor was not really designed for metaprogramming so while it may be possible to get it working for something like this, it's usually faster, easier, and less bug-prone to use a different tool.

bta
  • 43,959
  • 6
  • 69
  • 99
0

You don't need to specify parameter names here; any reasonably unique name in your macro will do the trick, so long as you match the argument names to corresponding parameter names. It may be easier to just generate those names than to do the work of passing it down.

This leaves us with the task of expanding things like this:

DISPATCHER(int, myfunction, int, int)
DISPATCHER(int, myfunction2, int, int, int)
DISPATCHER(int, myfunction3, int)

I prefer not just jumping to ... for semantically related things, so I suggest instead a three-argument macro, where the third argument itself is a list of types:

DISPATCHER(int, myfunction, (int, int))
DISPATCHER(int, myfunction2, (int, int, int))
DISPATCHER(int, myfunction3, (int))

Using the boost preprocessor, you can do this:

#include <boost/preprocessor/repetition/enum.hpp>
#include <boost/preprocessor/tuple/size.hpp>
#include <boost/preprocessor/tuple/elem.hpp>
#include <boost/preprocessor/cat.hpp>

#define PARAMETER_M(z,OFFSET_,DATA_) \
   BOOST_PP_TUPLE_ELEM(OFFSET_,DATA_) BOOST_PP_CAT(arg_,OFFSET_)
#define ARGUMENT_M(z,OFFSET_,DATA_) \
   BOOST_PP_CAT(arg_,OFFSET_)

#define DISPATCH(RETVAL_,NAME_,ARGLIST_) \
   RETVAL_ NAME_ ( BOOST_PP_ENUM(BOOST_PP_TUPLE_SIZE(ARGLIST_), PARAMETER_M,ARGLIST_) ) \
   { \
         if (flag) return NAME_ ## flag1 \
         ( BOOST_PP_ENUM(BOOST_PP_TUPLE_SIZE(ARGLIST_), ARGUMENT_M, ARGLIST_) ); \
         else return NAME_ ## flag0 \
         ( BOOST_PP_ENUM(BOOST_PP_TUPLE_SIZE(ARGLIST_), ARGUMENT_M, ARGLIST_) ); \
   }

This implementation uses BOOST_PP_ENUM to generate the parameter and argument list. In both cases, ARG is our type tuple (e.g., (int,int,short,char*)).

PARAMETER_M is the MACRO that generates parameter lists by picking the OFFSET_-th parameter from the type list, following it by arg_ pasted to OFFSET_.

ARGUMENT_M is the MACRO that generates argument lists by simply pasting arg_ to OFFSET_.

H Walters
  • 2,634
  • 1
  • 11
  • 13