chiark / gitweb /
Initial sketch.
[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 */
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