aboutsummaryrefslogtreecommitdiffhomepage
path: root/content/2024-04-24-fn-wrapper-macro-magic/index.md
blob: b966a1a6c3c3c5f8331fcaf9df946aaeb004a4b6 (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
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
63
64
65
66
67
68
69
70
71
72
73
74
75
76
77
78
79
80
81
82
83
84
85
86
87
88
89
90
91
92
93
94
95
96
97
98
99
100
101
102
103
104
105
106
107
108
109
110
111
112
113
114
115
116
117
118
119
120
121
122
123
124
125
126
127
128
129
130
131
132
133
134
135
136
137
138
139
140
141
142
143
144
145
146
147
148
149
150
151
+++
title = "Generate function wrapper with macro magic"

[taxonomies]
tags = ["c++"]
+++

**Short disclaimer at front**.
This post discusses about c/cpp macros, which in general, I try to use as
little as possible or avoid at all. However, they have their application and
can be useful.
All I want to say is, use the right amount of macros for the corresponding
context and think about your colleagues if used in production. :^)

---

Recently I had the need to wrap a list of c functions and perform some common
work before and after each wrapped function had been invoked. Hence, the body
of each wrapper function was identical.
The wrappers were compiled and linked into a shared library which then was used
for **LD_PRELOAD**ing.

In this post I want to collect and archive a few approaches to generate the
required boilerplate code using preprocessor macros. Going from top to bottom,
the amount of macro code (*magic*?) increases.

To inspect the generated code, one can use the following command with all the
code examples from this post.
```
g++ -E file.cc | clang-format
```
> With the **-E** option, g++ stops after the preprocessor stage; output is
> written to *stdout*.
> Alternatively, one can just invoke the preprocessor **cpp file.cc**.

To syntax check the code examples, one can use the following command.
```
g++ -fsyntax-only file.cc
```

## Version 1

The first version provides a *function definition* like macro, here called
`WRAP`. When creating a wrapper function, this macro is directly used to define
the wrapper. One declares the signature in the WRAP macro, which is then
followed by curly braces including the wrapper body.

The benefit of this approach is that the wrapper implementation is quite
explicit and a reader, not familiar with the WRAP macro, might have a good
chance to reason about what the code is doing. The draw back is that bodies
have to be repeated for each wrapper.

```cpp
{{ include(path="content/2024-04-24-fn-wrapper-macro-magic/wrap-v1.cc") }}
```

> In the code examples, I use the `MOCK_WRAPPER_IMPL`<sup><a href="#sup-1">1</a></sup>
> macro, to mock away the common pre and post work and to invoke the real
> function.

## Version 2

The second version provides a simple approach to also generate the wrapper
bodies by explicitly writing out a typed list of arguments and a list of
argument names.

The main drawbacks are that the wrapper definition as well as the body of the
`WRAP` macro are somewhat obfuscated, which makes it hard for a reader to
reason about the code. Additionally, the arguments must be specified twice,
when defining a wrapper.

```cpp
{{ include(path="content/2024-04-24-fn-wrapper-macro-magic/wrap-v2.cc") }}
```

## Version 3

The third version provides macros to generate the list of typed arguments as
well as the list of argument names. Then for the different *arity* of the
wrapper function, corresponding `WRAP1, WRAP2` macros are provided to generate
the boilerplate code with the correct number of arguments.

The example only supports function with **one** or **two** arguments, but the
code can easily be extended.

```cpp
{{ include(path="content/2024-04-24-fn-wrapper-macro-magic/wrap-v3.cc") }}
```

## Version 4

The fourth version improves the third one by automatically using the correct
`TYPEDARGS` and `ARGS` macro depending on the *arity* of the wrapper function.

This is achieved by dynamically constructing the correct macro name during
preprocessing time. The technique to count the number of elements in the
variadic argument list [__VA_ARGS__][va-args] is presented in
[__VA_NARG__][va-narg].

```cpp, hide_lines=41-100
{{ include(path="content/2024-04-24-fn-wrapper-macro-magic/wrap-v4.cc") }}
```
> The implementation to handle empty arguments **CPP_ARGC() == 0** uses a gcc
> extension for the **##** operator. This is also supported by clang, but not
> by msvc. Maybe I will come back in the future to update this, if I have the
> need.

## Appendix: Listify codegen data

A hacker friend once showed me some neat macro magic, to organize data,
relevant for generating code in some list, which can then be used at multiple
places.

Since it fits topic wise, I want to archive this technique here as well.

The idea is that the list accepts a macro, which is applied to each entry. The
following is an example, using the `WRAP` macro from [version 4](#version-4).

```cpp
#define API(M) \
    M(int, foo, const char*) \
    M(int, bar, const char*, const char*) \
    M(int, baz, char, int, unsigned)

API(WRAP)
```

To close this appendix with some words the same great hacker friend once said:
> *This is the excel-ification of cpp code*.

## References

- Count number of elements in VA_ARGS [[ref][va-narg]]
- Variadic macros [[ref][va-args]]
- Preprocessor concatenation [[ref][cpp-concatenation]]
- Preprocessor macro argument handling [[ref][cpp-macro-args]]
- Preprocessor argument prescan [[ref][cpp-args-prescan]]

## Footnotes

<div id="sup-1"></div>

**1)** In the examples above, **MOCK_WRAPPER_IMPL** is a macro. However it does
not need to be one, as it can be seen in the [fn_hook.cc][fn-hook] example.

[va-narg]: https://groups.google.com/g/comp.std.c/c/d-6Mj5Lko_s
[va-args]: https://gcc.gnu.org/onlinedocs/cpp/Variadic-Macros.html
[cpp-concatenation]: https://gcc.gnu.org/onlinedocs/cpp/Concatenation.html
[cpp-macro-args]: https://gcc.gnu.org/onlinedocs/cpp/Macro-Arguments.html
[cpp-args-prescan]: https://gcc.gnu.org/onlinedocs/cpp/Argument-Prescan.html
[fn-hook]: https://git.memzero.de/testground/cpp-templates/tree/fn_hook.cc