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:
parent
2248ab6a46
commit
e403212ede
@ -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
|
||||
|
||||
@ -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() {
|
||||
|
||||
@ -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("---");
|
||||
|
||||
|
||||
Loading…
x
Reference in New Issue
Block a user