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 | */ | |
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 |