/* * 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 AAPT_MAYBE_H #define AAPT_MAYBE_H #include #include #include "android-base/logging.h" #include "util/TypeTraits.h" namespace aapt { /** * Either holds a valid value of type T, or holds Nothing. * The value is stored inline in this structure, so no * heap memory is used when creating a Maybe object. */ template class Maybe { public: /** * Construct Nothing. */ Maybe(); ~Maybe(); Maybe(const Maybe& rhs); template Maybe(const Maybe& rhs); // NOLINT(google-explicit-constructor) Maybe(Maybe&& rhs) noexcept; template Maybe(Maybe&& rhs); // NOLINT(google-explicit-constructor) Maybe& operator=(const Maybe& rhs); template Maybe& operator=(const Maybe& rhs); Maybe& operator=(Maybe&& rhs) noexcept; template Maybe& operator=(Maybe&& rhs); /** * Construct a Maybe holding a value. */ Maybe(const T& value); // NOLINT(google-explicit-constructor) /** * Construct a Maybe holding a value. */ Maybe(T&& value); // NOLINT(google-explicit-constructor) /** * True if this holds a value, false if * it holds Nothing. */ explicit operator bool() const; /** * Gets the value if one exists, or else * panics. */ T& value(); /** * Gets the value if one exists, or else * panics. */ const T& value() const; T value_or_default(const T& def) const; private: template friend class Maybe; template Maybe& copy(const Maybe& rhs); template Maybe& move(Maybe&& rhs); void destroy(); bool nothing_; typename std::aligned_storage::type storage_; }; template Maybe::Maybe() : nothing_(true) {} template Maybe::~Maybe() { if (!nothing_) { destroy(); } } template Maybe::Maybe(const Maybe& rhs) : nothing_(rhs.nothing_) { if (!rhs.nothing_) { new (&storage_) T(reinterpret_cast(rhs.storage_)); } } template template Maybe::Maybe(const Maybe& rhs) : nothing_(rhs.nothing_) { if (!rhs.nothing_) { new (&storage_) T(reinterpret_cast(rhs.storage_)); } } template Maybe::Maybe(Maybe&& rhs) noexcept : nothing_(rhs.nothing_) { if (!rhs.nothing_) { rhs.nothing_ = true; // Move the value from rhs. new (&storage_) T(std::move(reinterpret_cast(rhs.storage_))); rhs.destroy(); } } template template Maybe::Maybe(Maybe&& rhs) : nothing_(rhs.nothing_) { if (!rhs.nothing_) { rhs.nothing_ = true; // Move the value from rhs. new (&storage_) T(std::move(reinterpret_cast(rhs.storage_))); rhs.destroy(); } } template inline Maybe& Maybe::operator=(const Maybe& rhs) { // Delegate to the actual assignment. return copy(rhs); } template template inline Maybe& Maybe::operator=(const Maybe& rhs) { return copy(rhs); } template template Maybe& Maybe::copy(const Maybe& rhs) { if (nothing_ && rhs.nothing_) { // Both are nothing, nothing to do. return *this; } else if (!nothing_ && !rhs.nothing_) { // We both are something, so assign rhs to us. reinterpret_cast(storage_) = reinterpret_cast(rhs.storage_); } else if (nothing_) { // We are nothing but rhs is something. nothing_ = rhs.nothing_; // Copy the value from rhs. new (&storage_) T(reinterpret_cast(rhs.storage_)); } else { // We are something but rhs is nothing, so destroy our value. nothing_ = rhs.nothing_; destroy(); } return *this; } template inline Maybe& Maybe::operator=(Maybe&& rhs) noexcept { // Delegate to the actual assignment. return move(std::forward>(rhs)); } template template inline Maybe& Maybe::operator=(Maybe&& rhs) { return move(std::forward>(rhs)); } template template Maybe& Maybe::move(Maybe&& rhs) { if (nothing_ && rhs.nothing_) { // Both are nothing, nothing to do. return *this; } else if (!nothing_ && !rhs.nothing_) { // We both are something, so move assign rhs to us. rhs.nothing_ = true; reinterpret_cast(storage_) = std::move(reinterpret_cast(rhs.storage_)); rhs.destroy(); } else if (nothing_) { // We are nothing but rhs is something. nothing_ = false; rhs.nothing_ = true; // Move the value from rhs. new (&storage_) T(std::move(reinterpret_cast(rhs.storage_))); rhs.destroy(); } else { // We are something but rhs is nothing, so destroy our value. nothing_ = true; destroy(); } return *this; } template Maybe::Maybe(const T& value) : nothing_(false) { new (&storage_) T(value); } template Maybe::Maybe(T&& value) : nothing_(false) { new (&storage_) T(std::forward(value)); } template Maybe::operator bool() const { return !nothing_; } template T& Maybe::value() { CHECK(!nothing_) << "Maybe::value() called on Nothing"; return reinterpret_cast(storage_); } template const T& Maybe::value() const { CHECK(!nothing_) << "Maybe::value() called on Nothing"; return reinterpret_cast(storage_); } template T Maybe::value_or_default(const T& def) const { if (nothing_) { return def; } return reinterpret_cast(storage_); } template void Maybe::destroy() { reinterpret_cast(storage_).~T(); } template inline Maybe::type> make_value(T&& value) { return Maybe::type>(std::forward(value)); } template inline Maybe make_nothing() { return Maybe(); } // Define the == operator between Maybe and Maybe only if the operator T == U is defined. // That way the compiler will show an error at the callsite when comparing two Maybe<> objects // whose inner types can't be compared. template typename std::enable_if::value, bool>::type operator==(const Maybe& a, const Maybe& b) { if (a && b) { return a.value() == b.value(); } else if (!a && !b) { return true; } return false; } template typename std::enable_if::value, bool>::type operator==(const Maybe& a, const U& b) { return a ? a.value() == b : false; } // Same as operator== but negated. template typename std::enable_if::value, bool>::type operator!=(const Maybe& a, const Maybe& b) { return !(a == b); } template typename std::enable_if::value, bool>::type operator<(const Maybe& a, const Maybe& b) { if (a && b) { return a.value() < b.value(); } else if (!a && !b) { return false; } return !a; } } // namespace aapt #endif // AAPT_MAYBE_H