net: make FaceUri usable as container key

refs #4227

Change-Id: I4389fe6691ab2499c73e6ed83d4090b77fdbafca
diff --git a/ndn-cxx/net/face-uri.hpp b/ndn-cxx/net/face-uri.hpp
index d427858..aab2597 100644
--- a/ndn-cxx/net/face-uri.hpp
+++ b/ndn-cxx/net/face-uri.hpp
@@ -35,6 +35,7 @@
 #include <boost/asio/ip/tcp.hpp>
 #include <boost/asio/ip/udp.hpp>
 #include <boost/asio/local/stream_protocol.hpp>
+#include <boost/operators.hpp>
 
 namespace ndn {
 
@@ -42,7 +43,7 @@
  * \brief The underlying protocol and address used by a Face.
  * \sa https://redmine.named-data.net/projects/nfd/wiki/FaceMgmt#FaceUri
  */
-class FaceUri
+class FaceUri : private boost::totally_ordered<FaceUri>
 {
 public:
   class Error : public std::invalid_argument
@@ -180,21 +181,23 @@
 private: // non-member operators
   // NOTE: the following "hidden friend" operators are available via
   //       argument-dependent lookup only and must be defined inline.
+  // boost::totally_ordered provides !=, <=, >=, and > operators.
 
   friend bool
   operator==(const FaceUri& lhs, const FaceUri& rhs)
   {
-    return !(lhs != rhs);
+    return lhs.m_isV6 == rhs.m_isV6 &&
+           lhs.m_scheme == rhs.m_scheme &&
+           lhs.m_host == rhs.m_host &&
+           lhs.m_port == rhs.m_port &&
+           lhs.m_path == rhs.m_path;
   }
 
   friend bool
-  operator!=(const FaceUri& lhs, const FaceUri& rhs)
+  operator<(const FaceUri& lhs, const FaceUri& rhs)
   {
-    return lhs.m_isV6 != rhs.m_isV6 ||
-           lhs.m_scheme != rhs.m_scheme ||
-           lhs.m_host != rhs.m_host ||
-           lhs.m_port != rhs.m_port ||
-           lhs.m_path != rhs.m_path;
+    return std::tie(lhs.m_scheme, lhs.m_isV6, lhs.m_host, lhs.m_port, lhs.m_path) <
+           std::tie(rhs.m_scheme, rhs.m_isV6, rhs.m_host, rhs.m_port, rhs.m_path);
   }
 
 private:
diff --git a/tests/unit/net/face-uri.t.cpp b/tests/unit/net/face-uri.t.cpp
index 34e050e..fe34b01 100644
--- a/tests/unit/net/face-uri.t.cpp
+++ b/tests/unit/net/face-uri.t.cpp
@@ -37,6 +37,7 @@
 namespace ndn::tests {
 
 BOOST_CONCEPT_ASSERT((boost::EqualityComparable<FaceUri>));
+BOOST_CONCEPT_ASSERT((boost::Comparable<FaceUri>));
 
 BOOST_AUTO_TEST_SUITE(Net)
 BOOST_AUTO_TEST_SUITE(TestFaceUri)
@@ -610,6 +611,42 @@
   BOOST_CHECK_EQUAL(uri.toString(), "wsclient://76.90.11.239:56366");
 }
 
+BOOST_AUTO_TEST_CASE(Compare)
+{
+  FaceUri uri0("udp://[::1]:6363");
+  FaceUri uri1("tcp://[::1]:6363");
+  FaceUri uri2("tcp://127.0.0.1:6363");
+  FaceUri uri3("unix:///run/ndn/nfd.sock");
+
+  BOOST_CHECK_EQUAL(uri0, uri0);
+  BOOST_CHECK_LE(uri0, uri0);
+  BOOST_CHECK_GE(uri0, uri0);
+
+  BOOST_CHECK_GT(uri0, uri1);
+  BOOST_CHECK_GE(uri0, uri1);
+  BOOST_CHECK_NE(uri0, uri1);
+
+  BOOST_CHECK_LT(uri1, uri0);
+  BOOST_CHECK_LE(uri1, uri0);
+  BOOST_CHECK_NE(uri1, uri0);
+
+  BOOST_CHECK_GT(uri0, uri2);
+  BOOST_CHECK_GE(uri0, uri2);
+  BOOST_CHECK_NE(uri0, uri2);
+
+  BOOST_CHECK_LT(uri2, uri0);
+  BOOST_CHECK_LE(uri2, uri0);
+  BOOST_CHECK_NE(uri2, uri0);
+
+  BOOST_CHECK_LT(uri0, uri3);
+  BOOST_CHECK_LE(uri0, uri3);
+  BOOST_CHECK_NE(uri0, uri3);
+
+  BOOST_CHECK_GT(uri3, uri0);
+  BOOST_CHECK_GE(uri3, uri0);
+  BOOST_CHECK_NE(uri3, uri0);
+}
+
 BOOST_AUTO_TEST_SUITE_END() // TestFaceUri
 BOOST_AUTO_TEST_SUITE_END() // Net