[v1,01/16] abg-cxx-compat: add simplified version of std::optional

Message ID 20200619214305.562-2-maennich@google.com
State Superseded
Headers
Series Refactor (k)symtab reader |

Commit Message

Matthias Männich June 19, 2020, 9:42 p.m. UTC
  In the absence (but desire) of std::optional<T>, add a simplified
version of it to abg_compat:: in case we are compiling with a pre-C++17
standard. Otherwise use std::optional from <optional> directly.

This is being used by a later patch and serves as a prerequisite.
It only serves the purpose of being a compatibility implementation and
does not claim to be complete at all. Just enough for the project's
needs.

	* include/abg-cxx-compat.h (abg_compat::optional): Add new class.
	* tests/tests-cxx-compat.cc: Add new test cases.

Reviewed-by: Giuliano Procida <gprocida@google.com>
Signed-off-by: Matthias Maennich <maennich@google.com>
---
 include/abg-cxx-compat.h | 86 ++++++++++++++++++++++++++++++++++++++++
 tests/test-cxx-compat.cc | 51 ++++++++++++++++++++++++
 2 files changed, 137 insertions(+)
  

Patch

diff --git a/include/abg-cxx-compat.h b/include/abg-cxx-compat.h
index 714177183945..1283e9191d46 100644
--- a/include/abg-cxx-compat.h
+++ b/include/abg-cxx-compat.h
@@ -23,6 +23,8 @@ 
 #ifndef __ABG_CXX_COMPAT_H
 #define __ABG_CXX_COMPAT_H
 
+// C++11 support (mostly via tr1 if compiled with earlier standard)
+
 #if __cplusplus >= 201103L
 
 #include <functional>
@@ -39,6 +41,18 @@ 
 
 #endif
 
+// C++17 support (via custom implementations if compiled with earlier standard)
+
+#if __cplusplus >= 201703L
+
+#include <optional>
+
+#else
+
+#include <stdexcept> // for throwing std::runtime_error("bad_optional_access")
+
+#endif
+
 namespace abg_compat {
 
 #if __cplusplus >= 201103L
@@ -77,6 +91,78 @@  using std::tr1::unordered_set;
 
 #endif
 
+#if __cplusplus >= 201703L
+
+using std::optional;
+
+#else
+
+// <optional>
+
+/// Simplified implementation of std::optional just enough to be used as a
+/// replacement for our purposes and when compiling with pre C++17.
+///
+/// The implementation intentionally does not support a whole lot of features
+/// to minimize the maintainence effort with this.
+template <typename T> class optional
+{
+  bool has_value_;
+  T    value_;
+
+public:
+  optional() : has_value_(false), value_() {}
+  optional(const T& value) : has_value_(true), value_(value) {}
+
+  bool
+  has_value() const
+  {
+    return has_value_;
+  }
+
+  const T&
+  value() const
+  {
+    if (!has_value_)
+      throw std::runtime_error("bad_optional_access");
+    return value_;
+  }
+
+  const T
+  value_or(const T& default_value) const
+  {
+    if (!has_value_)
+      return default_value;
+    return value_;
+  }
+
+  const T&
+  operator*() const
+  { return value_; }
+
+  T&
+  operator*()
+  { return value_; }
+
+  const T*
+  operator->() const
+  { return &value_; }
+
+  T*
+  operator->()
+  { return &value_; }
+
+  optional&
+  operator=(const T& value)
+  {
+    has_value_ = true;
+    value_ = value;
+    return *this;
+  }
+
+  explicit operator bool() const { return has_value_; }
+};
+
+#endif
 }
 
 #endif  // __ABG_CXX_COMPAT_H
diff --git a/tests/test-cxx-compat.cc b/tests/test-cxx-compat.cc
index 0a230a54e1a5..adeb7b8d7e4f 100644
--- a/tests/test-cxx-compat.cc
+++ b/tests/test-cxx-compat.cc
@@ -28,3 +28,54 @@ 
 
 #include "abg-cxx-compat.h"
 
+using abg_compat::optional;
+
+TEST_CASE("OptionalConstruction", "[abg_compat::optional]")
+{
+  optional<bool> opt1;
+  REQUIRE_FALSE(opt1.has_value());
+
+  optional<bool> opt2(true);
+  REQUIRE(opt2.has_value());
+  CHECK(opt2.value() == true);
+
+  optional<bool> opt3(false);
+  REQUIRE(opt3.has_value());
+  CHECK(opt3.value() == false);
+}
+
+TEST_CASE("OptionalValue", "[abg_compat::optional]")
+{
+  optional<bool> opt;
+  REQUIRE_FALSE(opt.has_value());
+  REQUIRE_THROWS(opt.value());
+
+  opt = true;
+  REQUIRE_NOTHROW(opt.value());
+  CHECK(opt.value() == true);
+}
+
+TEST_CASE("OptionalValueOr", "[abg_compat::optional]")
+{
+  optional<std::string> opt;
+  REQUIRE_FALSE(opt.has_value());
+
+  const std::string& mine = "mine";
+  // Ensure we get a copy of our own value.
+  CHECK(opt.value_or(mine) == mine);
+
+  // Now set the value
+  const std::string& other = "other";
+  opt = other;
+  CHECK(opt.value_or(mine) != mine);
+  CHECK(opt.value_or(mine) == other);
+}
+
+TEST_CASE("OptionalDeref", "[abg_compat::optional]")
+{
+  optional<std::string> opt("asdf");
+  REQUIRE(opt.has_value());
+
+  CHECK(*opt == "asdf");
+  CHECK(opt->size() == 4);
+}