chiark / gitweb /
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