optimise mavlink SS packet size (#3029)
[ExpressLRS.git] / src / lib / logging / VA_OPT.h
blob43028257558dfaad925e68e3ec008c39f23dad79
1 // Copyright (c) 2019 Will Wray https://keybase.io/willwray
2 //
3 // Distributed under the Boost Software License, Version 1.0.
4 // http://www.boost.org/LICENSE_1_0.txt
5 //
6 // Repo: https://github.com/willwray/VA_OPT
8 #pragma once
11 VA_OPT.hpp
12 ==========
14 Preprocessor utilities for testing emptiness of macro arguments
15 and for conditional expansion based on the emptiness of ARGS.
17 VA_OPT_SUPPORT(?)
18 1 if __VA_OPT__ support is detected else 0 (see platform note).
20 C++20's __VA_OPT__ finally provides a reliable test for empty ARGS.
21 The following macros use __VA_OPT__, if detected, otherwise they
22 provide 'polyfill' fallbacks (though with some failing edge cases).
24 IS_EMPTY(...)
25 1 if the ... ARGS is empty else 0.
27 IFN(...)
28 If ... ARGS are not empty then a trailing 'paren expression' (X)
29 is deparenthesized to X else the trailing (X) term is consumed.
30 E.g. IFN()(N) vanishes, while IFN(NotEmpty)(N) -> N
32 In other words, IFN(ARGS) is like __VA_OPT__, but with explicit
33 (ARGS) in place of an implicit __VA_ARGS__ check.
35 IFE(...)
36 If ... ARGS is empty expand trailing 'paren expression' (X) to X
37 else if ARGS are not empty consume the trailing paren expression.
38 E.g. IFE(NotEmpty)(E) vanishes, while IFE()(E) -> E
40 IFNE(...)(N,E...)
41 If ... ARGS are not empty expands to N else expands to E...
42 E.g. IFNE(ARG)(X,()) is equivalent to IFN(ARG)(X)IFE(ARG)(())
43 both put back a terminating () removed by the outer macro call.
45 Without VA_OPT_SUPPORT these 'emptiness' macros are not perfect;
46 IS_EMPTY, IFN, IFE, IFNE may cause a compile error ('too few args')
47 if the argument is a function-like macro name that expects ARG(s).
49 IBP(...)
50 IS_BEGIN_PARENS macro to test if an argument is parenthesised:
51 1 if ... ARGS begins with a 'paren expression' else 0.
53 Platform note: Current Sept 2019 __VA_OPT__ support:
54 -------------
55 Clang -std=c++2a enables it. GCC has it enabled without -std=c++2a
56 but warns "__VA_OPT__ is not available until C++2a" if another -std
57 flag is supplied along with -pedantic (dont know how to supress it).
58 MSVC TBD
60 Credits
61 -------
62 Props to pre-pro pioneers, particularly Paul Mensonides.
63 The 'emptiness' methods are adapted from BOOST_VMD_IS_EMPTY which,
64 . in turn, depends on BOOST Preprocessor's BOOST_PP_IS_BEGIN_PARENS
65 (adapted and exposed here as IBP 'Is Begin Parens'):
66 www.boost.org/doc/libs/1_71_0/libs/vmd
67 www.boost.org/doc/libs/1_71_0/libs/preprocessor
70 #define VA_ARG1(A0,A1,...) A1
71 // VA_EMPTY works only if __VA_OPT__ is supported, else always -> 1
72 #define VA_EMPTY(...) VA_ARG1(__VA_OPT__(,)0,1,)
74 // VA_OPT_SUPPORT helper macro for __VA_OPT__ feature detection.
75 // Adapted from https://stackoverflow.com/a/48045656/7443483
76 // Use as #if VA_OPT_SUPPORT(?)
77 #define VA_OPT_SUPPORT ! VA_EMPTY
79 #if VA_OPT_SUPPORT(?)
81 # define IS_EMPTY(...) VA_EMPTY(__VA_ARGS__)
82 # define IFN(...) VA_EAT __VA_OPT__(()VA_IDENT)
83 # define IFE(...) VA_IDENT __VA_OPT__(()VA_EAT)
84 # define IFNE(...) VA_ARGTAIL __VA_OPT__((,)VA_ARG0)
86 #else
88 # define IS_EMPTY(...) IFP(IBP(__VA_ARGS__))(IE_GEN_0,IE_IBP)(__VA_ARGS__)
89 # define IFN(...) IFP(IBP(__VA_ARGS__))(GEN_IDENT,EAT_OR_IDENT)(__VA_ARGS__)
90 # define IFE(...) IFP(IBP(__VA_ARGS__))(GEN_EAT,IDENT_OR_EAT)(__VA_ARGS__)
91 # define IFNE(...) IFP(IBP(__VA_ARGS__))(GEN_ARGTAIL,ARG0_OR_TAIL)(__VA_ARGS__)
93 #endif
95 #define VA_EAT(...)
96 #define VA_IDENT(...) __VA_ARGS__
97 #define VA_ARG0_(A0,...) A0
98 #define VA_ARG0(...) VA_ARG0_(__VA_ARGS__)
99 #define VA_ARGTAIL_(A0,...) __VA_ARGS__
100 #define VA_ARGTAIL(...) VA_ARGTAIL_(__VA_ARGS__)
102 // IFP helper macros to test IBP for IFN and IS_EMPTY
103 #define IFP_0(T,...) __VA_ARGS__
104 #define IFP_1(T,...) T
106 #define IFP_CAT(A,...) A##__VA_ARGS__
107 #define IFP(BP) IFP_CAT(IFP_,BP)
109 // IS_BEGIN_PAREN helper macros adapted from BOOST VMD
110 #define IBP_CAT_(A,...) A##__VA_ARGS__
111 #define IBP_CAT(A,...) IBP_CAT_(A,__VA_ARGS__)
113 #define IBP_ARG0_(A,...) A
114 #define IBP_ARG0(...) IBP_ARG0_(__VA_ARGS__)
116 #define IBP_IS_ARGS(...) 1
118 #define IBP_1 1,
119 #define IBP_IBP_IS_ARGS 0,
121 // IBP IS_BEGIN_PAREN returns 1 or 0 if ... ARGS is parenthesised
122 #define IBP(...) IBP_ARG0(IBP_CAT(IBP_, IBP_IS_ARGS __VA_ARGS__))
124 // IFN, IFE, IFNE and IF_EMPTY helpers without __VA_OPT__ support
125 #if ! VA_OPT_SUPPORT(?)
127 # define IBP_(T,...) IBP_ARG0(IBP_CAT(IF##T##_, IBP_IS_ARGS __VA_ARGS__))
129 // IS_EMPTY helper macros, depend on IBP
130 # define IE_REDUCE_IBP(...) ()
131 # define IE_GEN_0(...) 0
132 # define IE_IBP(...) IBP(IE_REDUCE_IBP __VA_ARGS__ ())
134 # define GEN_IDENT(...) VA_IDENT
135 # define GEN_EAT(...) VA_EAT
136 # define GEN_ARGTAIL(...) VA_ARGTAIL
137 # define GEN_ARG0(...) VA_ARG0
139 // IFN, IFE, IFNE helper macros
140 # define EAT_OR_IDENT(...) IBP_(N,IE_REDUCE_IBP __VA_ARGS__ ())
141 # define IFN_1 VA_EAT,
142 # define IFN_IBP_IS_ARGS VA_IDENT,
144 # define IDENT_OR_EAT(...) IBP_(E,IE_REDUCE_IBP __VA_ARGS__ ())
145 # define IFE_1 VA_IDENT,
146 # define IFE_IBP_IS_ARGS VA_EAT,
148 # define ARG0_OR_TAIL(...) IBP_(NE,IE_REDUCE_IBP __VA_ARGS__ ())
149 # define IFNE_1 VA_ARGTAIL,
150 # define IFNE_IBP_IS_ARGS VA_ARG0,
152 #endif // IFN and IF_EMPTY defs