blob: 7b4bc1216c0836df04abc0234ec05cd3ee2ac1ff [file] [log] [blame]
Igor Murashkinfb326cf2015-07-23 16:53:53 -07001/*
2 * Copyright (C) 2015 The Android Open Source Project
3 *
4 * Licensed under the Apache License, Version 2.0 (the "License");
5 * you may not use this file except in compliance with the License.
6 * You may obtain a copy of the License at
7 *
8 * http://www.apache.org/licenses/LICENSE-2.0
9 *
10 * Unless required by applicable law or agreed to in writing, software
11 * distributed under the License is distributed on an "AS IS" BASIS,
12 * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13 * See the License for the specific language governing permissions and
14 * limitations under the License.
15 */
16
17#ifndef ART_RUNTIME_BASE_OUT_H_
18#define ART_RUNTIME_BASE_OUT_H_
19
20#include <base/macros.h>
21#include <base/logging.h>
22
23#include <memory>
24// A zero-overhead abstraction marker that means this value is meant to be used as an out
25// parameter for functions. It mimics semantics of a pointer that the function will
26// dereference and output its value into.
27//
28// Inspired by the 'out' language keyword in C#.
29//
30// Declaration example:
31// int do_work(size_t args, out<int> result);
32// // returns 0 on success, sets result, otherwise error code
33//
34// Use-site example:
35// // (1) -- out of a local variable or field
36// int res;
37// if (do_work(1, outof(res)) {
38// cout << "success: " << res;
39// }
40// // (2) -- out of an iterator
41// std::vector<int> list = {1};
42// std::vector<int>::iterator it = list.begin();
43// if (do_work(2, outof_iterator(*it)) {
44// cout << "success: " << list[0];
45// }
46// // (3) -- out of a pointer
47// int* array = &some_other_value;
48// if (do_work(3, outof_ptr(array))) {
49// cout << "success: " << *array;
50// }
51//
52// The type will also automatically decay into a C-style pointer for compatibility
53// with calling legacy code that expect pointers.
54//
55// Declaration example:
56// void write_data(int* res) { *res = 5; }
57//
58// Use-site example:
59// int data;
60// write_data(outof(res));
61// // data is now '5'
62// (The other outof_* functions can be used analogously when the target is a C-style pointer).
63//
64// ---------------
65//
66// Other typical pointer operations such as addition, subtraction, etc are banned
67// since there is exactly one value being output.
68//
69namespace art {
70
71// Forward declarations. See below for specific functions.
72template <typename T>
73struct out_convertible; // Implicitly converts to out<T> or T*.
74
75// Helper function that automatically infers 'T'
76//
77// Returns a type that is implicitly convertible to either out<T> or T* depending
78// on the call site.
79//
80// Example:
81// int do_work(size_t args, out<int> result);
82// // returns 0 on success, sets result, otherwise error code
83//
84// Usage:
85// int res;
86// if (do_work(1, outof(res)) {
87// cout << "success: " << res;
88// }
89template <typename T>
90out_convertible<T> outof(T& param) ALWAYS_INLINE;
91
92// Helper function that automatically infers 'T' from a container<T>::iterator.
93// To use when the argument is already inside an iterator.
94//
95// Returns a type that is implicitly convertible to either out<T> or T* depending
96// on the call site.
97//
98// Example:
99// int do_work(size_t args, out<int> result);
100// // returns 0 on success, sets result, otherwise error code
101//
102// Usage:
103// std::vector<int> list = {1};
104// std::vector<int>::iterator it = list.begin();
105// if (do_work(2, outof_iterator(*it)) {
106// cout << "success: " << list[0];
107// }
108template <typename It>
109auto ALWAYS_INLINE outof_iterator(It iter)
110 -> out_convertible<typename std::remove_reference<decltype(*iter)>::type>;
111
112// Helper function that automatically infers 'T'.
113// To use when the argument is already a pointer.
114//
115// ptr must be not-null, else a DCHECK failure will occur.
116//
117// Returns a type that is implicitly convertible to either out<T> or T* depending
118// on the call site.
119//
120// Example:
121// int do_work(size_t args, out<int> result);
122// // returns 0 on success, sets result, otherwise error code
123//
124// Usage:
125// int* array = &some_other_value;
126// if (do_work(3, outof_ptr(array))) {
127// cout << "success: " << *array;
128// }
129template <typename T>
130out_convertible<T> outof_ptr(T* ptr) ALWAYS_INLINE;
131
132// Zero-overhead wrapper around a non-null non-const pointer meant to be used to output
133// the result of parameters. There are no other extra guarantees.
134//
135// The most common use case is to treat this like a typical pointer argument, for example:
136//
137// void write_out_5(out<int> x) {
138// *x = 5;
139// }
140//
141// The following operations are supported:
142// operator* -> use like a pointer (guaranteed to be non-null)
143// == and != -> compare against other pointers for (in)equality
144// begin/end -> use in standard C++ algorithms as if it was an iterator
145template <typename T>
146struct out {
147 // Has to be mutable lref. Otherwise how would you write something as output into it?
148 explicit inline out(T& param)
149 : param_(param) {}
150
151 // Model a single-element iterator (or pointer) to the parameter.
152 inline T& operator *() {
153 return param_;
154 }
155
Igor Murashkinbc1d78d2015-07-30 16:39:45 -0700156 // Model dereferencing fields/methods on a pointer.
157 inline T* operator->() {
158 return std::addressof(param_);
159 }
160
Igor Murashkinfb326cf2015-07-23 16:53:53 -0700161 //
162 // Comparison against this or other pointers.
163 //
164 template <typename T2>
165 inline bool operator==(const T2* other) const {
166 return std::addressof(param_) == other;
167 }
168
169 template <typename T2>
170 inline bool operator==(const out<T>& other) const {
171 return std::addressof(param_) == std::addressof(other.param_);
172 }
173
174 // An out-parameter is never null.
175 inline bool operator==(std::nullptr_t) const {
176 return false;
177 }
178
179 template <typename T2>
180 inline bool operator!=(const T2* other) const {
181 return std::addressof(param_) != other;
182 }
183
184 template <typename T2>
185 inline bool operator!=(const out<T>& other) const {
186 return std::addressof(param_) != std::addressof(other.param_);
187 }
188
189 // An out-parameter is never null.
190 inline bool operator!=(std::nullptr_t) const {
191 return true;
192 }
193
194 //
195 // Iterator interface implementation. Use with standard algorithms.
196 // TODO: (add items in iterator_traits if this is truly useful).
197 //
198
199 inline T* begin() {
200 return std::addressof(param_);
201 }
202
203 inline const T* begin() const {
204 return std::addressof(param_);
205 }
206
207 inline T* end() {
208 return std::addressof(param_) + 1;
209 }
210
211 inline const T* end() const {
212 return std::addressof(param_) + 1;
213 }
214
215 private:
216 T& param_;
217};
218
219//
220// IMPLEMENTATION DETAILS
221//
222
223//
224// This intermediate type should not be used directly by user code.
225//
226// It enables 'outof(x)' to be passed into functions that expect either
227// an out<T> **or** a regular C-style pointer (T*).
228//
229template <typename T>
230struct out_convertible {
231 explicit inline out_convertible(T& param)
232 : param_(param) {
233 }
234
235 // Implicitly convert into an out<T> for standard usage.
236 inline operator out<T>() {
237 return out<T>(param_);
238 }
239
240 // Implicitly convert into a '*' for legacy usage.
241 inline operator T*() {
242 return std::addressof(param_);
243 }
244 private:
245 T& param_;
246};
247
248// Helper function that automatically infers 'T'
249template <typename T>
250inline out_convertible<T> outof(T& param) {
251 return out_convertible<T>(param);
252}
253
254// Helper function that automatically infers 'T'.
255// To use when the argument is already inside an iterator.
256template <typename It>
257inline auto outof_iterator(It iter)
258 -> out_convertible<typename std::remove_reference<decltype(*iter)>::type> {
259 return outof(*iter);
260}
261
262// Helper function that automatically infers 'T'.
263// To use when the argument is already a pointer.
264template <typename T>
265inline out_convertible<T> outof_ptr(T* ptr) {
266 DCHECK(ptr != nullptr);
267 return outof(*ptr);
268}
269
270// Helper function that automatically infers 'T'.
271// Forwards an out parameter from one function into another.
272template <typename T>
273inline out_convertible<T> outof_forward(out<T>& out_param) {
Igor Murashkinbc1d78d2015-07-30 16:39:45 -0700274 T& param = *out_param;
Igor Murashkinfb326cf2015-07-23 16:53:53 -0700275 return out_convertible<T>(param);
276}
277
278} // namespace art
279#endif // ART_RUNTIME_BASE_OUT_H_