blob: 4cb53bf57f829e44da9fbd49d6fd9c8c98c944d4 [file] [log] [blame]
/*
* Copyright (C) 2014 Apple Inc. All rights reserved.
*
* Redistribution and use in source and binary forms, with or without
* modification, are permitted provided that the following conditions
* are met:
* 1. Redistributions of source code must retain the above copyright
* notice, this list of conditions and the following disclaimer.
* 2. Redistributions in binary form must reproduce the above copyright
* notice, this list of conditions and the following disclaimer in the
* documentation and/or other materials provided with the distribution.
*
* THIS SOFTWARE IS PROVIDED BY APPLE INC. AND ITS CONTRIBUTORS ``AS IS''
* AND ANY EXPRESS OR IMPLIED WARRANTIES, INCLUDING, BUT NOT LIMITED TO,
* THE IMPLIED WARRANTIES OF MERCHANTABILITY AND FITNESS FOR A PARTICULAR
* PURPOSE ARE DISCLAIMED. IN NO EVENT SHALL APPLE INC. OR ITS CONTRIBUTORS
* BE LIABLE FOR ANY DIRECT, INDIRECT, INCIDENTAL, SPECIAL, EXEMPLARY, OR
* CONSEQUENTIAL DAMAGES (INCLUDING, BUT NOT LIMITED TO, PROCUREMENT OF
* SUBSTITUTE GOODS OR SERVICES; LOSS OF USE, DATA, OR PROFITS; OR BUSINESS
* INTERRUPTION) HOWEVER CAUSED AND ON ANY THEORY OF LIABILITY, WHETHER IN
* CONTRACT, STRICT LIABILITY, OR TORT (INCLUDING NEGLIGENCE OR OTHERWISE)
* ARISING IN ANY WAY OUT OF THE USE OF THIS SOFTWARE, EVEN IF ADVISED OF
* THE POSSIBILITY OF SUCH DAMAGE.
*/
#ifndef Optional_h
#define Optional_h
#include <type_traits>
#include <wtf/Assertions.h>
#include <wtf/StdLibExtras.h>
// WTF::Optional is a class based on std::optional, described here:
// http://www.open-std.org/JTC1/SC22/WG21/docs/papers/2013/n3527.html
// If this ends up in a C++ standard, we should replace our implementation with it.
namespace WTF {
struct InPlaceTag { };
constexpr InPlaceTag InPlace { };
struct NulloptTag { explicit constexpr NulloptTag(int) { } };
constexpr NulloptTag Nullopt { 0 };
template<typename T>
class Optional {
public:
Optional()
: m_isEngaged(false)
{
}
Optional(NulloptTag)
: m_isEngaged(false)
{
}
Optional(const T& value)
: m_isEngaged(true)
{
new (NotNull, &m_value) T(value);
}
Optional(const Optional& other)
: m_isEngaged(other.m_isEngaged)
{
if (m_isEngaged)
new (NotNull, &m_value) T(*other.asPtr());
}
Optional(Optional&& other)
: m_isEngaged(other.m_isEngaged)
{
if (m_isEngaged)
new (NotNull, &m_value) T(WTFMove(*other.asPtr()));
}
Optional(T&& value)
: m_isEngaged(true)
{
new (NotNull, &m_value) T(WTFMove(value));
}
template<typename... Args>
Optional(InPlaceTag, Args&&... args)
: m_isEngaged(true)
{
new (NotNull, &m_value) T(std::forward<Args>(args)...);
}
~Optional()
{
destroy();
}
Optional& operator=(NulloptTag)
{
destroy();
return *this;
}
Optional& operator=(const Optional& other)
{
if (this == &other)
return *this;
destroy();
if (other.m_isEngaged) {
new (NotNull, &m_value) T(*other.asPtr());
m_isEngaged = true;
}
return *this;
}
Optional& operator=(Optional&& other)
{
if (this == &other)
return *this;
destroy();
if (other.m_isEngaged) {
new (NotNull, &m_value) T(WTFMove(*other.asPtr()));
m_isEngaged = true;
}
return *this;
}
template<typename U, class = typename std::enable_if<std::is_same<typename std::remove_reference<U>::type, T>::value>::type>
Optional& operator=(U&& u)
{
destroy();
new (NotNull, &m_value) T(std::forward<U>(u));
m_isEngaged = true;
return *this;
}
explicit operator bool() const { return m_isEngaged; }
const T* operator->() const
{
ASSERT(m_isEngaged);
return asPtr();
}
T* operator->()
{
ASSERT(m_isEngaged);
return asPtr();
}
const T& operator*() const { return value(); }
T& operator*() { return value(); }
T& value()
{
ASSERT(m_isEngaged);
return *asPtr();
}
const T& value() const
{
ASSERT(m_isEngaged);
return *asPtr();
}
template<typename U>
T valueOr(U&& value) const
{
if (m_isEngaged)
return *asPtr();
return std::forward<U>(value);
}
template<typename U>
T valueOrCompute(U callback) const
{
if (m_isEngaged)
return *asPtr();
return callback();
}
private:
const T* asPtr() const { return reinterpret_cast<const T*>(&m_value); }
T* asPtr() { return reinterpret_cast<T*>(&m_value); }
void destroy()
{
if (m_isEngaged) {
asPtr()->~T();
m_isEngaged = false;
}
}
bool m_isEngaged;
typename std::aligned_storage<sizeof(T), std::alignment_of<T>::value>::type m_value;
};
template<typename T>
constexpr bool operator==(const Optional<T>& lhs, const Optional<T>& rhs)
{
return static_cast<bool>(lhs) == static_cast<bool>(rhs) && (!static_cast<bool>(lhs) || lhs.value() == rhs.value());
}
template<typename T>
constexpr bool operator!=(const Optional<T>& lhs, const Optional<T>& rhs)
{
return !(lhs == rhs);
}
template<typename T>
constexpr bool operator==(const Optional<T>& opt, NulloptTag)
{
return !opt;
}
template<typename T>
constexpr bool operator!=(const Optional<T>& opt, NulloptTag)
{
return static_cast<bool>(opt);
}
template<typename T>
constexpr bool operator==(NulloptTag, const Optional<T>& opt)
{
return !opt;
}
template<typename T>
constexpr bool operator!=(NulloptTag, const Optional<T>& opt)
{
return static_cast<bool>(opt);
}
template<typename T>
constexpr bool operator==(const Optional<T>& opt, const T& value)
{
return opt && opt.value() == value;
}
template<typename T>
constexpr bool operator!=(const Optional<T>& opt, const T& value)
{
return !(opt == value);
}
template<typename T>
constexpr bool operator==(const T& value, const Optional<T>& opt)
{
return opt && opt.value() == value;
}
template<typename T>
constexpr bool operator!=(const T& value, const Optional<T>& opt)
{
return !(value == opt);
}
template<typename T>
Optional<typename std::decay<T>::type>
makeOptional(T&& value)
{
return Optional<typename std::decay<T>::type>(std::forward<T>(value));
}
} // namespace WTF
using WTF::InPlace;
using WTF::Nullopt;
using WTF::Optional;
using WTF::makeOptional;
#endif // Optional_h