aboutsummaryrefslogtreecommitdiffhomepage
path: root/content/2024-04-24-fn-wrapper-macro-magic/wrap-v4.cc
blob: ea5e41e42df78ffce2318ebf34c4a806f0cafd16 (plain) (blame)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
// Get Nth argument.
#define CPP_NTH(_0, _1, _2, _3, n, ...) n
// Get number of arguments (uses gcc/clang extension for empty argument).
#define CPP_ARGC(...) CPP_NTH(_0, ##__VA_ARGS__, 3, 2, 1, 0)

// Utility to concatenate preprocessor tokens.
#define CONCAT2(lhs, rhs) lhs##rhs
#define CONCAT1(lhs, rhs) CONCAT2(lhs, rhs)

#define ARGS0()
#define ARGS1() a0
#define ARGS2() a1, ARGS1()
#define ARGS3() a2, ARGS2()

// Invoke correct ARGSn macro depending on #arguments.
#define ARGS(...) CONCAT1(ARGS, CPP_ARGC(__VA_ARGS__))()

#define TYPEDARGS0()
#define TYPEDARGS1(ty)      ty a0
#define TYPEDARGS2(ty, ...) ty a1, TYPEDARGS1(__VA_ARGS__)
#define TYPEDARGS3(ty, ...) ty a2, TYPEDARGS2(__VA_ARGS__)

// Invoke correct TYPEDARGSn macro depending on #arguments.
#define TYPEDARGS(...) CONCAT1(TYPEDARGS, CPP_ARGC(__VA_ARGS__))(__VA_ARGS__)

#define MOCK_WRAPPER_IMPL(ret, fn) \
  /* do common work */             \
  static ret wrap_##fn(...);

// Utility to generate wrapper boilerplate.
#define WRAP(ret, fn, ...)                    \
  MOCK_WRAPPER_IMPL(ret, fn)                  \
                                              \
  extern "C" ret fn(TYPEDARGS(__VA_ARGS__)) { \
    return wrap_##fn(ARGS(__VA_ARGS__));      \
  }

WRAP(int, foo, const char*)
WRAP(int, bar, const char*, const char*)
WRAP(int, baz, char, int, unsigned)

// -- TESTS --------------------------------------------------------------------

static_assert(CPP_ARGC() == 0);
static_assert(CPP_ARGC(int) == 1);
static_assert(CPP_ARGC(int, int) == 2);
static_assert(CPP_ARGC(int, int, int) == 3);