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