/* * 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 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. */ 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(); } } // namespace aapt #endif // AAPT_MAYBE_H