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