/* * 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 "util/TypeTraits.h" #include #include #include 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); Maybe(Maybe&& rhs); template Maybe(Maybe&& rhs); Maybe& operator=(const Maybe& rhs); template Maybe& operator=(const Maybe& rhs); Maybe& operator=(Maybe&& rhs); template Maybe& operator=(Maybe&& rhs); /** * Construct a Maybe holding a value. */ Maybe(const T& value); /** * Construct a Maybe holding a value. */ Maybe(T&& value); /** * 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; private: template friend class Maybe; template Maybe& copy(const Maybe& rhs); template Maybe& move(Maybe&& rhs); void destroy(); bool mNothing; typename std::aligned_storage::type mStorage; }; template Maybe::Maybe() : mNothing(true) { } template Maybe::~Maybe() { if (!mNothing) { destroy(); } } template Maybe::Maybe(const Maybe& rhs) : mNothing(rhs.mNothing) { if (!rhs.mNothing) { new (&mStorage) T(reinterpret_cast(rhs.mStorage)); } } template template Maybe::Maybe(const Maybe& rhs) : mNothing(rhs.mNothing) { if (!rhs.mNothing) { new (&mStorage) T(reinterpret_cast(rhs.mStorage)); } } template Maybe::Maybe(Maybe&& rhs) : mNothing(rhs.mNothing) { if (!rhs.mNothing) { rhs.mNothing = true; // Move the value from rhs. new (&mStorage) T(std::move(reinterpret_cast(rhs.mStorage))); rhs.destroy(); } } template template Maybe::Maybe(Maybe&& rhs) : mNothing(rhs.mNothing) { if (!rhs.mNothing) { rhs.mNothing = true; // Move the value from rhs. new (&mStorage) T(std::move(reinterpret_cast(rhs.mStorage))); 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 (mNothing && rhs.mNothing) { // Both are nothing, nothing to do. return *this; } else if (!mNothing && !rhs.mNothing) { // We both are something, so assign rhs to us. reinterpret_cast(mStorage) = reinterpret_cast(rhs.mStorage); } else if (mNothing) { // We are nothing but rhs is something. mNothing = rhs.mNothing; // Copy the value from rhs. new (&mStorage) T(reinterpret_cast(rhs.mStorage)); } else { // We are something but rhs is nothing, so destroy our value. mNothing = rhs.mNothing; destroy(); } return *this; } template inline Maybe& Maybe::operator=(Maybe&& rhs) { // 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 (mNothing && rhs.mNothing) { // Both are nothing, nothing to do. return *this; } else if (!mNothing && !rhs.mNothing) { // We both are something, so move assign rhs to us. rhs.mNothing = true; reinterpret_cast(mStorage) = std::move(reinterpret_cast(rhs.mStorage)); rhs.destroy(); } else if (mNothing) { // We are nothing but rhs is something. mNothing = false; rhs.mNothing = true; // Move the value from rhs. new (&mStorage) T(std::move(reinterpret_cast(rhs.mStorage))); rhs.destroy(); } else { // We are something but rhs is nothing, so destroy our value. mNothing = true; destroy(); } return *this; } template Maybe::Maybe(const T& value) : mNothing(false) { new (&mStorage) T(value); } template Maybe::Maybe(T&& value) : mNothing(false) { new (&mStorage) T(std::forward(value)); } template Maybe::operator bool() const { return !mNothing; } template T& Maybe::value() { assert(!mNothing && "Maybe::value() called on Nothing"); return reinterpret_cast(mStorage); } template const T& Maybe::value() const { assert(!mNothing && "Maybe::value() called on Nothing"); return reinterpret_cast(mStorage); } template void Maybe::destroy() { reinterpret_cast(mStorage).~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< has_eq_op::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; } /** * Same as operator== but negated. */ template typename std::enable_if< has_eq_op::value, bool >::type operator!=(const Maybe& a, const Maybe& b) { return !(a == b); } } // namespace aapt #endif // AAPT_MAYBE_H