chiark / gitweb /
Initial sketch.
[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) && \
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