Add half-hearted support for Clang, because its `blocks' are deficient.
[finally] / finally.h
CommitLineData
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. \
56This 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 \
219reference. 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