aboutsummaryrefslogtreecommitdiffhomepage
path: root/content/2024-04-24-fn-wrapper-macro-magic/index.md
diff options
context:
space:
mode:
Diffstat (limited to 'content/2024-04-24-fn-wrapper-macro-magic/index.md')
-rw-r--r--content/2024-04-24-fn-wrapper-macro-magic/index.md147
1 files changed, 147 insertions, 0 deletions
diff --git a/content/2024-04-24-fn-wrapper-macro-magic/index.md b/content/2024-04-24-fn-wrapper-macro-magic/index.md
new file mode 100644
index 0000000..fe45614
--- /dev/null
+++ b/content/2024-04-24-fn-wrapper-macro-magic/index.md
@@ -0,0 +1,147 @@
++++
+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-22-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-22-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-22-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-22-fn-wrapper-macro-magic/wrap-v4.cc") }}
+```
+
+## 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