Added nullopt and implicit construction to rtc::Optional

Both of these features are in std::optional and the lack
of them is making Optional use in WebRTC more cumbersome.

We are currently looking at using a more fully-fledged library
for some of our standard utility classes. This is merely a
stop-gap measure.

Bug: None
Change-Id: I958a984fa97a42f6e407be1f38662553efeceac4
Reviewed-on: https://webrtc-review.googlesource.com/22920
Commit-Queue: Oskar Sundbom <ossu@webrtc.org>
Reviewed-by: Karl Wiberg <kwiberg@webrtc.org>
Reviewed-by: Danil Chapovalov <danilchap@webrtc.org>
Cr-Commit-Position: refs/heads/master@{#20688}
This commit is contained in:
Oskar Sundbom 2017-11-15 12:24:28 +01:00 committed by Commit Bot
parent 2248ab6a46
commit e403212ede
3 changed files with 127 additions and 3 deletions

View File

@ -21,5 +21,14 @@ void* FunctionThatDoesNothingImpl(void* x) {
#endif
struct NulloptArg {
constexpr NulloptArg() {}
};
static NulloptArg nullopt_arg;
} // namespace optional_internal
const nullopt_t nullopt(rtc::optional_internal::nullopt_arg);
} // namespace rtc

View File

@ -50,8 +50,24 @@ inline T* FunctionThatDoesNothing(T* x) {
#endif
struct NulloptArg;
} // namespace optional_internal
// nullopt_t must be a non-aggregate literal type with a constexpr constructor
// that takes some implementation-defined literal type. It mustn't have a
// default constructor nor an initializer-list constructor.
// See:
// http://en.cppreference.com/w/cpp/utility/optional/nullopt_t
// That page uses int, though this seems to confuse older versions of GCC.
struct nullopt_t {
constexpr explicit nullopt_t(rtc::optional_internal::NulloptArg&) {}
};
// Specification:
// http://en.cppreference.com/w/cpp/utility/optional/nullopt
extern const nullopt_t nullopt;
// Simple std::optional-wannabe. It either contains a T or not.
//
// A moved-from Optional<T> may only be destroyed, and assigned to if T allows
@ -100,11 +116,16 @@ class Optional final {
// Construct an empty Optional.
Optional() : has_value_(false), empty_('\0') { PoisonValue(); }
Optional(rtc::nullopt_t) // NOLINT(runtime/explicit)
: Optional() {}
// Construct an Optional that contains a value.
explicit Optional(const T& value) : has_value_(true) {
Optional(const T& value) // NOLINT(runtime/explicit)
: has_value_(true) {
new (&value_) T(value);
}
explicit Optional(T&& value) : has_value_(true) {
Optional(T&& value) // NOLINT(runtime/explicit)
: has_value_(true) {
new (&value_) T(std::move(value));
}
@ -134,6 +155,11 @@ class Optional final {
UnpoisonValue();
}
Optional& operator=(rtc::nullopt_t) {
reset();
return *this;
}
// Copy assignment. Uses T's copy assignment if both sides have a value, T's
// copy constructor if only the right-hand side has a value.
Optional& operator=(const Optional& m) {
@ -274,6 +300,14 @@ class Optional final {
return opt.has_value_ && value == opt.value_;
}
friend bool operator==(const Optional& opt, rtc::nullopt_t) {
return !opt.has_value_;
}
friend bool operator==(rtc::nullopt_t, const Optional& opt) {
return !opt.has_value_;
}
friend bool operator!=(const Optional& m1, const Optional& m2) {
return m1.has_value_ && m2.has_value_ ? m1.value_ != m2.value_
: m1.has_value_ != m2.has_value_;
@ -285,6 +319,14 @@ class Optional final {
return !opt.has_value_ || value != opt.value_;
}
friend bool operator!=(const Optional& opt, rtc::nullopt_t) {
return opt.has_value_;
}
friend bool operator!=(rtc::nullopt_t, const Optional& opt) {
return opt.has_value_;
}
private:
// Tell sanitizers that value_ shouldn't be touched.
void PoisonValue() {

View File

@ -159,6 +159,16 @@ TEST(OptionalTest, TestConstructDefault) {
EXPECT_EQ(V(), *log);
}
TEST(OptionalTest, TestConstructNullopt) {
auto log = Logger::Setup();
{
Optional<Logger> x(nullopt);
EXPECT_FALSE(x);
EXPECT_FALSE(x.has_value());
}
EXPECT_EQ(V(), *log);
}
TEST(OptionalTest, TestConstructCopyEmpty) {
auto log = Logger::Setup();
{
@ -249,6 +259,36 @@ TEST(OptionalTest, TestCopyAssignToFullFromEmpty) {
*log);
}
TEST(OptionalTest, TestCopyAssignToFullFromNullopt) {
auto log = Logger::Setup();
{
Optional<Logger> x(Logger(17));
log->push_back("---");
x = nullopt;
log->push_back("---");
EXPECT_FALSE(x);
}
EXPECT_EQ(
V("0:17. explicit constructor", "1:17. move constructor (from 0:17)",
"0:17. destructor", "---", "1:17. destructor", "---"),
*log);
}
TEST(OptionalTest, TestCopyAssignToFullFromEmptyBraces) {
auto log = Logger::Setup();
{
Optional<Logger> x(Logger(17));
log->push_back("---");
x = {};
log->push_back("---");
EXPECT_FALSE(x);
}
EXPECT_EQ(
V("0:17. explicit constructor", "1:17. move constructor (from 0:17)",
"0:17. destructor", "---", "1:17. destructor", "---"),
*log);
}
TEST(OptionalTest, TestCopyAssignToEmptyFromFull) {
auto log = Logger::Setup();
{
@ -714,12 +754,45 @@ TEST(OptionalTest, TestEquality) {
*log);
}
TEST(OptionalTest, TestEqualityWithNullopt) {
auto log = Logger::Setup();
{
Logger a(17);
Optional<Logger> ma(a), me;
// Using operator== and operator!= explicitly instead of EXPECT_EQ/EXPECT_NE
// macros because those operators are under test.
log->push_back("---");
EXPECT_FALSE(ma == nullopt);
EXPECT_FALSE(nullopt == ma);
EXPECT_TRUE(me == nullopt);
EXPECT_TRUE(nullopt == me);
EXPECT_TRUE(ma != nullopt);
EXPECT_TRUE(nullopt != ma);
EXPECT_FALSE(me != nullopt);
EXPECT_FALSE(nullopt != me);
log->push_back("---");
}
// clang-format off
EXPECT_EQ(V("0:17. explicit constructor",
"1:17. copy constructor (from 0:17)",
"---",
// No operators should be called when comparing to empty.
"---",
"1:17. destructor",
"0:17. destructor"),
*log);
// clang-format on
}
TEST(OptionalTest, TestEqualityWithObject) {
auto log = Logger::Setup();
{
Logger a(17), b(42);
Optional<Logger> ma(a), me;
// Using operator== and operator!= explicetly instead of EXPECT_EQ/EXPECT_NE
// Using operator== and operator!= explicitly instead of EXPECT_EQ/EXPECT_NE
// macros because those operators are under test.
log->push_back("---");