util: backport C++17 optional
This commit also makes code style of parameter packs consistent.
refs #3753
Change-Id: I1e301d25b73501b0b28678e6095bbb5619a0e760
diff --git a/src/util/backports-optional.hpp b/src/util/backports-optional.hpp
new file mode 100644
index 0000000..c93e855
--- /dev/null
+++ b/src/util/backports-optional.hpp
@@ -0,0 +1,309 @@
+/* -*- Mode:C++; c-file-style:"gnu"; indent-tabs-mode:nil; -*- */
+/**
+ * Copyright (c) 2013-2016 Regents of the University of California.
+ *
+ * This file is part of ndn-cxx library (NDN C++ library with eXperimental eXtensions).
+ *
+ * ndn-cxx library is free software: you can redistribute it and/or modify it under the
+ * terms of the GNU Lesser General Public License as published by the Free Software
+ * Foundation, either version 3 of the License, or (at your option) any later version.
+ *
+ * ndn-cxx library is distributed in the hope that it will be useful, but WITHOUT ANY
+ * WARRANTY; without even the implied warranty of MERCHANTABILITY or FITNESS FOR A
+ * PARTICULAR PURPOSE. See the GNU Lesser General Public License for more details.
+ *
+ * You should have received copies of the GNU General Public License and GNU Lesser
+ * General Public License along with ndn-cxx, e.g., in COPYING.md file. If not, see
+ * <http://www.gnu.org/licenses/>.
+ *
+ * See AUTHORS.md for complete list of ndn-cxx authors and contributors.
+ */
+
+/** \file
+ * \brief C++17 std::optional backport implemented using boost::optional
+ * \sa http://open-std.org/JTC1/SC22/WG21/docs/papers/2016/n4594.pdf section 20.6
+ * \sa http://en.cppreference.com/w/cpp/utility/optional
+ *
+ * Differences from C++17 include:
+ * \li No constructor and operator= taking a T&&,
+ * because boost::optional lacks a move constructor as of Boost 1.54
+ * \li No constructor, operator=, emplace, and make_optional with std::initializer_list
+ * \li In-place constructor and emplace require copyable arguments,
+ * because boost::in_place requires such
+ * \li Move constructor may or may not exist (it's implicitly defined when available),
+ * because boost::optional lacks a move constructor as of Boost 1.54
+ * \li Non-const operator-> and operator* are not constexpr
+ * \li value() is not constexpr
+ * \li swap is declared without noexcept specification
+ * \li No comparison operators with const T& or nullopt_t
+ * \li No specialized std::hash support
+ */
+
+#ifndef NDN_UTIL_BACKPORTS_OPTIONAL_HPP
+#define NDN_UTIL_BACKPORTS_OPTIONAL_HPP
+
+#include "../common.hpp"
+
+#include <boost/none.hpp>
+#include <boost/optional.hpp>
+#include <boost/utility/typed_in_place_factory.hpp>
+
+namespace ndn {
+
+template<typename T>
+class optional;
+
+struct in_place_t
+{
+};
+constexpr in_place_t in_place{};
+
+class nullopt_t
+{
+public:
+ constexpr explicit
+ nullopt_t(int)
+ {
+ }
+};
+constexpr nullopt_t nullopt(0);
+
+#if BOOST_VERSION >= 105600
+using boost::bad_optional_access;
+#else
+class bad_optional_access : public std::logic_error
+{
+public:
+ bad_optional_access()
+ : std::logic_error("bad optional access")
+ {
+ }
+};
+#endif
+
+template<typename T>
+constexpr bool
+operator==(const optional<T>& lhs, const optional<T>& rhs);
+
+template<typename T>
+constexpr bool
+operator!=(const optional<T>& lhs, const optional<T>& rhs);
+
+template<typename T>
+constexpr bool
+operator<(const optional<T>& lhs, const optional<T>& rhs);
+
+template<typename T>
+constexpr bool
+operator<=(const optional<T>& lhs, const optional<T>& rhs);
+
+template<typename T>
+constexpr bool
+operator>(const optional<T>& lhs, const optional<T>& rhs);
+
+template<typename T>
+constexpr bool
+operator>=(const optional<T>& lhs, const optional<T>& rhs);
+
+template<typename T>
+class optional
+{
+public:
+ static_assert(!std::is_same<typename std::remove_cv<T>::type, in_place_t>::value &&
+ !std::is_same<typename std::remove_cv<T>::type, nullopt_t>::value &&
+ !std::is_reference<T>::value,
+ "Invalid instantiation of optional<T>");
+
+ typedef T value_type;
+
+ constexpr
+ optional() noexcept
+ {
+ }
+
+ constexpr
+ optional(nullopt_t) noexcept
+ {
+ }
+
+ constexpr
+ optional(const T& value)
+ : m_boostOptional(value)
+ {
+ }
+
+ template<typename... Args>
+ constexpr explicit
+ optional(in_place_t, Args&&... args)
+ : m_boostOptional(boost::in_place<T>(std::forward<Args>(args)...))
+ {
+ }
+
+ optional&
+ operator=(nullopt_t) noexcept
+ {
+ m_boostOptional = boost::none;
+ return *this;
+ }
+
+ optional&
+ operator=(const optional& other)
+ {
+ m_boostOptional = other.m_boostOptional;
+ return *this;
+ }
+
+ template<typename U,
+ typename = typename std::enable_if<std::is_same<typename std::decay<U>::type, T>::value>::type>
+ optional&
+ operator=(U&& value)
+ {
+ m_boostOptional = std::forward<U>(value);
+ return *this;
+ }
+
+public: // observers
+ constexpr const T*
+ operator->() const
+ {
+ return m_boostOptional.get_ptr();
+ }
+
+ T*
+ operator->()
+ {
+ return m_boostOptional.get_ptr();
+ }
+
+ constexpr const T&
+ operator*() const
+ {
+ return m_boostOptional.get();
+ }
+
+ T&
+ operator*()
+ {
+ return m_boostOptional.get();
+ }
+
+ constexpr explicit
+ operator bool() const noexcept
+ {
+ return static_cast<bool>(m_boostOptional);
+ }
+
+ const T&
+ value() const
+ {
+ return const_cast<optional*>(this)->value();
+ }
+
+ T&
+ value()
+ {
+#if BOOST_VERSION >= 105600
+ return m_boostOptional.value();
+#else
+ if (!m_boostOptional) {
+ BOOST_THROW_EXCEPTION(bad_optional_access());
+ }
+ return m_boostOptional.get();
+#endif
+ }
+
+ template<typename U>
+ constexpr T
+ value_or(U&& default_value) const
+ {
+#if BOOST_VERSION >= 105600
+ return m_boostOptional.value_or(default_value);
+#else
+ return m_boostOptional.get_value_or(default_value);
+#endif
+ }
+
+public: // modifiers
+ void
+ swap(optional& other)
+ {
+ boost::swap(m_boostOptional, other.m_boostOptional);
+ }
+
+ template<typename... Args>
+ void
+ emplace(Args&&... args)
+ {
+ m_boostOptional = boost::in_place<T>(std::forward<Args>(args)...);
+ }
+
+private:
+ boost::optional<T> m_boostOptional;
+
+ friend bool operator==<T>(const optional<T>&, const optional<T>&);
+ friend bool operator!=<T>(const optional<T>&, const optional<T>&);
+ friend bool operator< <T>(const optional<T>&, const optional<T>&);
+ friend bool operator<=<T>(const optional<T>&, const optional<T>&);
+ friend bool operator> <T>(const optional<T>&, const optional<T>&);
+ friend bool operator>=<T>(const optional<T>&, const optional<T>&);
+};
+
+template<typename T>
+constexpr bool
+operator==(const optional<T>& lhs, const optional<T>& rhs)
+{
+ return operator==(lhs.m_boostOptional, rhs.m_boostOptional);
+}
+
+template<typename T>
+constexpr bool
+operator!=(const optional<T>& lhs, const optional<T>& rhs)
+{
+ return operator!=(lhs.m_boostOptional, rhs.m_boostOptional);
+}
+
+template<typename T>
+constexpr bool
+operator<(const optional<T>& lhs, const optional<T>& rhs)
+{
+ return operator<(lhs.m_boostOptional, rhs.m_boostOptional);
+}
+
+template<typename T>
+constexpr bool
+operator<=(const optional<T>& lhs, const optional<T>& rhs)
+{
+ return operator<=(lhs.m_boostOptional, rhs.m_boostOptional);
+}
+
+template<typename T>
+constexpr bool
+operator>(const optional<T>& lhs, const optional<T>& rhs)
+{
+ return operator>(lhs.m_boostOptional, rhs.m_boostOptional);
+}
+
+template<typename T>
+constexpr bool
+operator>=(const optional<T>& lhs, const optional<T>& rhs)
+{
+ return operator>=(lhs.m_boostOptional, rhs.m_boostOptional);
+}
+
+template<typename T>
+constexpr optional<typename std::decay<T>::type>
+make_optional(T&& value)
+{
+ return optional<typename std::decay<T>::type>(std::forward<T>(value));
+}
+
+template<typename T, typename... Args>
+constexpr optional<T>
+make_optional(Args&&... args)
+{
+ return optional<T>(in_place, std::forward<Args>(args)...);
+}
+
+} // namespace ndn
+
+#endif // NDN_UTIL_BACKPORTS_OPTIONAL_HPP
diff --git a/src/util/backports.hpp b/src/util/backports.hpp
index 072d1c7..acb03e6 100644
--- a/src/util/backports.hpp
+++ b/src/util/backports.hpp
@@ -35,7 +35,7 @@
#if __cpp_lib_make_unique
using std::make_unique;
#else
-template<typename T, typename... Args>
+template<typename T, typename ...Args>
inline unique_ptr<T>
make_unique(Args&&... args)
{
@@ -74,4 +74,6 @@
} // namespace ndn
+#include "backports-optional.hpp"
+
#endif // NDN_UTIL_BACKPORTS_HPP
diff --git a/src/util/signal-signal.hpp b/src/util/signal-signal.hpp
index 7756eae..fb5b14b 100644
--- a/src/util/signal-signal.hpp
+++ b/src/util/signal-signal.hpp
@@ -86,13 +86,13 @@
* who emits this signal, and some handlers may not be executed.
*/
void
- operator()(const TArgs&...args);
+ operator()(const TArgs&... args);
/** \brief (implementation detail) emits a signal
* \note This overload is used by signal-emit.hpp.
*/
void
- operator()(const TArgs&...args, const DummyExtraArg&);
+ operator()(const TArgs&... args, const DummyExtraArg&);
// make Owner a friend of Signal<Owner, ...> so that API for owner can be called
friend Owner;
diff --git a/tests/unit-tests/util/backports.t.cpp b/tests/unit-tests/util/backports.t.cpp
index 35cb8cc..6d8dfd2 100644
--- a/tests/unit-tests/util/backports.t.cpp
+++ b/tests/unit-tests/util/backports.t.cpp
@@ -93,8 +93,182 @@
BOOST_CHECK_EQUAL(x, 10);
}
-BOOST_AUTO_TEST_SUITE_END()
-BOOST_AUTO_TEST_SUITE_END()
+BOOST_AUTO_TEST_SUITE(Optional)
+
+BOOST_AUTO_TEST_CASE(Construct)
+{
+ optional<int> o1;
+ BOOST_CHECK_EQUAL(static_cast<bool>(o1), false);
+
+ optional<int> o2(1);
+ BOOST_CHECK_EQUAL(static_cast<bool>(o2), true);
+ BOOST_CHECK_EQUAL(o2.value(), 1);
+
+ optional<int> o3(o2);
+ BOOST_CHECK_EQUAL(static_cast<bool>(o3), true);
+ BOOST_CHECK_EQUAL(static_cast<bool>(o2), true);
+ BOOST_CHECK_EQUAL(o3.value(), 1);
+
+ optional<std::pair<int, int>> o4(in_place, 41, 42);
+ BOOST_CHECK_EQUAL(static_cast<bool>(o4), true);
+ BOOST_CHECK_EQUAL(o4.value().first, 41);
+ BOOST_CHECK_EQUAL(o4.value().second, 42);
+}
+
+BOOST_AUTO_TEST_CASE(Assign)
+{
+ optional<int> o1;
+ BOOST_CHECK_EQUAL(static_cast<bool>(o1), false);
+
+ optional<int> o2(2);
+ o1 = o2;
+ BOOST_CHECK_EQUAL(static_cast<bool>(o1), true);
+ BOOST_CHECK_EQUAL(o1.value(), 2);
+
+ o1 = nullopt;
+ BOOST_CHECK_EQUAL(static_cast<bool>(o1), false);
+
+ o1 = 18763;
+ BOOST_CHECK_EQUAL(static_cast<bool>(o1), true);
+ BOOST_CHECK_EQUAL(o1.value(), 18763);
+}
+
+BOOST_AUTO_TEST_CASE(Access)
+{
+ optional<int> o1(1);
+ BOOST_CHECK_EQUAL(*o1, 1);
+
+ optional<std::string> o2("abc");
+ BOOST_CHECK_EQUAL(o2->size(), 3);
+}
+
+BOOST_AUTO_TEST_CASE(HasValue)
+{
+ optional<int> o1;
+ BOOST_CHECK_EQUAL(static_cast<bool>(o1), false);
+ BOOST_CHECK_EQUAL(!o1, true);
+ if (o1) {
+ BOOST_ERROR("o1 should evaluate to false");
+ }
+
+ optional<int> o2(1);
+ BOOST_CHECK_EQUAL(static_cast<bool>(o2), true);
+ BOOST_CHECK_EQUAL(!o2, false);
+ if (o2) {
+ }
+ else {
+ BOOST_ERROR("o2 should evaluate to true");
+ }
+}
+
+BOOST_AUTO_TEST_CASE(Value)
+{
+ optional<int> o1;
+ BOOST_CHECK_THROW(o1.value(), bad_optional_access);
+
+ optional<int> o2(2);
+ BOOST_CHECK_NO_THROW(o2.value());
+}
+
+BOOST_AUTO_TEST_CASE(ValueOr)
+{
+ optional<int> o1;
+ BOOST_CHECK_EQUAL(o1.value_or(3.0), 3);
+
+ optional<int> o2(2);
+ BOOST_CHECK_EQUAL(o2.value_or(3.0), 2);
+}
+
+BOOST_AUTO_TEST_CASE(Swap)
+{
+ optional<int> o1;
+ optional<int> o2(2);
+
+ o1.swap(o2);
+ BOOST_CHECK_EQUAL(static_cast<bool>(o1), true);
+ BOOST_CHECK_EQUAL(o1.value(), 2);
+ BOOST_CHECK_EQUAL(static_cast<bool>(o2), false);
+}
+
+BOOST_AUTO_TEST_CASE(Emplace)
+{
+ optional<std::pair<int, int>> o1;
+ o1.emplace(11, 12);
+ BOOST_CHECK_EQUAL(static_cast<bool>(o1), true);
+ BOOST_CHECK_EQUAL(o1.value().first, 11);
+ BOOST_CHECK_EQUAL(o1.value().second, 12);
+}
+
+BOOST_AUTO_TEST_CASE(Compare)
+{
+ optional<int> o0a;
+ optional<int> o0b;
+ optional<int> o1a(1);
+ optional<int> o1b(1);
+ optional<int> o2(2);
+
+ BOOST_CHECK_EQUAL(o0a == o0b, true);
+ BOOST_CHECK_EQUAL(o0a != o0b, false);
+ BOOST_CHECK_EQUAL(o0a < o0b, false);
+ BOOST_CHECK_EQUAL(o0a <= o0b, true);
+ BOOST_CHECK_EQUAL(o0a > o0b, false);
+ BOOST_CHECK_EQUAL(o0a >= o0b, true);
+
+ BOOST_CHECK_EQUAL(o1a == o1b, true);
+ BOOST_CHECK_EQUAL(o1a != o1b, false);
+ BOOST_CHECK_EQUAL(o1a < o1b, false);
+ BOOST_CHECK_EQUAL(o1a <= o1b, true);
+ BOOST_CHECK_EQUAL(o1a > o1b, false);
+ BOOST_CHECK_EQUAL(o1a >= o1b, true);
+
+ BOOST_CHECK_EQUAL(o0a == o1a, false);
+ BOOST_CHECK_EQUAL(o0a != o1a, true);
+ BOOST_CHECK_EQUAL(o0a < o1a, true);
+ BOOST_CHECK_EQUAL(o0a <= o1a, true);
+ BOOST_CHECK_EQUAL(o0a > o1a, false);
+ BOOST_CHECK_EQUAL(o0a >= o1a, false);
+
+ BOOST_CHECK_EQUAL(o1a == o0a, false);
+ BOOST_CHECK_EQUAL(o1a != o0a, true);
+ BOOST_CHECK_EQUAL(o1a < o0a, false);
+ BOOST_CHECK_EQUAL(o1a <= o0a, false);
+ BOOST_CHECK_EQUAL(o1a > o0a, true);
+ BOOST_CHECK_EQUAL(o1a >= o0a, true);
+
+ BOOST_CHECK_EQUAL(o1a == o2, false);
+ BOOST_CHECK_EQUAL(o1a != o2, true);
+ BOOST_CHECK_EQUAL(o1a < o2, true);
+ BOOST_CHECK_EQUAL(o1a <= o2, true);
+ BOOST_CHECK_EQUAL(o1a > o2, false);
+ BOOST_CHECK_EQUAL(o1a >= o2, false);
+
+ BOOST_CHECK_EQUAL(o2 == o1a, false);
+ BOOST_CHECK_EQUAL(o2 != o1a, true);
+ BOOST_CHECK_EQUAL(o2 < o1a, false);
+ BOOST_CHECK_EQUAL(o2 <= o1a, false);
+ BOOST_CHECK_EQUAL(o2 > o1a, true);
+ BOOST_CHECK_EQUAL(o2 >= o1a, true);
+}
+
+BOOST_AUTO_TEST_CASE(MakeOptional)
+{
+ auto o1 = make_optional(1);
+ static_assert(std::is_same<decltype(o1), optional<int>>::value, "o1 must be optional<int>");
+ BOOST_CHECK_EQUAL(static_cast<bool>(o1), true);
+ BOOST_CHECK_EQUAL(o1.value(), 1);
+
+ auto o2 = make_optional<std::pair<int, int>>(21, 22);
+ static_assert(std::is_same<decltype(o2), optional<std::pair<int, int>>>::value,
+ "o2 must be optional<std::pair<int, int>>");
+ BOOST_CHECK_EQUAL(static_cast<bool>(o2), true);
+ BOOST_CHECK_EQUAL(o2.value().first, 21);
+ BOOST_CHECK_EQUAL(o2.value().second, 22);
+}
+
+BOOST_AUTO_TEST_SUITE_END() // Optional
+
+BOOST_AUTO_TEST_SUITE_END() // TestBackports
+BOOST_AUTO_TEST_SUITE_END() // Util
} // namespace tests
} // namespace ndn