File: //proc/self/root/usr/include/hphp/util/either.h
/*
+----------------------------------------------------------------------+
| HipHop for PHP |
+----------------------------------------------------------------------+
| Copyright (c) 2010-present Facebook, Inc. (http://www.facebook.com) |
+----------------------------------------------------------------------+
| This source file is subject to version 3.01 of the PHP license, |
| that is bundled with this package in the file LICENSE, and is |
| available through the world-wide-web at the following url: |
| http://www.php.net/license/3_01.txt |
| If you did not receive a copy of the PHP license and are unable to |
| obtain it through the world-wide-web, please send a note to |
| license@php.net so we can mail you a copy immediately. |
+----------------------------------------------------------------------+
*/
#ifndef incl_HPHP_EITHER_H_
#define incl_HPHP_EITHER_H_
#include <cassert>
#include <cstdint>
#include <cstddef>
#include <type_traits>
// Workaround a bug that using std::common_type<void, void> causes error with
// libc++. See https://llvm.org/bugs/show_bug.cgi?id=22135
#ifdef _LIBCPP_VERSION
namespace std {
template<>
struct common_type<void, void> {
typedef void type;
};
}
#endif
namespace HPHP {
//////////////////////////////////////////////////////////////////////
/*
* Discriminated pointer to one of two types, or a nullptr.
*
* It does not distinguish between two types of null (for left and
* right)---if the Either is nullptr it is effectively a third state.
*
* This class does not do any intelligent automatic ownership, and is
* guaranteed to be a trivially copyable, standard layout class.
* Zero-initializing it is guaranteed to have a nullptr value
* (assuming null is represented with all bits zero, which of course
* it is on our supported architectures).
*
* Requirements:
*
* - The two types must be unrelated, and must be pointers.
*
* - The pointer types used with this class must be known to be at least
* 2-byte aligned with the default arguments, since it discriminates using
* the low bit (NB: this is not the case for string literals on gcc). You
* can pass either_policy::high_bit to use the highest bit as the tag
* instead in cases where this doesn't apply.
*
* - This class assumes pointers of L and R types never alias.
*
*/
namespace either_policy { struct high_bit {}; }
template<class L, class R, class TagBitPolicy = void>
struct Either {
using Opaque = uintptr_t;
static_assert(
std::is_same<TagBitPolicy,void>::value ||
std::is_same<TagBitPolicy,either_policy::high_bit>::value,
"Unknown policy in Either"
);
static constexpr Opaque TagBit =
std::conditional<
std::is_same<TagBitPolicy,void>::value,
std::integral_constant<Opaque,0x1>,
std::integral_constant<Opaque,0x8000000000000000>
>::type::value;
/*
* The default constructor creates an Either that isNull.
*
* Post: left() == nullptr && right() == nullptr
* isNull()
*/
Either() noexcept : bits{0} {}
/*
* Create an Either that isNull.
*
* Post: left() == nullptr && right() == nullptr
* isNull()
*/
/* implicit */ Either(std::nullptr_t) : bits{0} {}
/*
* Create an Either in the left mode.
*
* Post: left() == l && right() == nullptr
*/
/* implicit */ Either(L l)
: bits{reinterpret_cast<Opaque>(l)}
{
assert(!(reinterpret_cast<Opaque>(l) & TagBit));
}
/*
* Create an Either in the right mode.
*
* Post: left() == nullptr && right() == r
*/
/* implicit */ Either(R r)
: bits{reinterpret_cast<Opaque>(r) | TagBit}
{
assert(!(reinterpret_cast<Opaque>(r) & TagBit));
}
/*
* Raw access to allow using e.g., std::atomic<Opaque>. Values must come
* from identical instantiations of the Either template, and nothing can
* be assumed about the meaning of the bits.
*/
static Either fromOpaque(Opaque raw) {
Either result;
result.bits = raw;
return result;
}
Opaque toOpaque() const { return bits; }
/*
* Equality comparison is shallow. If the pointers are equal, the
* Eithers are equal.
*/
bool operator==(Either<L,R> o) const {
// We assume L* and R* don't alias, so we don't need to check type tags
// when comparing. But we have to put the TagBit in in case we constructed
// a null from the right (we'll have a tagged null).
return (bits | TagBit) == (o.bits | TagBit);
}
bool operator!=(Either<L,R> o) const {
return !(*this == o);
}
/*
* Matching on Eithers takes two functions, for left and right. If
* you know that the either is non-null, this is reasonable. If it's
* potentially null, the null case is passed as a null pointer to the
* right branch.
*
* Example usage:
*
* Either<A*,B*> foo;
* auto const count = foo.match(
* [&] (A* a) { return a->size(); },
* [&] (B* b) { return b->size(); }
* );
*/
template<class LF, class RF>
typename std::common_type<
typename std::result_of<LF(L)>::type,
typename std::result_of<RF(R)>::type
>::type match(const LF& lf, const RF& rf) const {
if (auto const l = left()) return lf(l);
return rf(reinterpret_cast<R>(bits & ~TagBit));
}
/*
* Functions that simultaneously query the type tag and extract the
* pointer of that type. Both return nullptr if the Either does not
* hold that type.
*/
L left() const {
return bits & TagBit ? nullptr : reinterpret_cast<L>(bits);
}
R right() const {
return bits & TagBit ? reinterpret_cast<R>(bits & ~TagBit) : nullptr;
}
/*
* Returns whether this Either contains neither left nor right.
*/
bool isNull() const { return !(bits & ~TagBit); }
private:
static_assert(
!std::is_convertible<L,R>::value && !std::is_convertible<R,L>::value,
"Either<L,R> should not be used with compatible pointer types"
);
// Not implemented in gcc:
// static_assert(
// std::is_standard_layout<Either>::value,
// "Either<L,R> should be a standard layout class"
// );
static_assert(
std::is_pointer<L>::value && std::is_pointer<R>::value,
"Either<L,R> should only be used with pointer types"
);
private:
Opaque bits;
};
//////////////////////////////////////////////////////////////////////
}
#endif