Commit | Line | Data |
---|---|---|
d58b8198 MW |
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 | */ | |
31ac64c4 MW |
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) | |
d58b8198 MW |
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 | |
31ac64c4 MW |
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. | |
d58b8198 MW |
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 | |
31ac64c4 | 110 | #define FINALLY__FLAVOUR_CLANG_BLOCKS 2 |
d58b8198 MW |
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) == \ | |
31ac64c4 MW |
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) == \ | |
d58b8198 MW |
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 | ||
31ac64c4 MW |
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 | ||
d58b8198 MW |
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 |