1

I'm trying to utilize the preprocessor in C++ in a way which would ease my development progress enormously! I have a pretty simple issue, I work with a C API library whilst I use regular C++ classes. This library is based on callbacks/events, which I only can supply functions (not methods) to. Because of this I have a repetitive pattern of declaring a static and non-static function for each event:

public: // Here is the static method which is required
    static inline Vector StaticEventClickLeft(Vector vec) { return globalClass->EventClickLeft(vec); }
private: // And here is the method (i.e non-static)
    Vector EventClickLeft(Vector vec);

I want to create a macro which defines both these in a single line. It would decrease the size of my header by ten times at least! Here is my closest attempt (but far from enough):

#define DECL_EVENT(func, ret, ...) \
    public: static inline ret S ## Event ## func(__VA_ARGS__) { return globalClass->Event ## func(__VA_ARGS__); } \
    private: ret Event ## func(__VA_ARGS__);

If I use this macro like this DECL_EVENT(ClickLeft, Vector, Vector vec). This will be the output:

public: static inline Vector SEventClickLeft(Vector vec) { return globalClass->EventClickLeft(Vector vec); }
private: Vector EventClickLeft(Vector vec);

You can clearly see the problem. The static function calls the method and supplies the type of the argument as well as the name. Since the type is specified, it results in a compiler error; include/plugin.h:95:2: error: expected primary-expression before ‘TOKEN’ token .

So how can I solve this? There must be some solution, and I'm sure some macro expert can offer some help!

a3f
  • 8,517
  • 1
  • 41
  • 46
Elliott Darfink
  • 1,153
  • 14
  • 34
  • 1
    Boost.Preprocessor contains the tools you need to project out the second component of a pair and such things, which you could use for your construction. See [this question](http://stackoverflow.com/q/9897074/596781) for some examples. – Kerrek SB May 08 '12 at 21:49
  • Also note that using a static member as the destination for a C callback is not portable. Callbacks from C should **only** call C++ functions declared `extern "C"` otherwise you are dependent on C++ static member methods having the same ABI as C functions which may be holding on your platform but is in no way guaranteed. – Martin York May 08 '12 at 22:06
  • @LokiAstari I have an external "C" function `LoadPlugin` (see my comment on the first answer) which in turn uses static C functions to setup callbacks, so there is no worry. – Elliott Darfink May 08 '12 at 22:08

2 Answers2

1

Trying to abuse the preprocessor in C++ is usually a bad idea.

Is there any reason you can't use boost::signal for your needs?

As a bonus you could use std::function (or boost::function if you are stuck in C++03 land) to bind your member function to an object instance and get rid of the global.

Klemens Baum
  • 551
  • 3
  • 14
  • I'm not so experienced with `boost::signal` so I can fill you in with the details so you can decide whether it would work for me or not. I'm working with a closed API (I cannot alter it) and write extensions to this API. When an extension is loaded my extension receives a `LoadPlugin` (external) call. This function supplies a pointer to a function table (as argument), where I can set functions of interest to point at my functions, example; `pFunctionTable->pfnEventClickLeft = &MYCLASS::StaticEventClickLeft`. Do you think `boost::signal` would be the solution in this case? I'm stuck with C++03 – Elliott Darfink May 08 '12 at 21:45
  • Unfortunately if you cannot pass any arguments (usually in the form of a `void*`) along with the function pointer, you're bound to expose your state globally. – Klemens Baum May 08 '12 at 21:53
  • 3
    Calling abuse a bad idea is redundant. And automatically labeling usage of the preprocessor as abuse doesn't help anyone. – Benjamin Lindley May 08 '12 at 21:53
1

First, place parenthesis around your types, so the preprocessor can parse it. So you will call DECL_EVENT like this:

DECL_EVENT(ClickLeft, Vector, (Vector) vec)

Here are some macros that will retrieve the type and strip off the type(you will want to namespace them, I am leaving off the namespace just for demonstration):

#define EAT(...)
#define REM(...) __VA_ARGS__
#define STRIP(x) EAT x
#define PAIR(x) REM x

These macros work like this. When you write STRIP((Vector) vec) it will expand to vec. And when you write PAIR((Vector) vec) it will expand to Vector vec. Now next, you will want to do is to apply these macros to each argument that is passed in, so here is a simple APPLY macro that will let you do that for up to 8 arguments:

/* This counts the number of args */
#define NARGS_SEQ(_1,_2,_3,_4,_5,_6,_7,_8,N,...) N
#define NARGS(...) NARGS_SEQ(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1)

/* This will let macros expand before concating them */
#define PRIMITIVE_CAT(x, y) x ## y
#define CAT(x, y) PRIMITIVE_CAT(x, y)

/* This will call a macro on each argument passed in */
#define APPLY(macro, ...) CAT(APPLY_, NARGS(__VA_ARGS__))(macro, __VA_ARGS__)
#define APPLY_1(m, x1) m(x1)
#define APPLY_2(m, x1, x2) m(x1), m(x2)
#define APPLY_3(m, x1, x2, x3) m(x1), m(x2), m(x3)
#define APPLY_4(m, x1, x2, x3, x4) m(x1), m(x2), m(x3), m(x4)
#define APPLY_5(m, x1, x2, x3, x4, x5) m(x1), m(x2), m(x3), m(x4), m(x5)
#define APPLY_6(m, x1, x2, x3, x4, x5, x6) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6)
#define APPLY_7(m, x1, x2, x3, x4, x5, x6, x7) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7)
#define APPLY_8(m, x1, x2, x3, x4, x5, x6, x7, x8) m(x1), m(x2), m(x3), m(x4), m(x5), m(x6), m(x7), m(x8)

Now heres how you can write your DECL_EVENT macro:

#define DECL_EVENT(func, ret, ...) \
public: static inline ret S ## Event ## func(APPLY(PAIR, __VA_ARGS__)) { return globalClass->Event ## func(APPLY(STRIP, __VA_ARGS__)); } \
private: ret Event ## func(APPLY(PAIR, __VA_ARGS__));

Note: This probably won't work in MSVC, since they have a buggy preprocessor(although there are workarounds).

Paul Fultz II
  • 17,682
  • 13
  • 62
  • 59