blob: 7b4bc1216c0836df04abc0234ec05cd3ee2ac1ff [file] [log] [blame]
/*
* Copyright (C) 2015 The Android Open Source Project
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
* You may obtain a copy of the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS,
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
* See the License for the specific language governing permissions and
* limitations under the License.
*/
#ifndef ART_RUNTIME_BASE_OUT_H_
#define ART_RUNTIME_BASE_OUT_H_
#include <base/macros.h>
#include <base/logging.h>
#include <memory>
// A zero-overhead abstraction marker that means this value is meant to be used as an out
// parameter for functions. It mimics semantics of a pointer that the function will
// dereference and output its value into.
//
// Inspired by the 'out' language keyword in C#.
//
// Declaration example:
// int do_work(size_t args, out<int> result);
// // returns 0 on success, sets result, otherwise error code
//
// Use-site example:
// // (1) -- out of a local variable or field
// int res;
// if (do_work(1, outof(res)) {
// cout << "success: " << res;
// }
// // (2) -- out of an iterator
// std::vector<int> list = {1};
// std::vector<int>::iterator it = list.begin();
// if (do_work(2, outof_iterator(*it)) {
// cout << "success: " << list[0];
// }
// // (3) -- out of a pointer
// int* array = &some_other_value;
// if (do_work(3, outof_ptr(array))) {
// cout << "success: " << *array;
// }
//
// The type will also automatically decay into a C-style pointer for compatibility
// with calling legacy code that expect pointers.
//
// Declaration example:
// void write_data(int* res) { *res = 5; }
//
// Use-site example:
// int data;
// write_data(outof(res));
// // data is now '5'
// (The other outof_* functions can be used analogously when the target is a C-style pointer).
//
// ---------------
//
// Other typical pointer operations such as addition, subtraction, etc are banned
// since there is exactly one value being output.
//
namespace art {
// Forward declarations. See below for specific functions.
template <typename T>
struct out_convertible; // Implicitly converts to out<T> or T*.
// Helper function that automatically infers 'T'
//
// Returns a type that is implicitly convertible to either out<T> or T* depending
// on the call site.
//
// Example:
// int do_work(size_t args, out<int> result);
// // returns 0 on success, sets result, otherwise error code
//
// Usage:
// int res;
// if (do_work(1, outof(res)) {
// cout << "success: " << res;
// }
template <typename T>
out_convertible<T> outof(T& param) ALWAYS_INLINE;
// Helper function that automatically infers 'T' from a container<T>::iterator.
// To use when the argument is already inside an iterator.
//
// Returns a type that is implicitly convertible to either out<T> or T* depending
// on the call site.
//
// Example:
// int do_work(size_t args, out<int> result);
// // returns 0 on success, sets result, otherwise error code
//
// Usage:
// std::vector<int> list = {1};
// std::vector<int>::iterator it = list.begin();
// if (do_work(2, outof_iterator(*it)) {
// cout << "success: " << list[0];
// }
template <typename It>
auto ALWAYS_INLINE outof_iterator(It iter)
-> out_convertible<typename std::remove_reference<decltype(*iter)>::type>;
// Helper function that automatically infers 'T'.
// To use when the argument is already a pointer.
//
// ptr must be not-null, else a DCHECK failure will occur.
//
// Returns a type that is implicitly convertible to either out<T> or T* depending
// on the call site.
//
// Example:
// int do_work(size_t args, out<int> result);
// // returns 0 on success, sets result, otherwise error code
//
// Usage:
// int* array = &some_other_value;
// if (do_work(3, outof_ptr(array))) {
// cout << "success: " << *array;
// }
template <typename T>
out_convertible<T> outof_ptr(T* ptr) ALWAYS_INLINE;
// Zero-overhead wrapper around a non-null non-const pointer meant to be used to output
// the result of parameters. There are no other extra guarantees.
//
// The most common use case is to treat this like a typical pointer argument, for example:
//
// void write_out_5(out<int> x) {
// *x = 5;
// }
//
// The following operations are supported:
// operator* -> use like a pointer (guaranteed to be non-null)
// == and != -> compare against other pointers for (in)equality
// begin/end -> use in standard C++ algorithms as if it was an iterator
template <typename T>
struct out {
// Has to be mutable lref. Otherwise how would you write something as output into it?
explicit inline out(T& param)
: param_(param) {}
// Model a single-element iterator (or pointer) to the parameter.
inline T& operator *() {
return param_;
}
// Model dereferencing fields/methods on a pointer.
inline T* operator->() {
return std::addressof(param_);
}
//
// Comparison against this or other pointers.
//
template <typename T2>
inline bool operator==(const T2* other) const {
return std::addressof(param_) == other;
}
template <typename T2>
inline bool operator==(const out<T>& other) const {
return std::addressof(param_) == std::addressof(other.param_);
}
// An out-parameter is never null.
inline bool operator==(std::nullptr_t) const {
return false;
}
template <typename T2>
inline bool operator!=(const T2* other) const {
return std::addressof(param_) != other;
}
template <typename T2>
inline bool operator!=(const out<T>& other) const {
return std::addressof(param_) != std::addressof(other.param_);
}
// An out-parameter is never null.
inline bool operator!=(std::nullptr_t) const {
return true;
}
//
// Iterator interface implementation. Use with standard algorithms.
// TODO: (add items in iterator_traits if this is truly useful).
//
inline T* begin() {
return std::addressof(param_);
}
inline const T* begin() const {
return std::addressof(param_);
}
inline T* end() {
return std::addressof(param_) + 1;
}
inline const T* end() const {
return std::addressof(param_) + 1;
}
private:
T& param_;
};
//
// IMPLEMENTATION DETAILS
//
//
// This intermediate type should not be used directly by user code.
//
// It enables 'outof(x)' to be passed into functions that expect either
// an out<T> **or** a regular C-style pointer (T*).
//
template <typename T>
struct out_convertible {
explicit inline out_convertible(T& param)
: param_(param) {
}
// Implicitly convert into an out<T> for standard usage.
inline operator out<T>() {
return out<T>(param_);
}
// Implicitly convert into a '*' for legacy usage.
inline operator T*() {
return std::addressof(param_);
}
private:
T& param_;
};
// Helper function that automatically infers 'T'
template <typename T>
inline out_convertible<T> outof(T& param) {
return out_convertible<T>(param);
}
// Helper function that automatically infers 'T'.
// To use when the argument is already inside an iterator.
template <typename It>
inline auto outof_iterator(It iter)
-> out_convertible<typename std::remove_reference<decltype(*iter)>::type> {
return outof(*iter);
}
// Helper function that automatically infers 'T'.
// To use when the argument is already a pointer.
template <typename T>
inline out_convertible<T> outof_ptr(T* ptr) {
DCHECK(ptr != nullptr);
return outof(*ptr);
}
// Helper function that automatically infers 'T'.
// Forwards an out parameter from one function into another.
template <typename T>
inline out_convertible<T> outof_forward(out<T>& out_param) {
T& param = *out_param;
return out_convertible<T>(param);
}
} // namespace art
#endif // ART_RUNTIME_BASE_OUT_H_