enh(Nullable, Optional): reimplement using std::optional (#4502)

This commit is contained in:
Matej Kenda 2024-09-18 17:06:54 +02:00
parent 03c35cff93
commit e55bb7032d
4 changed files with 63 additions and 102 deletions

View File

@ -45,7 +45,7 @@ class Transcoder;
namespace Keywords { namespace Keywords {
static const NullData null = NULL_GENERIC; static const NullData null = std::nullopt;
} // namespace Keywords } // namespace Keywords

View File

@ -20,18 +20,13 @@
#include "Poco/Foundation.h" #include "Poco/Foundation.h"
#include "Poco/Exception.h" #include "Poco/Exception.h"
#include <algorithm> #include <optional>
#include <iostream> #include <iostream>
namespace Poco { namespace Poco {
using NullType = std::nullopt_t;
enum NullType
{
NULL_GENERIC = 0
};
template <typename C> template <typename C>
class Nullable class Nullable
@ -58,88 +53,70 @@ class Nullable
/// default construction. /// default construction.
{ {
public: public:
Nullable(): Nullable()
/// Creates an empty Nullable. /// Creates an empty Nullable.
_value(),
_isNull(true),
_null()
{ {
} }
Nullable(const NullType&): Nullable(const NullType&)
/// Creates an empty Nullable. /// Creates an empty Nullable.
_value(),
_isNull(true),
_null()
{ {
} }
Nullable(const C& value): Nullable(const C& value):
/// Creates a Nullable with the given value. /// Creates a Nullable with the given value.
_value(value), _optional(value)
_isNull(false),
_null()
{ {
} }
Nullable(C&& value): Nullable(C&& value):
/// Creates a Nullable by moving the given value. /// Creates a Nullable by moving the given value.
_value(std::forward<C>(value)), _optional(std::forward<C>(value))
_isNull(false),
_null()
{ {
} }
Nullable(const Nullable& other): Nullable(const Nullable& other):
/// Creates a Nullable by copying another one. /// Creates a Nullable by copying another one.
_value(other._value), _optional(other._optional)
_isNull(other._isNull),
_null()
{ {
} }
Nullable(Nullable&& other) noexcept: Nullable(Nullable&& other) noexcept:
/// Creates a Nullable by moving another one. /// Creates a Nullable by moving another one.
_value(std::move(other._value)), _optional(std::move(other._optional))
_isNull(other._isNull),
_null()
{ {
other._isNull = true; other._optional.reset();
} }
~Nullable() ~Nullable() = default;
/// Destroys the Nullable. /// Destroys the Nullable.
{
}
Nullable& assign(const C& value) Nullable& assign(const C& value)
/// Assigns a value to the Nullable. /// Assigns a value to the Nullable.
{ {
_value = value; _optional.emplace(value);
_isNull = false;
return *this; return *this;
} }
Nullable& assign(C&& value) Nullable& assign(C&& value)
/// Assigns a value to the Nullable. /// Assigns a value to the Nullable.
{ {
_value = std::move(value); _optional.emplace(std::move(value));
_isNull = false;
return *this; return *this;
} }
Nullable& assign(const Nullable& other) Nullable& assign(const Nullable& other)
/// Assigns another Nullable. /// Assigns another Nullable.
{ {
Nullable tmp(other); if (&other != this)
swap(tmp); _optional = other._optional;
return *this; return *this;
} }
Nullable& assign(NullType) Nullable& assign(NullType)
/// Sets value to null. /// Sets value to null.
{ {
_isNull = true; _optional.reset();
return *this; return *this;
} }
@ -164,42 +141,39 @@ public:
Nullable& operator = (Nullable&& other) noexcept Nullable& operator = (Nullable&& other) noexcept
/// Moves another Nullable. /// Moves another Nullable.
{ {
_isNull = other._isNull; _optional = std::move(other._optional);
_value = std::move(other._value);
other._isNull = true;
return *this; return *this;
} }
Nullable& operator = (NullType) Nullable& operator = (NullType)
/// Assigns another Nullable. /// Assigns NullType.
{ {
_isNull = true; _optional.reset();
return *this; return *this;
} }
void swap(Nullable& other) noexcept void swap(Nullable& other) noexcept
/// Swaps this Nullable with other. /// Swaps this Nullable with other.
{ {
std::swap(_value, other._value); std::swap(_optional, other._optional);
std::swap(_isNull, other._isNull);
} }
bool operator == (const Nullable<C>& other) const bool operator == (const Nullable<C>& other) const
/// Compares two Nullables for equality /// Compares two Nullables for equality
{ {
return (_isNull && other._isNull) || (_isNull == other._isNull && _value == other._value); return _optional == other._optional;
} }
bool operator == (const C& value) const bool operator == (const C& value) const
/// Compares Nullable with value for equality /// Compares Nullable with value for equality
{ {
return (!_isNull && _value == value); return (_optional.has_value() && _optional.value() == value);
} }
bool operator == (const NullType&) const bool operator == (const NullType&) const
/// Compares Nullable with NullData for equality /// Compares Nullable with NullData for equality
{ {
return _isNull; return !_optional.has_value();
} }
bool operator != (const C& value) const bool operator != (const C& value) const
@ -217,7 +191,7 @@ public:
bool operator != (const NullType&) const bool operator != (const NullType&) const
/// Compares with NullData for non equality /// Compares with NullData for non equality
{ {
return !_isNull; return _optional.has_value();
} }
bool operator < (const Nullable<C>& other) const bool operator < (const Nullable<C>& other) const
@ -225,14 +199,7 @@ public:
/// value is smaler than the other object's value. /// value is smaler than the other object's value.
/// Null value is smaller than a non-null value. /// Null value is smaller than a non-null value.
{ {
if (_isNull && other._isNull) return false; return _optional < other._optional;
if (!_isNull && !other._isNull)
return (_value < other._value);
if (_isNull && !other._isNull) return true;
return false;
} }
bool operator > (const Nullable<C>& other) const bool operator > (const Nullable<C>& other) const
@ -248,8 +215,8 @@ public:
/// ///
/// Throws a NullValueException if the Nullable is empty. /// Throws a NullValueException if the Nullable is empty.
{ {
if (!_isNull) if (_optional.has_value())
return _value; return _optional.value();
else else
throw NullValueException(); throw NullValueException();
} }
@ -259,8 +226,8 @@ public:
/// ///
/// Throws a NullValueException if the Nullable is empty. /// Throws a NullValueException if the Nullable is empty.
{ {
if (!_isNull) if (_optional.has_value())
return _value; return _optional.value();
else else
throw NullValueException(); throw NullValueException();
} }
@ -269,7 +236,10 @@ public:
/// Returns the Nullable's value, or the /// Returns the Nullable's value, or the
/// given default value if the Nullable is empty. /// given default value if the Nullable is empty.
{ {
return _isNull ? deflt : _value; if (_optional.has_value())
return _optional.value();
return deflt;
} }
operator C& () operator C& ()
@ -284,7 +254,7 @@ public:
return value(); return value();
} }
operator NullType& () operator const NullType& () const
/// Get reference to the value /// Get reference to the value
{ {
return _null; return _null;
@ -293,19 +263,18 @@ public:
bool isNull() const bool isNull() const
/// Returns true if the Nullable is empty. /// Returns true if the Nullable is empty.
{ {
return _isNull; return !_optional.has_value();
} }
void clear() void clear()
/// Clears the Nullable. /// Clears the Nullable.
{ {
_isNull = true; _optional.reset();
} }
private: private:
C _value; std::optional<C> _optional;
bool _isNull; static constexpr NullType _null {std::nullopt};
NullType _null;
}; };

View File

@ -20,7 +20,7 @@
#include "Poco/Foundation.h" #include "Poco/Foundation.h"
#include "Poco/Exception.h" #include "Poco/Exception.h"
#include <algorithm> #include <optional>
namespace Poco { namespace Poco {
@ -53,40 +53,33 @@ class Optional
/// nillable == true. /// nillable == true.
{ {
public: public:
Optional(): Optional()
/// Creates an empty Optional. /// Creates an empty Optional.
_value(),
_isSpecified(false)
{ {
} }
Optional(const C& value): Optional(const C& value):
/// Creates a Optional with the given value. /// Creates a Optional with the given value.
_value(value), _optional(value)
_isSpecified(true)
{ {
} }
Optional(C&& value): Optional(C&& value):
/// Creates a Optional by moving the given value. /// Creates a Optional by moving the given value.
_value(std::forward<C>(value)), _optional(std::forward<C>(value))
_isSpecified(true)
{ {
} }
Optional(const Optional& other): Optional(const Optional& other):
/// Creates a Optional by copying another one. /// Creates a Optional by copying another one.
_value(other._value), _optional(other._optional)
_isSpecified(other._isSpecified)
{ {
} }
Optional(Optional&& other) noexcept: Optional(Optional&& other) noexcept:
/// Creates a Optional by moving another one. /// Creates a Optional by moving another one.
_value(std::move(other._value)), _optional(std::move(other._optional))
_isSpecified(other._isSpecified)
{ {
other._isSpecified = false;
} }
~Optional() ~Optional()
@ -97,16 +90,14 @@ public:
Optional& assign(const C& value) Optional& assign(const C& value)
/// Assigns a value to the Optional. /// Assigns a value to the Optional.
{ {
_value = value; _optional.emplace(value);
_isSpecified = true;
return *this; return *this;
} }
Optional& assign(C&& value) Optional& assign(C&& value)
/// Moves a value into the Optional. /// Moves a value into the Optional.
{ {
_value = std::move(value); _optional.emplace(std::move(value));
_isSpecified = true;
return *this; return *this;
} }
@ -135,17 +126,14 @@ public:
Optional& operator = (Optional&& other) noexcept Optional& operator = (Optional&& other) noexcept
{ {
_value = std::move(other._value); _optional = std::move(other._optional);
_isSpecified = other._isSpecified;
other._isSpecified = false;
return *this; return *this;
} }
void swap(Optional& other) noexcept void swap(Optional& other) noexcept
{ {
using std::swap; using std::swap;
swap(_value, other._value); swap(_optional, other._optional);
swap(_isSpecified, other._isSpecified);
} }
const C& value() const const C& value() const
@ -153,10 +141,10 @@ public:
/// ///
/// Throws a Poco::NullValueException if the value has not been specified. /// Throws a Poco::NullValueException if the value has not been specified.
{ {
if (_isSpecified) if (_optional.has_value())
return _value; return _optional.value();
else
throw Poco::NullValueException(); throw Poco::NullValueException();
} }
const C& value(const C& deflt) const const C& value(const C& deflt) const
@ -164,24 +152,27 @@ public:
/// given default value if the Optional's /// given default value if the Optional's
/// value has not been specified. /// value has not been specified.
{ {
return _isSpecified ? _value : deflt; if (_optional.has_value())
return _optional.value();
return deflt;
} }
bool isSpecified() const bool isSpecified() const
/// Returns true iff the Optional's value has been specified. /// Returns true iff the Optional's value has been specified.
{ {
return _isSpecified; return _optional.has_value();
} }
void clear() void clear()
/// Clears the Optional. /// Clears the Optional.
{ {
_isSpecified = false; _optional.reset();
} }
private: private:
C _value;
bool _isSpecified; std::optional<C> _optional;
}; };

View File

@ -30,6 +30,7 @@
#include <vector> #include <vector>
#include <cstring> #include <cstring>
using namespace std::string_literals;
using Poco::Bugcheck; using Poco::Bugcheck;
using Poco::Exception; using Poco::Exception;
@ -966,7 +967,7 @@ void CoreTest::testNullable()
assertTrue (i == 1); assertTrue (i == 1);
assertTrue (f == 1.5); assertTrue (f == 1.5);
assertTrue (s == "abc"); assertTrue (s == "abc"s);
i.clear(); i.clear();
f.clear(); f.clear();
@ -1035,7 +1036,7 @@ void CoreTest::testNullable()
assertTrue (n2 != n1); assertTrue (n2 != n1);
assertTrue (n1 > n2); assertTrue (n1 > n2);
NullType nd{}; const auto nd {std::nullopt};
assertTrue (n1 != nd); assertTrue (n1 != nd);
assertTrue (nd != n1); assertTrue (nd != n1);
n1.clear(); n1.clear();