8488161a6675f0fbcbf78413d8960f621f68d359
[finally] / finally.h
1 /* -*-c-*-
2 *
3 * Arrange to have code executed when a function ends
4 *
5 * (c) 2023 Straylight/Edgeware
6 */
7
8 /*----- Licensing notice --------------------------------------------------*
9 *
10 * This file is part of the `Finally' package.
11 *
12 * Finally is free software: you can redistribute it and/or modify it
13 * under the terms of the GNU Library General Public License as published
14 * by the Free Software Foundation; either version 2 of the License, or
15 * (at your option) any later version.
16 *
17 * Finally is distributed in the hope that it will be useful, but WITHOUT
18 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
19 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU Library General Public
20 * License for more details.
21 *
22 * You should have received a copy of the GNU Library General Public
23 * License along with Finally. If not, write to the Free Software
24 * Foundation, Inc., 59 Temple Place - Suite 330, Boston, MA 02111-1307,
25 * USA.
26 */
27
28 #ifndef FINALLY_H
29 #define FINALLY_H
30
31 /*----- Compatibility machinery -------------------------------------------*/
32
33 /* Some preliminary hacks for detecting compiler versions. */
34 #ifdef __GNUC__
35 # define FINALLY__GCC_P(maj, min) \
36 (__GNUC__ > (maj) || (__GNUC__ == (maj) && __GNUC_MINOR__ >= (min)))
37 #else
38 # define FINALLY__GCC_P(maj, min) 0
39 #endif
40
41 #ifdef __clang__
42 # define FINALLY__CLANG_P(maj, min) \
43 (__clang_major__ > (maj) || (__clang_major__ == (maj) && \
44 __clang_minor__ >= (min)))
45 #else
46 # define FINALLY__CLANG_P(maj, min) 0
47 #endif
48
49 /* If configuration machinery hasn't determined a flavour, then we'll take a
50 * rough guess based on compiler versions.
51 */
52 #if !defined(FINALLY_CONFIG_FLAVOUR) && \
53 !defined(__clang__) && FINALLY__GCC_P(3, 3)
54 # define FINALLY_CONFIG_FLAVOUR GCC_NESTED_FUNCTIONS
55 #endif
56
57 #if !defined(FINALLY_CONFIG_FLAVOUR)
58 # define FINALLY_CONFIG_FLAVOUR NIL
59 #endif
60
61 /*----- Macros provided ---------------------------------------------------*/
62
63 /* Before we start, a note about compatibility. We're using pretty esoteric
64 * compiler features here, and not all compilers support them. I'm most
65 * interested in GCC, which will work fine. This isn't going to work for
66 * other compilers, but lots of them try to impersonate GCC, and it's just
67 * not worth the effort to try to see through their lies.
68 *
69 * So the rules are: if you include this header file, you've either already
70 * made an effort to check that it's likely to work (e.g., by using the
71 * provided Autoconf macro), or you're willing to put up with whatever
72 * wreckage you end up with if the compiler doesn't actually like what we're
73 * saying here.
74 */
75
76 /* And a utility for pasting tokens after macro expansion. */
77 #define FINALLY__GLUE(x, y) FINALLY__DOGLUE(x, y)
78 #define FINALLY__DOGLUE(x, y) x##y
79
80 /* Now, we need a way to make a temporary name which isn't likely to conflict
81 * with anything else.
82 */
83 #define FINALLY__TMP(name) FINALLY__GLUE(_finally__##name##__, __LINE__)
84
85 /* And some other tricks which we may or may not need. */
86 #if defined(__cplusplus) && __cplusplus >= 201703
87 # define FINALLY__IGNORABLE fucksocks
88 #elif FINALLY__GCC_P(2, 5) || FINALLY__CLANG_P(3, 3)
89 # define FINALLY__IGNORABLE __attribute__((__unused__))
90 #endif
91 #ifndef FINALLY__IGNORABLE
92 # define FINALLY__IGNORABLE
93 #endif
94
95 /* Flavour selection machinery. */
96 #define FINALLY__FLAVOUR_NIL -1
97 #define FINALLY__FLAVOUR_GCC_NESTED_FUNCTIONS 1
98
99 #define FINALLY__SELECTED_FLAVOUR \
100 FINALLY__GLUE(FINALLY__FLAVOUR_, FINALLY_CONFIG_FLAVOUR)
101
102 /* @FINALLY(code)@
103 * @FINALLY_TAGGED(tag, code)@
104 *
105 * This macro may be placed anywhere within a function where a declaration is
106 * acceptable -- so at the head of a block in C89, or anywhere between
107 * statements in C99 or later. It causes @code@ to be executed when control
108 * leaves the enclosing scope. If it's placed at top-level within a
109 * function, for example, then the @code@ is executed when the function
110 * returns.
111 *
112 * There may be multiple @FINALLY@ invocations within a scope; they are
113 * executed in reverse order when control leaves the scope.
114 *
115 * Due to technical limitations, it's forbidden to have two @FINALLY@
116 * invocations on the same source line. This would seem to stymie writing a
117 * macro which expands to two invocations of @FINALLY@, which would be useful
118 * if those invocations were to end up in different scopes. Such a macro can
119 * still be written using @FINALLY_TAGGED@ instead, which takes an additional
120 * @tag@ argument, which may be any identifier: the rule then becomes that a
121 * source line may not contain two invocations of @FINALLY_TAGGED@ bearing
122 * the same @tag@. (It would be possible to overcome this limitation using
123 * the @__COUNTER__@ GCC extension, but I'd prefer not to limit the potential
124 * portability of this feature even further by insisting that something
125 * analogous exist in every supported compiler, and it would do users a
126 * disservice to create portability problems by having different rules on
127 * different compilers.)
128 */
129 #if defined(__cplusplus) && __cplusplus >= 201103
130 /* Oooh, we're being compiled by a C++ compiler. There's no need to deploy
131 * nonportable tricks, because we have an insane language that can already
132 * do what we need.
133 */
134
135 namespace finally {
136 template<typename F> class Finally {
137 F fn;
138 public:
139 Finally(F f) : fn{f} { ; }
140 ~Finally() { fn(); }
141 };
142 template<typename F> Finally<F> finally(F &&fn)
143 { return Finally<F>(fn); }
144 }
145
146 # define FINALLY_TAGGED(tag, body) \
147 FINALLY__IGNORABLE auto FINALLY__TMP(tag##__var) = \
148 finally::finally([&]{ body })
149
150 #elif FINALLY__GLUE(FINALLY__FLAVOUR_, FINALLY_CONFIG_FLAVOUR) == \
151 FINALLY__FLAVOUR_GCC_NESTED_FUNCTIONS
152 /* We're being compiled by GCC, or by something which wants to be mistaken
153 * for GCC at any rate. And GCC has nested functions. So, for each
154 * block, we'll define a dummy variable that we don't care about, and
155 * attach a nested function as its cleanup handler which will execute our
156 * cleanup code.
157 */
158
159 # define FINALLY_TAGGED(tag, body) \
160 __extension__ __inline__ \
161 void FINALLY__TMP(tag##__fn) \
162 (const int __attribute__((__unused__)) *_finally__hunoz) \
163 { body } \
164 __attribute__((__unused__, __cleanup__(FINALLY__TMP(tag##__fn)))) \
165 int FINALLY__TMP(tag##__var)
166
167 #elif FINALLY__GLUE(FINALLY__FLAVOUR_, FINALLY_CONFIG_FLAVOUR) == \
168 FINALLY__GLUE(FINALLY__FLAVOUR_, NIL)
169 /* We don't have a flavour to support this environment. */
170
171 # error "Compiler not supported. This isn't going to work."
172 #else
173 /* This case analysis should be exhaustive. */
174
175 # error "Internal error: `FINALLY_CONFIG_FLAVOUR' bungled."
176 #endif
177
178 /* We now have `FINALLY_TAGGED'; defining `FINALLY' is easy. The TAG here is
179 * guaranteed not conflict with any call on `FINALLY_TAGGED', since TAGs are
180 * required to be identifiers.
181 */
182 #define FINALLY(code) FINALLY_TAGGED(0, code)
183
184 /*----- That's all, folks -------------------------------------------------*/
185
186 #endif