abg-cxx-compat: add simplified version of std::optional

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>
This commit is contained in:
Matthias Maennich 2021-01-27 12:58:34 +00:00 committed by Dodji Seketeli
parent 701de3ba5d
commit c92d724e01
2 changed files with 136 additions and 0 deletions

View File

@ -8,8 +8,92 @@
#ifndef __ABG_CXX_COMPAT_H
#define __ABG_CXX_COMPAT_H
// 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 >= 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 maintenance 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

View File

@ -12,3 +12,55 @@
#include "lib/catch.hpp"
#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);
}