1 /*
2 * Copyright (c) 2020, Oracle and/or its affiliates. All rights reserved.
3 * DO NOT ALTER OR REMOVE COPYRIGHT NOTICES OR THIS FILE HEADER.
4 *
5 * This code is free software; you can redistribute it and/or modify it
6 * under the terms of the GNU General Public License version 2 only, as
7 * published by the Free Software Foundation. Oracle designates this
8 * particular file as subject to the "Classpath" exception as provided
9 * by Oracle in the LICENSE file that accompanied this code.
10 *
11 * This code is distributed in the hope that it will be useful, but WITHOUT
12 * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
13 * FITNESS FOR A PARTICULAR PURPOSE. See the GNU General Public License
14 * version 2 for more details (a copy is included in the LICENSE file that
15 * accompanied this code).
16 *
17 * You should have received a copy of the GNU General Public License version
18 * 2 along with this work; if not, write to the Free Software Foundation,
19 * Inc., 51 Franklin St, Fifth Floor, Boston, MA 02110-1301 USA.
20 *
21 * Please contact Oracle, 500 Oracle Parkway, Redwood Shores, CA 94065 USA
22 * or visit www.oracle.com if you need additional information or have any
23 * questions.
24 */
25
26 #ifndef Toolbox_h
27 #define Toolbox_h
28
29
30 #include <algorithm>
31 #include "tstrings.h"
32
33
34 /**
35 * Placeholder for API not falling into any particular category.
36 */
37
38
39
40 struct deletePtr {
41 template <class PtrT>
operatordeletePtr42 void operator () (PtrT ptr) const {
43 delete ptr;
44 }
45 };
46
47
48 /**
49 * Deletes all pointers in the given range of items.
50 */
51 template <class It>
deleteAll(It b,It e)52 void deleteAll(It b, It e) {
53 std::for_each(b, e, deletePtr());
54 }
55
56 /**
57 * Deletes all pointers from the container.
58 */
59 template <class Ctnr>
deleteAll(Ctnr & ctnr)60 void deleteAll(Ctnr& ctnr) {
61 deleteAll(std::begin(ctnr), std::end(ctnr));
62 }
63
64
65 /**
66 * Applies std::for_each() to the given object that has begin() and end()
67 * methods.
68 */
69 template <class Ctnr, class Fn>
forEach(Ctnr & ctnr,Fn fn)70 void forEach(Ctnr& ctnr, Fn fn) {
71 std::for_each(std::begin(ctnr), std::end(ctnr), fn);
72 }
73
74 template <class Ctnr, class Fn>
forEach(const Ctnr & ctnr,Fn fn)75 void forEach(const Ctnr& ctnr, Fn fn) {
76 std::for_each(std::begin(ctnr), std::end(ctnr), fn);
77 }
78
79
80 /**
81 * Runs the given functor from destructor. Don't use directly.
82 * This is just a helper for runAtEndOfScope().
83 */
84 template <class Fn>
85 class AtEndOfScope {
86 Fn func;
87 bool theAbort;
88 public:
AtEndOfScope(Fn f)89 explicit AtEndOfScope(Fn f): func(f), theAbort(false) {
90 }
~AtEndOfScope()91 ~AtEndOfScope() {
92 if (!theAbort) {
93 JP_NO_THROW(func());
94 }
95 }
96 void abort(bool v=true) {
97 theAbort = v;
98 }
99 };
100
101 /**
102 * Helper to create AtEndOfScope instance without need to
103 * specify template type explicitly. Like std::make_pair to construct
104 * std::pair instance.
105 *
106 * Use case:
107 * Say you need to call a function (foo()) at exit from another
108 * function (bar()).
109 * You will normally do:
110 * void bar() {
111 * workload();
112 * foo();
113 * }
114 *
115 * If workload() can throw exceptions things become little bit more
116 * complicated:
117 * void bar() {
118 * JP_NO_THROW(workload());
119 * foo();
120 * }
121 *
122 * If there is branching in bar() it is little bit more complicated again:
123 * int bar() {
124 * if (...) {
125 * JP_NO_THROW(workload());
126 * foo();
127 * return 0;
128 * }
129 * if (...) {
130 * JP_NO_THROW(workload2());
131 * foo();
132 * return 1;
133 * }
134 * foo();
135 * return 2;
136 * }
137 *
138 * So for relatively complex bar() this approach will end up with
139 * missing foo() call. The standard solution to avoid errors like this,
140 * is to call foo() from some object's destructor, i.e. delegate
141 * responsibility to ensure it is always called to compiler:
142 *
143 * struct FooCaller {
144 * ~FooCaller() { JP_NO_THROW(foo()); }
145 * };
146 *
147 * int bar() {
148 * FooCaller fooCaller;
149 * if (...) {
150 * JP_NO_THROW(workload());
151 * return 0;
152 * }
153 * if (...) {
154 * JP_NO_THROW(workload2());
155 * foo();
156 * }
157 * return 2;
158 * }
159 *
160 * However it is annoying to explicitly create FooCaller-like types
161 * for tasks like this. Use of runAtEndOfScope() saves you from this:
162 *
163 * int bar() {
164 * const auto fooCaller = runAtEndOfScope(foo);
165 * if (...) {
166 * JP_NO_THROW(workload());
167 * return 0;
168 * }
169 * if (...) {
170 * JP_NO_THROW(workload2());
171 * foo();
172 * }
173 * return 2;
174 * }
175 *
176 */
177 template <class Fn>
runAtEndOfScope(Fn func)178 AtEndOfScope<Fn> runAtEndOfScope(Fn func) {
179 return AtEndOfScope<Fn>(func);
180 }
181
182 #endif // #ifndef Toolbox_h
183