diff --git a/include/boost/optional/detail/optional_reference_spec.hpp b/include/boost/optional/detail/optional_reference_spec.hpp index 2e08e30..4be140c 100644 --- a/include/boost/optional/detail/optional_reference_spec.hpp +++ b/include/boost/optional/detail/optional_reference_spec.hpp @@ -170,6 +170,15 @@ public: return none; } + template + optional::type>::type> flat_map(F f) const + { + if (this->has_value()) + return f(get()); + else + return none; + } + #ifndef BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES optional(T&& /* rhs */) BOOST_NOEXCEPT { detail::prevent_binding_rvalue(); } diff --git a/include/boost/optional/optional.hpp b/include/boost/optional/optional.hpp index 8ee259c..ff39dad 100644 --- a/include/boost/optional/optional.hpp +++ b/include/boost/optional/optional.hpp @@ -758,7 +758,16 @@ class optional_base : public optional_tag storage_type m_storage ; } ; +template +struct optional_value_type +{ +}; +template +struct optional_value_type< ::boost::optional > +{ + typedef T type; +}; #include @@ -1351,6 +1360,33 @@ class optional return none; } + template + optional::type>::type> flat_map(F f) & + { + if (this->has_value()) + return f(get()); + else + return none; + } + + template + optional::type>::type> flat_map(F f) const& + { + if (this->has_value()) + return f(get()); + else + return none; + } + + template + optional::type>::type> flat_map(F f) && + { + if (this->has_value()) + return f(boost::move(get())); + else + return none; + } + #else template value_type value_or_eval ( F f ) const @@ -1378,6 +1414,25 @@ class optional else return none; } + + template + optional::type>::type> flat_map(F f) + { + if (this->has_value()) + return f(get()); + else + return none; + } + + template + optional::type>::type> flat_map(F f) const + { + if (this->has_value()) + return f(get()); + else + return none; + } + #endif bool has_value() const BOOST_NOEXCEPT { return this->is_initialized() ; } diff --git a/test/Jamfile.v2 b/test/Jamfile.v2 index e1690d2..4a758da 100644 --- a/test/Jamfile.v2 +++ b/test/Jamfile.v2 @@ -23,7 +23,7 @@ import testing ; [ run optional_test_convert_from_T.cpp ] [ run optional_test_empty_braces.cpp ] [ run optional_test_make_optional.cpp ] - #[ run optional_test_flat_map.cpp ] + [ run optional_test_flat_map.cpp ] [ run optional_test_map.cpp ] [ run optional_test_tie.cpp ] [ run optional_test_ref_assign_portable_minimum.cpp ] diff --git a/test/optional_test_flat_map.cpp b/test/optional_test_flat_map.cpp new file mode 100644 index 0000000..409e111 --- /dev/null +++ b/test/optional_test_flat_map.cpp @@ -0,0 +1,275 @@ +// Copyright (C) 2018 Andrzej Krzemienski. +// +// Use, modification, and distribution is subject to the Boost Software +// License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at +// http://www.boost.org/LICENSE_1_0.txt) +// +// See http://www.boost.org/lib/optional for documentation. +// +// You are welcome to contact the author at: +// akrzemi1@gmail.com + +#include "boost/optional/optional.hpp" + +#ifdef __BORLANDC__ +#pragma hdrstop +#endif + +#include "boost/core/ignore_unused.hpp" +#include "boost/core/is_same.hpp" +#include "boost/core/lightweight_test.hpp" +#include "boost/core/lightweight_test_trait.hpp" + + +using boost::optional; +using boost::make_optional; +using boost::core::is_same; + +template +void verify_type(Deduced) +{ + BOOST_TEST_TRAIT_TRUE(( is_same )); +} + +struct Int +{ + int i; + explicit Int(int i_) : i(i_) {} +}; + +struct convert_t +{ + typedef optional result_type; + optional operator()(int i) { if (i != 0) return Int(i); else return boost::none; } +}; + +void test_flat_map_on_mutable_optional_with_function_object() +{ + { + optional oi (1); + verify_type< optional >(oi.flat_map(convert_t())); + optional oI = oi.flat_map(convert_t()); + BOOST_TEST(bool(oI)); + BOOST_TEST_EQ(1, oI->i); + } + { + optional oi (0); + optional oI = oi.flat_map(convert_t()); + BOOST_TEST(!oI); + } + { + optional oi; + optional oI = oi.flat_map(convert_t()); + BOOST_TEST(!oI); + } +} + +void test_flat_map_on_const_optional_with_function_object() +{ + { + const optional oi (1); + verify_type< optional >(oi.flat_map(convert_t())); + optional oI = oi.flat_map(convert_t()); + BOOST_TEST(bool(oI)); + BOOST_TEST_EQ(1, oI->i); + } + { + const optional oi (0); + optional oI = oi.flat_map(convert_t()); + BOOST_TEST(!oI); + } + { + const optional oi; + optional oI = oi.flat_map(convert_t()); + BOOST_TEST(!oI); + } +} + +void test_flat_map_with_lambda() +{ +#if !defined BOOST_NO_CXX11_LAMBDAS && !defined BOOST_NO_CXX11_DECLTYPE_N3276 + { + optional oi (1); + verify_type< optional >(oi.flat_map([](int i){ return optional(i == 0, Int(i)); })); + optional oI = oi.flat_map([](int i){ return optional(i != 0, Int(i)); }); + BOOST_TEST(bool(oI)); + BOOST_TEST_EQ(1, oI->i); + } + { + optional oi (0); + optional oI = oi.flat_map([](int i){ return optional(i != 0, Int(i)); }); + BOOST_TEST(!oI); + } + { + optional oi; + optional oI = oi.flat_map([](int i){ return optional(i != 0, Int(i)); }); + BOOST_TEST(!oI); + } +#endif // lambdas +} + +struct get_opt_ref +{ + typedef optional result_type; + optional operator()(int& i) { return i != 0 ? optional(i) : optional(); } +}; + +void test_flat_map_obj_to_ref() +{ + { + optional oi (2); + verify_type< optional >(oi.flat_map(get_opt_ref())); + optional ori = oi.flat_map(get_opt_ref()); + BOOST_TEST(bool(ori)); + BOOST_TEST_EQ(2, *ori); + *ori = 3; + BOOST_TEST(bool(oi)); + BOOST_TEST_EQ(3, *oi); + BOOST_TEST_EQ(3, *ori); + } + { + optional oi (0); + optional ori = oi.flat_map(get_opt_ref()); + BOOST_TEST(!ori); + } + { + optional oi; + optional ori = oi.flat_map(get_opt_ref()); + BOOST_TEST(!ori); + } +} + +optional get_opt_int_ref(Int& i) +{ + return i.i ? optional(i.i) : optional(); +} + +void test_flat_map_ref_to_ref() +{ + { + Int I (5); + optional orI (I); + verify_type< optional >(orI.flat_map(get_opt_int_ref)); + optional ori = orI.flat_map(get_opt_int_ref); + BOOST_TEST(bool(ori)); + BOOST_TEST_EQ(5, *ori); + *ori = 6; + BOOST_TEST_EQ(6, *ori); + BOOST_TEST_EQ(6, I.i); + } + { + Int I (0); + optional orI (I); + optional ori = orI.flat_map(get_opt_int_ref); + BOOST_TEST(!ori); + } + { + optional orI; + optional ori = orI.flat_map(get_opt_int_ref); + BOOST_TEST(!ori); + } +} + +optional< optional > make_opt_int(int i) +{ + if (i == 0) + return boost::none; + else if (i == 1) + return boost::make_optional(optional()); + else + return boost::make_optional(boost::make_optional(Int(i))); +} + +void test_flat_map_opt_opt() +{ + { + optional oi (9); + verify_type > >(oi.flat_map(make_opt_int)); + optional > ooI = oi.flat_map(make_opt_int); + BOOST_TEST(bool(ooI)); + BOOST_TEST(bool(*ooI)); + BOOST_TEST_EQ(9, (**ooI).i); + } + { + optional oi (1); + optional > ooI = oi.flat_map(make_opt_int); + BOOST_TEST(bool(ooI)); + BOOST_TEST(!*ooI); + } + { + optional oi (0); + optional > ooI = oi.flat_map(make_opt_int); + BOOST_TEST(!ooI); + } + { + optional oi; + optional > ooI = oi.flat_map(make_opt_int); + BOOST_TEST(!ooI); + } +} + +#if (!defined BOOST_NO_CXX11_REF_QUALIFIERS) && (!defined BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES) +struct MoveOnly +{ + int value; + explicit MoveOnly(int i) : value(i) {} + MoveOnly(MoveOnly && r) : value(r.value) { r.value = 0; } + MoveOnly& operator=(MoveOnly && r) { value = r.value; r.value = 0; return *this; } + +private: + MoveOnly(MoveOnly const&); + void operator=(MoveOnly const&); +}; + +MoveOnly makeMoveOnly(int i) +{ + return MoveOnly(i); +} + +optional makeOptMoveOnly(int i) +{ + return optional(MoveOnly(i)); +} + +optional get_val(MoveOnly m) +{ + return optional(m.value != 0, m.value); +} + +void test_flat_map_move_only() +{ + { + optional om (makeMoveOnly(1)), om2 (makeMoveOnly(2)); + verify_type >(boost::move(om).flat_map(get_val)); + optional oi = boost::move(om2).flat_map(get_val); + BOOST_TEST(bool(oi)); + BOOST_TEST_EQ(2, *oi); + } + { + optional oj = makeOptMoveOnly(4).flat_map(get_val); + BOOST_TEST(bool(oj)); + BOOST_TEST_EQ(4, *oj); + } + { + optional oj = optional().flat_map(get_val); + BOOST_TEST(!oj); + } +} + +#endif // no rvalue refs + +int main() +{ + test_flat_map_on_mutable_optional_with_function_object(); + test_flat_map_on_const_optional_with_function_object(); + test_flat_map_with_lambda(); + test_flat_map_obj_to_ref(); + test_flat_map_ref_to_ref(); + test_flat_map_opt_opt(); + +#if (!defined BOOST_NO_CXX11_REF_QUALIFIERS) && (!defined BOOST_OPTIONAL_DETAIL_NO_RVALUE_REFERENCES) + test_flat_map_move_only(); +#endif + + return boost::report_errors(); +}