Add half-hearted support for Clang, because its `blocks' are deficient.
[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) && defined(__clang__)
53 # if FINALLY__CLANG_P(2, 5)
54 # ifndef __BLOCKS__
55 # error "Clang detected, but blocks support is not available. \
56 This isn't going to work. Try setting the `-fblocks' compiler option."
57 # endif
58 # define FINALLY_CONFIG_FLAVOUR CLANG_BLOCKS
59 # else
60 # define FINALLY_CONFIG_FLAVOUR NIL
61 # endif
62 #endif
63
64 #if !defined(FINALLY_CONFIG_FLAVOUR) && FINALLY__GCC_P(3, 3)
65 # define FINALLY_CONFIG_FLAVOUR GCC_NESTED_FUNCTIONS
66 #endif
67
68 #if !defined(FINALLY_CONFIG_FLAVOUR)
69 # define FINALLY_CONFIG_FLAVOUR NIL
70 #endif
71
72 /*----- Macros provided ---------------------------------------------------*/
73
74 /* Before we start, a note about compatibility. We're using pretty esoteric
75 * compiler features here, and not all compilers support them. I'm most
76 * interested in GCC, which will work fine, and I'm just a tiny bit
77 * interested in Clang, so there's support for that too. This isn't going to
78 * work for other compilers, but lots of them try to impersonate GCC, and
79 * it's just not worth the effort to try to see through their lies.
80 *
81 * So the rules are: if you include this header file, you've either already
82 * made an effort to check that it's likely to work (e.g., by using the
83 * provided Autoconf macro), or you're willing to put up with whatever
84 * wreckage you end up with if the compiler doesn't actually like what we're
85 * saying here.
86 */
87
88 /* And a utility for pasting tokens after macro expansion. */
89 #define FINALLY__GLUE(x, y) FINALLY__DOGLUE(x, y)
90 #define FINALLY__DOGLUE(x, y) x##y
91
92 /* Now, we need a way to make a temporary name which isn't likely to conflict
93 * with anything else.
94 */
95 #define FINALLY__TMP(name) FINALLY__GLUE(_finally__##name##__, __LINE__)
96
97 /* And some other tricks which we may or may not need. */
98 #if defined(__cplusplus) && __cplusplus >= 201703
99 # define FINALLY__IGNORABLE fucksocks
100 #elif FINALLY__GCC_P(2, 5) || FINALLY__CLANG_P(3, 3)
101 # define FINALLY__IGNORABLE __attribute__((__unused__))
102 #endif
103 #ifndef FINALLY__IGNORABLE
104 # define FINALLY__IGNORABLE
105 #endif
106
107 /* Flavour selection machinery. */
108 #define FINALLY__FLAVOUR_NIL -1
109 #define FINALLY__FLAVOUR_GCC_NESTED_FUNCTIONS 1
110 #define FINALLY__FLAVOUR_CLANG_BLOCKS 2
111
112 #define FINALLY__SELECTED_FLAVOUR \
113 FINALLY__GLUE(FINALLY__FLAVOUR_, FINALLY_CONFIG_FLAVOUR)
114
115 /* @FINALLY(code)@
116 * @FINALLY_TAGGED(tag, code)@
117 *
118 * This macro may be placed anywhere within a function where a declaration is
119 * acceptable -- so at the head of a block in C89, or anywhere between
120 * statements in C99 or later. It causes @code@ to be executed when control
121 * leaves the enclosing scope. If it's placed at top-level within a
122 * function, for example, then the @code@ is executed when the function
123 * returns.
124 *
125 * There may be multiple @FINALLY@ invocations within a scope; they are
126 * executed in reverse order when control leaves the scope.
127 *
128 * Due to technical limitations, it's forbidden to have two @FINALLY@
129 * invocations on the same source line. This would seem to stymie writing a
130 * macro which expands to two invocations of @FINALLY@, which would be useful
131 * if those invocations were to end up in different scopes. Such a macro can
132 * still be written using @FINALLY_TAGGED@ instead, which takes an additional
133 * @tag@ argument, which may be any identifier: the rule then becomes that a
134 * source line may not contain two invocations of @FINALLY_TAGGED@ bearing
135 * the same @tag@. (It would be possible to overcome this limitation using
136 * the @__COUNTER__@ GCC extension, but I'd prefer not to limit the potential
137 * portability of this feature even further by insisting that something
138 * analogous exist in every supported compiler, and it would do users a
139 * disservice to create portability problems by having different rules on
140 * different compilers.)
141 */
142 #if defined(__cplusplus) && __cplusplus >= 201103
143 /* Oooh, we're being compiled by a C++ compiler. There's no need to deploy
144 * nonportable tricks, because we have an insane language that can already
145 * do what we need.
146 */
147
148 namespace finally {
149 template<typename F> class Finally {
150 F fn;
151 public:
152 Finally(F f) : fn{f} { ; }
153 ~Finally() { fn(); }
154 };
155 template<typename F> Finally<F> finally(F &&fn)
156 { return Finally<F>(fn); }
157 }
158
159 # define FINALLY_TAGGED(tag, body) \
160 FINALLY__IGNORABLE auto FINALLY__TMP(tag##__var) = \
161 finally::finally([&]{ body })
162
163 #elif FINALLY__GLUE(FINALLY__FLAVOUR_, FINALLY_CONFIG_FLAVOUR) == \
164 FINALLY__FLAVOUR_GCC_NESTED_FUNCTIONS
165 /* We're being compiled by GCC, or by something which wants to be mistaken
166 * for GCC at any rate. And GCC has nested functions. So, for each
167 * block, we'll define a dummy variable that we don't care about, and
168 * attach a nested function as its cleanup handler which will execute our
169 * cleanup code.
170 */
171
172 # define FINALLY_TAGGED(tag, body) \
173 __extension__ __inline__ \
174 void FINALLY__TMP(tag##__fn) \
175 (const int __attribute__((__unused__)) *_finally__hunoz) \
176 { body } \
177 __attribute__((__unused__, __cleanup__(FINALLY__TMP(tag##__fn)))) \
178 int FINALLY__TMP(tag##__var)
179
180 #elif FINALLY__GLUE(FINALLY__FLAVOUR_, FINALLY_CONFIG_FLAVOUR) == \
181 FINALLY__FLAVOUR_CLANG_BLOCKS
182 /* We're being compiled by Clang, so we're messing with the ugly `blocks'
183 * syntax. Unfortunately, blocks capture names from their outer
184 * environment by copying rather than by reference, so a `FINALLY' block
185 * is insensitive to changes to variables since its establishment. As a
186 * result of this, we declare a bug.
187 */
188
189 # define FINALLY_BUG_CAPTURE_COPIES 1
190
191 /* We'll need a separate cleanup handler, because we're not allowed to
192 * define a local function to do this. We'll attach this as the cleanup
193 * handler for the block containing the code that we want to run.
194 */
195 static __inline__ void _finally__runblk(void (^*_blk)(void))
196 { (*_blk)(); }
197
198 /* Now we're ready for the actual macro definition. */
199 # define FINALLY_TAGGED(tag, body) \
200 __attribute__((__unused__, __cleanup__(_finally__runblk))) \
201 void (^FINALLY__TMP(tag##__blk))(void) = ^{ body }
202
203 #elif FINALLY__GLUE(FINALLY__FLAVOUR_, FINALLY_CONFIG_FLAVOUR) == \
204 FINALLY__GLUE(FINALLY__FLAVOUR_, NIL)
205 /* We don't have a flavour to support this environment. */
206
207 # error "Compiler not supported. This isn't going to work."
208 #else
209 /* This case analysis should be exhaustive. */
210
211 # error "Internal error: `FINALLY_CONFIG_FLAVOUR' bungled."
212 #endif
213
214 /* Check for bugs. */
215 #if defined(FINALLY_BUG_CAPTURE_COPIES) && \
216 (!defined(FINALLY_TOLERATE_BUG_CAPTURE_COPIES) || \
217 !FINALLY_TOLERATE_BUG_CAPTURE_COPIES)
218 # error "Implementation captures variables by copying rather than by \
219 reference. Define `FINALLY_BUG_CAPTURE_COPIES' if you don't mind."
220 #endif
221
222 /* We now have `FINALLY_TAGGED'; defining `FINALLY' is easy. The TAG here is
223 * guaranteed not conflict with any call on `FINALLY_TAGGED', since TAGs are
224 * required to be identifiers.
225 */
226 #define FINALLY(code) FINALLY_TAGGED(0, code)
227
228 /*----- That's all, folks -------------------------------------------------*/
229
230 #endif