/*! @file Forward declares `boost::hana::optional`. @copyright Louis Dionne 2013-2016 Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE.md or copy at http://boost.org/LICENSE_1_0.txt) */ #ifndef BOOST_HANA_FWD_OPTIONAL_HPP #define BOOST_HANA_FWD_OPTIONAL_HPP #include #include #include BOOST_HANA_NAMESPACE_BEGIN //! @ingroup group-datatypes //! Optional value whose optional-ness is known at compile-time. //! //! An `optional` either contains a value (represented as `just(x)`), or //! it is empty (represented as `nothing`). In essence, `hana::optional` //! is pretty much like a `boost::optional` or the upcoming `std::optional`, //! except for the fact that whether a `hana::optional` is empty or not is //! known at compile-time. This can be particularly useful for returning //! from a function that might fail, but whose reason for failing is not //! important. Of course, whether the function will fail has to be known //! at compile-time. //! //! This is really an important difference between `hana::optional` and //! `std::optional`. Unlike `std::optional{}` and `std::optional{x}` //! who share the same type (`std::optional`), `hana::just(x)` and //! `hana::nothing` do not share the same type, since the state of the //! optional has to be known at compile-time. Hence, whether a `hana::just` //! or a `hana::nothing` will be returned from a function has to be known //! at compile-time for the return type of that function to be computable //! by the compiler. This makes `hana::optional` well suited for static //! metaprogramming tasks, but very poor for anything dynamic. //! //! //! Interoperation with `type`s //! --------------------------- //! When a `just` contains an object of type `T` which is a `type`, //! it has a nested `::%type` alias equivalent to `T::%type`. `nothing`, //! however, never has a nested `::%type` alias. If `t` is a `type`, //! this allows `decltype(just(t))` to be seen as a nullary metafunction //! equivalent to `decltype(t)`. Along with the `sfinae` function, //! this allows `hana::optional` to interact seamlessly with //! SFINAE-friendly metafunctions. //! Example: //! @include example/optional/sfinae_friendly_metafunctions.cpp //! //! //! Modeled concepts //! ---------------- //! 1. `Comparable`\n //! Two `optional`s are equal if and only if they are both empty or they //! both contain a value and those values are equal. //! @include example/optional/comparable.cpp //! //! 2. `Orderable`\n //! Optional values can be ordered by considering the value they are //! holding, if any. To handle the case of an empty optional value, we //! arbitrarily set `nothing` as being less than any other `just`. Hence, //! @code //! just(x) < just(y) if and only if x < y //! nothing < just(anything) //! @endcode //! Example: //! @include example/optional/orderable.cpp //! //! 3. `Functor`\n //! An optional value can be seen as a list containing either one element //! (`just(x)`) or no elements at all (`nothing`). As such, mapping //! a function over an optional value is equivalent to applying it to //! its value if there is one, and to `nothing` otherwise: //! @code //! transform(just(x), f) == just(f(x)) //! transform(nothing, f) == nothing //! @endcode //! Example: //! @include example/optional/functor.cpp //! //! 4. `Applicative`\n //! First, a value can be made optional with `lift`, which //! is equivalent to `just`. Second, one can feed an optional value to an //! optional function with `ap`, which will return `just(f(x))` if there //! is both a function _and_ a value, and `nothing` otherwise: //! @code //! ap(just(f), just(x)) == just(f(x)) //! ap(nothing, just(x)) == nothing //! ap(just(f), nothing) == nothing //! ap(nothing, nothing) == nothing //! @endcode //! A simple example: //! @include example/optional/applicative.cpp //! A more complex example: //! @include example/optional/applicative.complex.cpp //! //! 5. `Monad`\n //! The `Monad` model makes it easy to compose actions that might fail. //! One can feed an optional value if there is one into a function with //! `chain`, which will return `nothing` if there is no value. Finally, //! optional-optional values can have their redundant level of optionality //! removed with `flatten`. Also note that the `|` operator can be used in //! place of the `chain` function. //! Example: //! @include example/optional/monad.cpp //! //! 6. `MonadPlus`\n //! The `MonadPlus` model allows choosing the first valid value out of //! two optional values with `concat`. If both optional values are //! `nothing`s, `concat` will return `nothing`. //! Example: //! @include example/optional/monad_plus.cpp //! //! 7. `Foldable`\n //! Folding an optional value is equivalent to folding a list containing //! either no elements (for `nothing`) or `x` (for `just(x)`). //! Example: //! @include example/optional/foldable.cpp //! //! 8. `Searchable`\n //! Searching an optional value is equivalent to searching a list //! containing `x` for `just(x)` and an empty list for `nothing`. //! Example: //! @include example/optional/searchable.cpp #ifdef BOOST_HANA_DOXYGEN_INVOKED template struct optional { // 5.3.1, Constructors //! Default-construct an `optional`. Only exists if the optional //! contains a value, and if that value is DefaultConstructible. constexpr optional() = default; //! Copy-construct an `optional`. //! An empty optional may only be copy-constructed from another //! empty `optional`, and an `optional` with a value may only be //! copy-constructed from another `optional` with a value. //! Furthermore, this constructor only exists if the value //! held in the `optional` is CopyConstructible. optional(optional const&) = default; //! Move-construct an `optional`. //! An empty optional may only be move-constructed from another //! empty `optional`, and an `optional` with a value may only be //! move-constructed from another `optional` with a value. //! Furthermore, this constructor only exists if the value //! held in the `optional` is MoveConstructible. optional(optional&&) = default; //! Construct an `optional` holding a value of type `T` from another //! object of type `T`. The value is copy-constructed. constexpr optional(T const& t) : value_(t) { } //! Construct an `optional` holding a value of type `T` from another //! object of type `T`. The value is move-constructed. constexpr optional(T&& t) : value_(static_cast(t)) { } // 5.3.3, Assignment //! Copy-assign an `optional`. //! An empty optional may only be copy-assigned from another empty //! `optional`, and an `optional` with a value may only be copy-assigned //! from another `optional` with a value. Furthermore, this assignment //! operator only exists if the value held in the `optional` is //! CopyAssignable. constexpr optional& operator=(optional const&) = default; //! Move-assign an `optional`. //! An empty optional may only be move-assigned from another empty //! `optional`, and an `optional` with a value may only be move-assigned //! from another `optional` with a value. Furthermore, this assignment //! operator only exists if the value held in the `optional` is //! MoveAssignable. constexpr optional& operator=(optional&&) = default; // 5.3.5, Observers //! Returns a pointer to the contained value, or a `nullptr` if the //! `optional` is empty. //! //! //! @note Overloads of this method are provided for both the `const` //! and the non-`const` cases. //! //! //! Example //! ------- //! @include example/optional/value.cpp constexpr T* operator->(); //! Extract the content of an `optional`, or fail at compile-time. //! //! If `*this` contains a value, that value is returned. Otherwise, //! a static assertion is triggered. //! //! @note //! Overloads of this method are provided for the cases where `*this` //! is a reference, a rvalue-reference and their `const` counterparts. //! //! //! Example //! ------- //! @include example/optional/value.cpp constexpr T& value(); //! Equivalent to `value()`, provided for convenience. //! //! @note //! Overloads of this method are provided for the cases where `*this` //! is a reference, a rvalue-reference and their `const` counterparts. //! //! //! Example //! ------- //! @include example/optional/value.cpp constexpr T& operator*(); //! Return the contents of an `optional`, with a fallback result. //! //! If `*this` contains a value, that value is returned. Otherwise, //! the default value provided is returned. //! //! @note //! Overloads of this method are provided for the cases where `*this` //! is a reference, a rvalue-reference and their `const` counterparts. //! //! //! @param default_ //! The default value to return if `*this` does not contain a value. //! //! //! Example //! ------- //! @include example/optional/value_or.cpp template constexpr decltype(auto) value_or(U&& default_); //! Equivalent to `hana::chain`. template friend constexpr auto operator|(optional, F); //! Equivalent to `hana::equal` template friend constexpr auto operator==(X&& x, Y&& y); //! Equivalent to `hana::not_equal` template friend constexpr auto operator!=(X&& x, Y&& y); //! Equivalent to `hana::less` template friend constexpr auto operator<(X&& x, Y&& y); //! Equivalent to `hana::greater` template friend constexpr auto operator>(X&& x, Y&& y); //! Equivalent to `hana::less_equal` template friend constexpr auto operator<=(X&& x, Y&& y); //! Equivalent to `hana::greater_equal` template friend constexpr auto operator>=(X&& x, Y&& y); }; #else template struct optional; #endif //! Tag representing a `hana::optional`. //! @relates hana::optional struct optional_tag { }; //! Create an optional value. //! @relates hana::optional //! //! Specifically, `make()` is equivalent to `nothing`, and //! `make(x)` is equivalent to `just(x)`. This is provided //! for consistency with the other `make<...>` functions. //! //! //! Example //! ------- //! @include example/optional/make.cpp #ifdef BOOST_HANA_DOXYGEN_INVOKED template <> constexpr auto make = []([auto&& x]) { return optional::type>{forwarded(x)}; }; #endif //! Alias to `make`; provided for convenience. //! @relates hana::optional //! //! //! Example //! ------- //! @include example/optional/make.cpp constexpr auto make_optional = make; //! Create an optional value containing `x`. //! @relates hana::optional //! //! //! Example //! ------- //! @include example/optional/just.cpp #ifdef BOOST_HANA_DOXYGEN_INVOKED constexpr auto just = [](auto&& x) { return optional::type>{forwarded(x)}; }; #else struct make_just_t { template constexpr auto operator()(T&&) const; }; constexpr make_just_t just{}; #endif //! An empty optional value. //! @relates hana::optional //! //! //! Example //! ------- //! @include example/optional/nothing.cpp #ifdef BOOST_HANA_DOXYGEN_INVOKED constexpr optional<> nothing{}; #else template <> struct optional<> : detail::operators::adl> { // 5.3.1, Constructors constexpr optional() = default; constexpr optional(optional const&) = default; constexpr optional(optional&&) = default; // 5.3.3, Assignment constexpr optional& operator=(optional const&) = default; constexpr optional& operator=(optional&&) = default; // 5.3.5, Observers constexpr decltype(nullptr) operator->() const { return nullptr; } template constexpr auto value() const; template constexpr auto operator*() const; template constexpr U&& value_or(U&& u) const; }; constexpr optional<> nothing{}; #endif //! Apply a function to the contents of an optional, with a fallback //! result. //! @relates hana::optional //! //! Specifically, `maybe` takes a default value, a function and an //! optional value. If the optional value is `nothing`, the default //! value is returned. Otherwise, the function is applied to the //! content of the `just`. //! //! //! @param default_ //! A default value returned if `m` is `nothing`. //! //! @param f //! A function called as `f(x)` if and only if `m` is an optional value //! of the form `just(x)`. In that case, the result returend by `maybe` //! is the result of `f`. //! //! @param m //! An optional value. //! //! //! Example //! ------- //! @include example/optional/maybe.cpp #ifdef BOOST_HANA_DOXYGEN_INVOKED constexpr auto maybe = [](auto&& default_, auto&& f, auto&& m) -> decltype(auto) { if (m is a just(x)) { return forwarded(f)(forwarded(x)); else return forwarded(default_); } }; #else struct maybe_t { template constexpr decltype(auto) operator()(Def&&, F&& f, optional const& m) const { return static_cast(f)(m.value_); } template constexpr decltype(auto) operator()(Def&&, F&& f, optional& m) const { return static_cast(f)(m.value_); } template constexpr decltype(auto) operator()(Def&&, F&& f, optional&& m) const { return static_cast(f)(static_cast&&>(m).value_); } template constexpr Def operator()(Def&& def, F&&, optional<> const&) const { return static_cast(def); } }; constexpr maybe_t maybe{}; #endif //! Calls a function if the call expression is well-formed. //! @relates hana::optional //! //! Given a function `f`, `sfinae` returns a new function applying `f` //! to its arguments and returning `just` the result if the call is //! well-formed, and `nothing` otherwise. In other words, `sfinae(f)(x...)` //! is `just(f(x...))` if that expression is well-formed, and `nothing` //! otherwise. Note, however, that it is possible for an expression //! `f(x...)` to be well-formed as far as SFINAE is concerned, but //! trying to actually compile `f(x...)` still fails. In this case, //! `sfinae` won't be able to detect it and a hard failure is likely //! to happen. //! //! //! @note //! The function given to `sfinae` must not return `void`, since //! `just(void)` does not make sense. A compilation error is //! triggered if the function returns void. //! //! //! Example //! ------- //! @include example/optional/sfinae.cpp #ifdef BOOST_HANA_DOXYGEN_INVOKED auto sfinae = [](auto&& f) { return [perfect-capture](auto&& ...x) { if (decltype(forwarded(f)(forwarded(x)...)) is well-formed) return just(forwarded(f)(forwarded(x)...)); else return nothing; }; }; #else struct sfinae_t { template constexpr decltype(auto) operator()(F&& f) const; }; constexpr sfinae_t sfinae{}; #endif //! Return whether an `optional` contains a value. //! @relates hana::optional //! //! Specifically, returns a compile-time true-valued `Logical` if `m` is //! of the form `just(x)` for some `x`, and a false-valued one otherwise. //! //! //! Example //! ------- //! @include example/optional/is_just.cpp #ifdef BOOST_HANA_DOXYGEN_INVOKED constexpr auto is_just = [](auto const& m) { return m is a just(x); }; #else struct is_just_t { template constexpr auto operator()(optional const&) const; }; constexpr is_just_t is_just{}; #endif //! Return whether an `optional` is empty. //! @relates hana::optional //! //! Specifically, returns a compile-time true-valued `Logical` if `m` is //! a `nothing`, and a false-valued one otherwise. //! //! //! Example //! ------- //! @include example/optional/is_nothing.cpp #ifdef BOOST_HANA_DOXYGEN_INVOKED constexpr auto is_nothing = [](auto const& m) { return m is a nothing; }; #else struct is_nothing_t { template constexpr auto operator()(optional const&) const; }; constexpr is_nothing_t is_nothing{}; #endif BOOST_HANA_NAMESPACE_END #endif // !BOOST_HANA_FWD_OPTIONAL_HPP