mirror of
https://github.com/Kitware/CMake.git
synced 2025-12-31 19:00:54 -06:00
Source: Add cm::optional
This commit is contained in:
343
Source/cm_optional.hxx
Normal file
343
Source/cm_optional.hxx
Normal file
@@ -0,0 +1,343 @@
|
||||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
file Copyright.txt or https://cmake.org/licensing for details. */
|
||||
#ifndef cm_optional_hxx
|
||||
#define cm_optional_hxx
|
||||
|
||||
#include "cmConfigure.h" // IWYU pragma: keep
|
||||
|
||||
#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
|
||||
# define CMake_HAVE_CXX_OPTIONAL
|
||||
#endif
|
||||
|
||||
#if defined(CMake_HAVE_CXX_OPTIONAL)
|
||||
# include <optional>
|
||||
#else
|
||||
# include "cm_utility.hxx"
|
||||
# include <memory>
|
||||
# include <utility>
|
||||
#endif
|
||||
|
||||
namespace cm {
|
||||
|
||||
#if defined(CMake_HAVE_CXX_OPTIONAL)
|
||||
|
||||
using std::nullopt_t;
|
||||
using std::nullopt;
|
||||
using std::optional;
|
||||
using std::bad_optional_access;
|
||||
using std::make_optional;
|
||||
|
||||
#else
|
||||
|
||||
class bad_optional_access : public std::exception
|
||||
{
|
||||
using std::exception::exception;
|
||||
};
|
||||
|
||||
struct nullopt_t
|
||||
{
|
||||
explicit constexpr nullopt_t(int) {}
|
||||
};
|
||||
|
||||
constexpr nullopt_t nullopt{ 0 };
|
||||
|
||||
template <typename T>
|
||||
class optional
|
||||
{
|
||||
public:
|
||||
using value_type = T;
|
||||
|
||||
optional() noexcept = default;
|
||||
optional(nullopt_t) noexcept;
|
||||
optional(const optional& other);
|
||||
optional(optional&& other) noexcept;
|
||||
|
||||
template <typename... Args>
|
||||
explicit optional(cm::in_place_t, Args&&... args);
|
||||
|
||||
template <
|
||||
typename U = T,
|
||||
typename = typename std::enable_if<
|
||||
std::is_constructible<T, U&&>::value &&
|
||||
!std::is_same<typename std::decay<U>::type, cm::in_place_t>::value &&
|
||||
!std::is_same<typename std::decay<U>::type,
|
||||
cm::optional<T>>::value>::type>
|
||||
optional(U&& v);
|
||||
|
||||
~optional();
|
||||
|
||||
optional& operator=(nullopt_t) noexcept;
|
||||
optional& operator=(const optional& other);
|
||||
optional& operator=(optional&& other) noexcept;
|
||||
|
||||
template <
|
||||
typename U = T,
|
||||
typename = typename std::enable_if<
|
||||
!std::is_same<typename std::decay<U>::type, cm::optional<T>>::value &&
|
||||
std::is_constructible<T, U>::value && std::is_assignable<T&, U>::value &&
|
||||
(!std::is_scalar<T>::value ||
|
||||
!std::is_same<typename std::decay<U>::type, T>::value)>::type>
|
||||
optional& operator=(U&& v);
|
||||
|
||||
const T* operator->() const;
|
||||
T* operator->();
|
||||
const T& operator*() const&;
|
||||
T& operator*() &;
|
||||
const T&& operator*() const&&;
|
||||
T&& operator*() &&;
|
||||
|
||||
explicit operator bool() const noexcept;
|
||||
bool has_value() const noexcept;
|
||||
|
||||
T& value() &;
|
||||
const T& value() const&;
|
||||
|
||||
T&& value() &&;
|
||||
const T&& value() const&&;
|
||||
|
||||
template <typename U>
|
||||
T value_or(U&& default_value) const&;
|
||||
|
||||
template <typename U>
|
||||
T value_or(U&& default_value) &&;
|
||||
|
||||
void swap(optional& other) noexcept;
|
||||
void reset() noexcept;
|
||||
|
||||
template <typename... Args>
|
||||
T& emplace(Args&&... args);
|
||||
|
||||
private:
|
||||
bool _has_value = false;
|
||||
std::allocator<T> _allocator;
|
||||
union _mem_union
|
||||
{
|
||||
T value;
|
||||
|
||||
// Explicit constructor and destructor is required to make this work
|
||||
_mem_union() noexcept {}
|
||||
~_mem_union() noexcept {}
|
||||
} _mem;
|
||||
};
|
||||
|
||||
template <typename T>
|
||||
optional<typename std::decay<T>::type> make_optional(T&& value)
|
||||
{
|
||||
return optional<typename std::decay<T>::type>(std::forward<T>(value));
|
||||
}
|
||||
|
||||
template <typename T, class... Args>
|
||||
optional<T> make_optional(Args&&... args)
|
||||
{
|
||||
return optional<T>(in_place, std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
optional<T>::optional(nullopt_t) noexcept
|
||||
{
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
optional<T>::optional(const optional& other)
|
||||
{
|
||||
*this = other;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
optional<T>::optional(optional&& other) noexcept
|
||||
{
|
||||
*this = std::move(other);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename... Args>
|
||||
optional<T>::optional(cm::in_place_t, Args&&... args)
|
||||
{
|
||||
this->emplace(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename U, typename>
|
||||
optional<T>::optional(U&& v)
|
||||
{
|
||||
this->emplace(std::forward<U>(v));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
optional<T>::~optional()
|
||||
{
|
||||
this->reset();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
optional<T>& optional<T>::operator=(nullopt_t) noexcept
|
||||
{
|
||||
this->reset();
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
optional<T>& optional<T>::operator=(const optional& other)
|
||||
{
|
||||
if (other.has_value()) {
|
||||
if (this->has_value()) {
|
||||
this->value() = *other;
|
||||
} else {
|
||||
this->emplace(*other);
|
||||
}
|
||||
} else {
|
||||
this->reset();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
optional<T>& optional<T>::operator=(optional&& other) noexcept
|
||||
{
|
||||
if (other.has_value()) {
|
||||
if (this->has_value()) {
|
||||
this->value() = std::move(*other);
|
||||
} else {
|
||||
this->emplace(std::move(*other));
|
||||
}
|
||||
} else {
|
||||
this->reset();
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename U, typename>
|
||||
optional<T>& optional<T>::operator=(U&& v)
|
||||
{
|
||||
if (this->has_value()) {
|
||||
this->value() = v;
|
||||
} else {
|
||||
this->emplace(std::forward<U>(v));
|
||||
}
|
||||
return *this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T* optional<T>::operator->() const
|
||||
{
|
||||
return &**this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T* optional<T>::operator->()
|
||||
{
|
||||
return &**this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T& optional<T>::operator*() const&
|
||||
{
|
||||
return this->_mem.value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T& optional<T>::operator*() &
|
||||
{
|
||||
return this->_mem.value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T&& optional<T>::operator*() const&&
|
||||
{
|
||||
return std::move(**this);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T&& optional<T>::operator*() &&
|
||||
{
|
||||
return std::move(**this);
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
bool optional<T>::has_value() const noexcept
|
||||
{
|
||||
return this->_has_value;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
optional<T>::operator bool() const noexcept
|
||||
{
|
||||
return this->has_value();
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
T& optional<T>::value() &
|
||||
{
|
||||
if (!this->has_value()) {
|
||||
throw cm::bad_optional_access{};
|
||||
}
|
||||
return **this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
const T& optional<T>::value() const&
|
||||
{
|
||||
if (!this->has_value()) {
|
||||
throw cm::bad_optional_access{};
|
||||
}
|
||||
return **this;
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename U>
|
||||
T optional<T>::value_or(U&& default_value) const&
|
||||
{
|
||||
return bool(*this) ? **this : static_cast<T>(std::forward<U>(default_value));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename U>
|
||||
T optional<T>::value_or(U&& default_value) &&
|
||||
{
|
||||
return bool(*this) ? std::move(**this)
|
||||
: static_cast<T>(std::forward<U>(default_value));
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void optional<T>::swap(optional& other) noexcept
|
||||
{
|
||||
if (this->has_value()) {
|
||||
if (other.has_value()) {
|
||||
using std::swap;
|
||||
swap(**this, *other);
|
||||
} else {
|
||||
other.emplace(std::move(**this));
|
||||
this->reset();
|
||||
}
|
||||
} else if (other.has_value()) {
|
||||
this->emplace(std::move(*other));
|
||||
other.reset();
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
void optional<T>::reset() noexcept
|
||||
{
|
||||
if (this->has_value()) {
|
||||
this->_has_value = false;
|
||||
std::allocator_traits<std::allocator<T>>::destroy(this->_allocator,
|
||||
&**this);
|
||||
}
|
||||
}
|
||||
|
||||
template <typename T>
|
||||
template <typename... Args>
|
||||
T& optional<T>::emplace(Args&&... args)
|
||||
{
|
||||
this->reset();
|
||||
std::allocator_traits<std::allocator<T>>::construct(
|
||||
this->_allocator, &**this, std::forward<Args>(args)...);
|
||||
this->_has_value = true;
|
||||
return this->value();
|
||||
}
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
35
Source/cm_utility.hxx
Normal file
35
Source/cm_utility.hxx
Normal file
@@ -0,0 +1,35 @@
|
||||
/* Distributed under the OSI-approved BSD 3-Clause License. See accompanying
|
||||
file Copyright.txt or https://cmake.org/licensing for details. */
|
||||
#ifndef cm_utility_hxx
|
||||
#define cm_utility_hxx
|
||||
|
||||
#include "cmConfigure.h" // IWYU pragma: keep
|
||||
|
||||
#if __cplusplus >= 201703L || (defined(_MSVC_LANG) && _MSVC_LANG >= 201703L)
|
||||
# define CMake_HAVE_CXX_IN_PLACE
|
||||
#endif
|
||||
|
||||
#if defined(CMake_HAVE_CXX_IN_PLACE)
|
||||
# include <utility>
|
||||
#endif
|
||||
|
||||
namespace cm {
|
||||
|
||||
#if defined(CMake_HAVE_CXX_IN_PLACE)
|
||||
|
||||
using std::in_place_t;
|
||||
using std::in_place;
|
||||
|
||||
#else
|
||||
|
||||
struct in_place_t
|
||||
{
|
||||
explicit in_place_t() = default;
|
||||
};
|
||||
|
||||
constexpr in_place_t in_place{};
|
||||
|
||||
#endif
|
||||
}
|
||||
|
||||
#endif
|
||||
@@ -9,6 +9,7 @@ set(CMakeLib_TESTS
|
||||
testGeneratedFileStream.cxx
|
||||
testRST.cxx
|
||||
testRange.cxx
|
||||
testOptional.cxx
|
||||
testString.cxx
|
||||
testStringAlgorithms.cxx
|
||||
testSystemTools.cxx
|
||||
|
||||
690
Tests/CMakeLib/testOptional.cxx
Normal file
690
Tests/CMakeLib/testOptional.cxx
Normal file
@@ -0,0 +1,690 @@
|
||||
#include "cm_optional.hxx"
|
||||
#include "cm_utility.hxx"
|
||||
|
||||
#include <iostream>
|
||||
#include <type_traits>
|
||||
#include <utility>
|
||||
#include <vector>
|
||||
|
||||
class EventLogger;
|
||||
|
||||
class Event
|
||||
{
|
||||
public:
|
||||
enum EventType
|
||||
{
|
||||
DEFAULT_CONSTRUCT,
|
||||
COPY_CONSTRUCT,
|
||||
MOVE_CONSTRUCT,
|
||||
VALUE_CONSTRUCT,
|
||||
|
||||
DESTRUCT,
|
||||
|
||||
COPY_ASSIGN,
|
||||
MOVE_ASSIGN,
|
||||
VALUE_ASSIGN,
|
||||
|
||||
REFERENCE,
|
||||
CONST_REFERENCE,
|
||||
RVALUE_REFERENCE,
|
||||
CONST_RVALUE_REFERENCE,
|
||||
|
||||
SWAP,
|
||||
};
|
||||
|
||||
EventType Type;
|
||||
const EventLogger* Logger1;
|
||||
const EventLogger* Logger2;
|
||||
int Value;
|
||||
|
||||
bool operator==(const Event& other) const;
|
||||
bool operator!=(const Event& other) const;
|
||||
};
|
||||
|
||||
bool Event::operator==(const Event& other) const
|
||||
{
|
||||
return this->Type == other.Type && this->Logger1 == other.Logger1 &&
|
||||
this->Logger2 == other.Logger2 && this->Value == other.Value;
|
||||
}
|
||||
|
||||
bool Event::operator!=(const Event& other) const
|
||||
{
|
||||
return !(*this == other);
|
||||
}
|
||||
|
||||
static std::vector<Event> events;
|
||||
|
||||
class EventLogger
|
||||
{
|
||||
public:
|
||||
EventLogger();
|
||||
EventLogger(const EventLogger& other);
|
||||
EventLogger(EventLogger&& other);
|
||||
EventLogger(int value);
|
||||
|
||||
~EventLogger();
|
||||
|
||||
EventLogger& operator=(const EventLogger& other);
|
||||
EventLogger& operator=(EventLogger&& other);
|
||||
EventLogger& operator=(int value);
|
||||
|
||||
void Reference() &;
|
||||
void Reference() const&;
|
||||
void Reference() &&;
|
||||
void Reference() const&&;
|
||||
|
||||
int Value = 0;
|
||||
};
|
||||
|
||||
// Certain builds of GCC generate false -Wmaybe-uninitialized warnings when
|
||||
// doing a release build with the system version of std::optional. These
|
||||
// warnings do not manifest when using our own cm::optional implementation.
|
||||
// Silence these false warnings.
|
||||
#if defined(__GNUC__) && !defined(__clang__)
|
||||
# define BEGIN_IGNORE_UNINITIALIZED \
|
||||
_Pragma("GCC diagnostic push") \
|
||||
_Pragma("GCC diagnostic ignored \"-Wmaybe-uninitialized\"")
|
||||
# define END_IGNORE_UNINITIALIZED _Pragma("GCC diagnostic pop")
|
||||
#else
|
||||
# define BEGIN_IGNORE_UNINITIALIZED
|
||||
# define END_IGNORE_UNINITIALIZED
|
||||
#endif
|
||||
|
||||
void swap(EventLogger& e1, EventLogger& e2)
|
||||
{
|
||||
BEGIN_IGNORE_UNINITIALIZED
|
||||
events.push_back({ Event::SWAP, &e1, &e2, e2.Value });
|
||||
END_IGNORE_UNINITIALIZED
|
||||
auto tmp = e1.Value;
|
||||
e1.Value = e2.Value;
|
||||
e2.Value = tmp;
|
||||
}
|
||||
|
||||
EventLogger::EventLogger()
|
||||
: Value(0)
|
||||
{
|
||||
events.push_back({ Event::DEFAULT_CONSTRUCT, this, nullptr, 0 });
|
||||
}
|
||||
|
||||
EventLogger::EventLogger(const EventLogger& other)
|
||||
: Value(other.Value)
|
||||
{
|
||||
events.push_back({ Event::COPY_CONSTRUCT, this, &other, other.Value });
|
||||
}
|
||||
|
||||
BEGIN_IGNORE_UNINITIALIZED
|
||||
EventLogger::EventLogger(EventLogger&& other)
|
||||
: Value(other.Value)
|
||||
{
|
||||
events.push_back({ Event::MOVE_CONSTRUCT, this, &other, other.Value });
|
||||
}
|
||||
END_IGNORE_UNINITIALIZED
|
||||
|
||||
EventLogger::EventLogger(int value)
|
||||
: Value(value)
|
||||
{
|
||||
events.push_back({ Event::VALUE_CONSTRUCT, this, nullptr, value });
|
||||
}
|
||||
|
||||
EventLogger::~EventLogger()
|
||||
{
|
||||
BEGIN_IGNORE_UNINITIALIZED
|
||||
events.push_back({ Event::DESTRUCT, this, nullptr, this->Value });
|
||||
END_IGNORE_UNINITIALIZED
|
||||
}
|
||||
|
||||
EventLogger& EventLogger::operator=(const EventLogger& other)
|
||||
{
|
||||
events.push_back({ Event::COPY_ASSIGN, this, &other, other.Value });
|
||||
this->Value = other.Value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
EventLogger& EventLogger::operator=(EventLogger&& other)
|
||||
{
|
||||
events.push_back({ Event::MOVE_ASSIGN, this, &other, other.Value });
|
||||
this->Value = other.Value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
EventLogger& EventLogger::operator=(int value)
|
||||
{
|
||||
events.push_back({ Event::VALUE_ASSIGN, this, nullptr, value });
|
||||
this->Value = value;
|
||||
return *this;
|
||||
}
|
||||
|
||||
void EventLogger::Reference() &
|
||||
{
|
||||
events.push_back({ Event::REFERENCE, this, nullptr, this->Value });
|
||||
}
|
||||
|
||||
void EventLogger::Reference() const&
|
||||
{
|
||||
events.push_back({ Event::CONST_REFERENCE, this, nullptr, this->Value });
|
||||
}
|
||||
|
||||
void EventLogger::Reference() &&
|
||||
{
|
||||
events.push_back({ Event::RVALUE_REFERENCE, this, nullptr, this->Value });
|
||||
}
|
||||
|
||||
void EventLogger::Reference() const&&
|
||||
{
|
||||
events.push_back(
|
||||
{ Event::CONST_RVALUE_REFERENCE, this, nullptr, this->Value });
|
||||
}
|
||||
|
||||
static bool testDefaultConstruct(std::vector<Event>& expected)
|
||||
{
|
||||
const cm::optional<EventLogger> o{};
|
||||
|
||||
expected = {};
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool testNulloptConstruct(std::vector<Event>& expected)
|
||||
{
|
||||
const cm::optional<EventLogger> o{ cm::nullopt };
|
||||
|
||||
expected = {};
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool testValueConstruct(std::vector<Event>& expected)
|
||||
{
|
||||
const cm::optional<EventLogger> o{ 4 };
|
||||
|
||||
expected = {
|
||||
{ Event::VALUE_CONSTRUCT, &*o, nullptr, 4 },
|
||||
{ Event::DESTRUCT, &*o, nullptr, 4 },
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool testInPlaceConstruct(std::vector<Event>& expected)
|
||||
{
|
||||
const cm::optional<EventLogger> o1{ cm::in_place, 4 };
|
||||
const cm::optional<EventLogger> o2{ cm::in_place_t{}, 4 };
|
||||
|
||||
expected = {
|
||||
{ Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
|
||||
{ Event::VALUE_CONSTRUCT, &*o2, nullptr, 4 },
|
||||
{ Event::DESTRUCT, &*o2, nullptr, 4 },
|
||||
{ Event::DESTRUCT, &*o1, nullptr, 4 },
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool testCopyConstruct(std::vector<Event>& expected)
|
||||
{
|
||||
const cm::optional<EventLogger> o1{ 4 };
|
||||
const cm::optional<EventLogger> o2{ o1 };
|
||||
const cm::optional<EventLogger> o3{};
|
||||
const cm::optional<EventLogger> o4{ o3 };
|
||||
|
||||
expected = {
|
||||
{ Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
|
||||
{ Event::COPY_CONSTRUCT, &*o2, &o1.value(), 4 },
|
||||
{ Event::DESTRUCT, &*o2, nullptr, 4 },
|
||||
{ Event::DESTRUCT, &*o1, nullptr, 4 },
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool testMoveConstruct(std::vector<Event>& expected)
|
||||
{
|
||||
cm::optional<EventLogger> o1{ 4 };
|
||||
const cm::optional<EventLogger> o2{ std::move(o1) };
|
||||
cm::optional<EventLogger> o3{};
|
||||
const cm::optional<EventLogger> o4{ std::move(o3) };
|
||||
|
||||
expected = {
|
||||
{ Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
|
||||
{ Event::MOVE_CONSTRUCT, &*o2, &o1.value(), 4 },
|
||||
{ Event::DESTRUCT, &*o2, nullptr, 4 },
|
||||
{ Event::DESTRUCT, &*o1, nullptr, 4 },
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool testNulloptAssign(std::vector<Event>& expected)
|
||||
{
|
||||
cm::optional<EventLogger> o1{ 4 };
|
||||
o1 = cm::nullopt;
|
||||
cm::optional<EventLogger> o2{};
|
||||
o2 = cm::nullopt;
|
||||
|
||||
expected = {
|
||||
{ Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
|
||||
{ Event::DESTRUCT, &*o1, nullptr, 4 },
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool testCopyAssign(std::vector<Event>& expected)
|
||||
{
|
||||
cm::optional<EventLogger> o1{};
|
||||
const cm::optional<EventLogger> o2{ 4 };
|
||||
o1 = o2;
|
||||
const cm::optional<EventLogger> o3{ 5 };
|
||||
o1 = o3;
|
||||
const cm::optional<EventLogger> o4{};
|
||||
o1 = o4;
|
||||
o1 = o4; // Intentionally duplicated to test assigning an empty optional to
|
||||
// an empty optional
|
||||
|
||||
expected = {
|
||||
{ Event::VALUE_CONSTRUCT, &*o2, nullptr, 4 },
|
||||
{ Event::COPY_CONSTRUCT, &*o1, &*o2, 4 },
|
||||
{ Event::VALUE_CONSTRUCT, &*o3, nullptr, 5 },
|
||||
{ Event::COPY_ASSIGN, &*o1, &*o3, 5 },
|
||||
{ Event::DESTRUCT, &*o1, nullptr, 5 },
|
||||
{ Event::DESTRUCT, &o3.value(), nullptr, 5 },
|
||||
{ Event::DESTRUCT, &o2.value(), nullptr, 4 },
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool testMoveAssign(std::vector<Event>& expected)
|
||||
{
|
||||
cm::optional<EventLogger> o1{};
|
||||
cm::optional<EventLogger> o2{ 4 };
|
||||
o1 = std::move(o2);
|
||||
cm::optional<EventLogger> o3{ 5 };
|
||||
o1 = std::move(o3);
|
||||
cm::optional<EventLogger> o4{};
|
||||
o1 = std::move(o4);
|
||||
|
||||
expected = {
|
||||
{ Event::VALUE_CONSTRUCT, &*o2, nullptr, 4 },
|
||||
{ Event::MOVE_CONSTRUCT, &*o1, &*o2, 4 },
|
||||
{ Event::VALUE_CONSTRUCT, &*o3, nullptr, 5 },
|
||||
{ Event::MOVE_ASSIGN, &*o1, &*o3, 5 },
|
||||
{ Event::DESTRUCT, &*o1, nullptr, 5 },
|
||||
{ Event::DESTRUCT, &*o3, nullptr, 5 },
|
||||
{ Event::DESTRUCT, &*o2, nullptr, 4 },
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool testPointer(std::vector<Event>& expected)
|
||||
{
|
||||
cm::optional<EventLogger> o1{ 4 };
|
||||
const cm::optional<EventLogger> o2{ 5 };
|
||||
|
||||
o1->Reference();
|
||||
o2->Reference();
|
||||
|
||||
expected = {
|
||||
{ Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
|
||||
{ Event::VALUE_CONSTRUCT, &*o2, nullptr, 5 },
|
||||
{ Event::REFERENCE, &*o1, nullptr, 4 },
|
||||
{ Event::CONST_REFERENCE, &*o2, nullptr, 5 },
|
||||
{ Event::DESTRUCT, &*o2, nullptr, 5 },
|
||||
{ Event::DESTRUCT, &*o1, nullptr, 4 },
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
#if !__GNUC__ || __GNUC__ > 4
|
||||
# define ALLOW_CONST_RVALUE
|
||||
#endif
|
||||
|
||||
static bool testDereference(std::vector<Event>& expected)
|
||||
{
|
||||
cm::optional<EventLogger> o1{ 4 };
|
||||
const cm::optional<EventLogger> o2{ 5 };
|
||||
|
||||
(*o1).Reference();
|
||||
(*o2).Reference();
|
||||
(*std::move(o1)).Reference();
|
||||
#ifdef ALLOW_CONST_RVALUE
|
||||
(*std::move(o2)).Reference(); // Broken in GCC 4.9.0. Sigh...
|
||||
#endif
|
||||
|
||||
expected = {
|
||||
{ Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
|
||||
{ Event::VALUE_CONSTRUCT, &*o2, nullptr, 5 },
|
||||
{ Event::REFERENCE, &*o1, nullptr, 4 },
|
||||
{ Event::CONST_REFERENCE, &*o2, nullptr, 5 },
|
||||
{ Event::RVALUE_REFERENCE, &*o1, nullptr, 4 },
|
||||
#ifdef ALLOW_CONST_RVALUE
|
||||
{ Event::CONST_RVALUE_REFERENCE, &*o2, nullptr, 5 },
|
||||
#endif
|
||||
{ Event::DESTRUCT, &*o2, nullptr, 5 },
|
||||
{ Event::DESTRUCT, &*o1, nullptr, 4 },
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool testHasValue(std::vector<Event>& expected)
|
||||
{
|
||||
bool retval = true;
|
||||
|
||||
const cm::optional<EventLogger> o1{ 4 };
|
||||
const cm::optional<EventLogger> o2{};
|
||||
|
||||
if (!o1.has_value()) {
|
||||
std::cout << "o1 should have a value" << std::endl;
|
||||
retval = false;
|
||||
}
|
||||
|
||||
if (!o1) {
|
||||
std::cout << "(bool)o1 should be true" << std::endl;
|
||||
retval = false;
|
||||
}
|
||||
|
||||
if (o2.has_value()) {
|
||||
std::cout << "o2 should not have a value" << std::endl;
|
||||
retval = false;
|
||||
}
|
||||
|
||||
if (o2) {
|
||||
std::cout << "(bool)o2 should be false" << std::endl;
|
||||
retval = false;
|
||||
}
|
||||
|
||||
expected = {
|
||||
{ Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
|
||||
{ Event::DESTRUCT, &*o1, nullptr, 4 },
|
||||
};
|
||||
return retval;
|
||||
}
|
||||
|
||||
static bool testValue(std::vector<Event>& expected)
|
||||
{
|
||||
bool retval = true;
|
||||
|
||||
cm::optional<EventLogger> o1{ 4 };
|
||||
const cm::optional<EventLogger> o2{ 5 };
|
||||
cm::optional<EventLogger> o3{};
|
||||
const cm::optional<EventLogger> o4{};
|
||||
|
||||
o1.value().Reference();
|
||||
o2.value().Reference();
|
||||
|
||||
bool thrown = false;
|
||||
try {
|
||||
(void)o3.value();
|
||||
} catch (cm::bad_optional_access&) {
|
||||
thrown = true;
|
||||
}
|
||||
if (!thrown) {
|
||||
std::cout << "o3.value() did not throw" << std::endl;
|
||||
retval = false;
|
||||
}
|
||||
|
||||
thrown = false;
|
||||
try {
|
||||
(void)o4.value();
|
||||
} catch (cm::bad_optional_access&) {
|
||||
thrown = true;
|
||||
}
|
||||
if (!thrown) {
|
||||
std::cout << "o4.value() did not throw" << std::endl;
|
||||
retval = false;
|
||||
}
|
||||
|
||||
expected = {
|
||||
{ Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
|
||||
{ Event::VALUE_CONSTRUCT, &*o2, nullptr, 5 },
|
||||
{ Event::REFERENCE, &*o1, nullptr, 4 },
|
||||
{ Event::CONST_REFERENCE, &*o2, nullptr, 5 },
|
||||
{ Event::DESTRUCT, &*o2, nullptr, 5 },
|
||||
{ Event::DESTRUCT, &*o1, nullptr, 4 },
|
||||
};
|
||||
return retval;
|
||||
}
|
||||
|
||||
static bool testValueOr()
|
||||
{
|
||||
bool retval = true;
|
||||
|
||||
const cm::optional<EventLogger> o1{ 4 };
|
||||
cm::optional<EventLogger> o2{ 5 };
|
||||
const cm::optional<EventLogger> o3{};
|
||||
cm::optional<EventLogger> o4{};
|
||||
|
||||
EventLogger e1{ 6 };
|
||||
EventLogger e2{ 7 };
|
||||
EventLogger e3{ 8 };
|
||||
EventLogger e4{ 9 };
|
||||
|
||||
EventLogger r1 = o1.value_or(e1);
|
||||
if (r1.Value != 4) {
|
||||
std::cout << "r1.Value should be 4" << std::endl;
|
||||
retval = false;
|
||||
}
|
||||
EventLogger r2 = std::move(o2).value_or(e2);
|
||||
if (r2.Value != 5) {
|
||||
std::cout << "r2.Value should be 5" << std::endl;
|
||||
retval = false;
|
||||
}
|
||||
EventLogger r3 = o3.value_or(e3);
|
||||
if (r3.Value != 8) {
|
||||
std::cout << "r3.Value should be 8" << std::endl;
|
||||
retval = false;
|
||||
}
|
||||
EventLogger r4 = std::move(o4).value_or(e4);
|
||||
if (r4.Value != 9) {
|
||||
std::cout << "r4.Value should be 9" << std::endl;
|
||||
retval = false;
|
||||
}
|
||||
|
||||
return retval;
|
||||
}
|
||||
|
||||
static bool testSwap(std::vector<Event>& expected)
|
||||
{
|
||||
bool retval = true;
|
||||
|
||||
cm::optional<EventLogger> o1{ 4 };
|
||||
cm::optional<EventLogger> o2{};
|
||||
|
||||
o1.swap(o2);
|
||||
|
||||
if (o1.has_value()) {
|
||||
std::cout << "o1 should not have value" << std::endl;
|
||||
retval = false;
|
||||
}
|
||||
if (!o2.has_value()) {
|
||||
std::cout << "o2 should have value" << std::endl;
|
||||
retval = false;
|
||||
}
|
||||
if (o2.value().Value != 4) {
|
||||
std::cout << "value of o2 should be 4" << std::endl;
|
||||
retval = false;
|
||||
}
|
||||
|
||||
o1.swap(o2);
|
||||
|
||||
if (!o1.has_value()) {
|
||||
std::cout << "o1 should have value" << std::endl;
|
||||
retval = false;
|
||||
}
|
||||
if (o1.value().Value != 4) {
|
||||
std::cout << "value of o1 should be 4" << std::endl;
|
||||
retval = false;
|
||||
}
|
||||
if (o2.has_value()) {
|
||||
std::cout << "o2 should not have value" << std::endl;
|
||||
retval = false;
|
||||
}
|
||||
|
||||
o2.emplace(5);
|
||||
o1.swap(o2);
|
||||
|
||||
if (!o1.has_value()) {
|
||||
std::cout << "o1 should have value" << std::endl;
|
||||
retval = false;
|
||||
}
|
||||
if (o1.value().Value != 5) {
|
||||
std::cout << "value of o1 should be 5" << std::endl;
|
||||
retval = false;
|
||||
}
|
||||
if (!o2.has_value()) {
|
||||
std::cout << "o2 should not have value" << std::endl;
|
||||
retval = false;
|
||||
}
|
||||
if (o2.value().Value != 4) {
|
||||
std::cout << "value of o2 should be 4" << std::endl;
|
||||
retval = false;
|
||||
}
|
||||
|
||||
o1.reset();
|
||||
o2.reset();
|
||||
o1.swap(o2);
|
||||
|
||||
if (o1.has_value()) {
|
||||
std::cout << "o1 should not have value" << std::endl;
|
||||
retval = false;
|
||||
}
|
||||
if (o2.has_value()) {
|
||||
std::cout << "o2 should not have value" << std::endl;
|
||||
retval = false;
|
||||
}
|
||||
|
||||
expected = {
|
||||
{ Event::VALUE_CONSTRUCT, &*o1, nullptr, 4 },
|
||||
{ Event::MOVE_CONSTRUCT, &*o2, &*o1, 4 },
|
||||
{ Event::DESTRUCT, &*o1, nullptr, 4 },
|
||||
{ Event::MOVE_CONSTRUCT, &*o1, &*o2, 4 },
|
||||
{ Event::DESTRUCT, &*o2, nullptr, 4 },
|
||||
{ Event::VALUE_CONSTRUCT, &*o2, nullptr, 5 },
|
||||
{ Event::SWAP, &*o1, &*o2, 5 },
|
||||
{ Event::DESTRUCT, &*o1, nullptr, 5 },
|
||||
{ Event::DESTRUCT, &*o2, nullptr, 4 },
|
||||
};
|
||||
return retval;
|
||||
}
|
||||
|
||||
static bool testReset(std::vector<Event>& expected)
|
||||
{
|
||||
bool retval = true;
|
||||
|
||||
cm::optional<EventLogger> o{ 4 };
|
||||
|
||||
o.reset();
|
||||
|
||||
if (o.has_value()) {
|
||||
std::cout << "o should not have value" << std::endl;
|
||||
retval = false;
|
||||
}
|
||||
|
||||
o.reset();
|
||||
|
||||
expected = {
|
||||
{ Event::VALUE_CONSTRUCT, &*o, nullptr, 4 },
|
||||
{ Event::DESTRUCT, &*o, nullptr, 4 },
|
||||
};
|
||||
return retval;
|
||||
}
|
||||
|
||||
static bool testEmplace(std::vector<Event>& expected)
|
||||
{
|
||||
cm::optional<EventLogger> o{ 4 };
|
||||
|
||||
o.emplace(5);
|
||||
o.reset();
|
||||
o.emplace();
|
||||
|
||||
expected = {
|
||||
{ Event::VALUE_CONSTRUCT, &*o, nullptr, 4 },
|
||||
{ Event::DESTRUCT, &*o, nullptr, 4 },
|
||||
{ Event::VALUE_CONSTRUCT, &*o, nullptr, 5 },
|
||||
{ Event::DESTRUCT, &*o, nullptr, 5 },
|
||||
{ Event::DEFAULT_CONSTRUCT, &*o, nullptr, 0 },
|
||||
{ Event::DESTRUCT, &*o, nullptr, 0 },
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool testMakeOptional(std::vector<Event>& expected)
|
||||
{
|
||||
EventLogger e{ 4 };
|
||||
cm::optional<EventLogger> o1 = cm::make_optional<EventLogger>(e);
|
||||
cm::optional<EventLogger> o2 = cm::make_optional<EventLogger>(5);
|
||||
|
||||
expected = {
|
||||
{ Event::VALUE_CONSTRUCT, &e, nullptr, 4 },
|
||||
{ Event::COPY_CONSTRUCT, &*o1, &e, 4 },
|
||||
{ Event::VALUE_CONSTRUCT, &*o2, nullptr, 5 },
|
||||
{ Event::DESTRUCT, &*o2, nullptr, 5 },
|
||||
{ Event::DESTRUCT, &*o1, nullptr, 4 },
|
||||
{ Event::DESTRUCT, &e, nullptr, 4 },
|
||||
};
|
||||
return true;
|
||||
}
|
||||
|
||||
static bool testMemoryRange(std::vector<Event>& expected)
|
||||
{
|
||||
bool retval = true;
|
||||
|
||||
cm::optional<EventLogger> o{ 4 };
|
||||
|
||||
auto* ostart = &o;
|
||||
auto* oend = ostart + 1;
|
||||
auto* estart = &o.value();
|
||||
auto* eend = estart + 1;
|
||||
|
||||
if (static_cast<void*>(estart) < static_cast<void*>(ostart) ||
|
||||
static_cast<void*>(eend) > static_cast<void*>(oend)) {
|
||||
std::cout << "value is not within memory range of optional" << std::endl;
|
||||
retval = false;
|
||||
}
|
||||
|
||||
expected = {
|
||||
{ Event::VALUE_CONSTRUCT, &*o, nullptr, 4 },
|
||||
{ Event::DESTRUCT, &*o, nullptr, 4 },
|
||||
};
|
||||
return retval;
|
||||
}
|
||||
|
||||
int testOptional(int /*unused*/, char* /*unused*/ [])
|
||||
{
|
||||
int retval = 0;
|
||||
|
||||
#define DO_EVENT_TEST(name) \
|
||||
do { \
|
||||
events.clear(); \
|
||||
std::vector<Event> expected; \
|
||||
if (!name(expected)) { \
|
||||
std::cout << "in " #name << std::endl; \
|
||||
retval = 1; \
|
||||
} else if (expected != events) { \
|
||||
std::cout << #name " did not produce expected events" << std::endl; \
|
||||
retval = 1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
#define DO_TEST(name) \
|
||||
do { \
|
||||
if (!name()) { \
|
||||
std::cout << "in " #name << std::endl; \
|
||||
retval = 1; \
|
||||
} \
|
||||
} while (0)
|
||||
|
||||
DO_EVENT_TEST(testDefaultConstruct);
|
||||
DO_EVENT_TEST(testNulloptConstruct);
|
||||
DO_EVENT_TEST(testValueConstruct);
|
||||
DO_EVENT_TEST(testInPlaceConstruct);
|
||||
DO_EVENT_TEST(testCopyConstruct);
|
||||
DO_EVENT_TEST(testMoveConstruct);
|
||||
DO_EVENT_TEST(testNulloptAssign);
|
||||
DO_EVENT_TEST(testCopyAssign);
|
||||
DO_EVENT_TEST(testMoveAssign);
|
||||
DO_EVENT_TEST(testPointer);
|
||||
DO_EVENT_TEST(testDereference);
|
||||
DO_EVENT_TEST(testHasValue);
|
||||
DO_EVENT_TEST(testValue);
|
||||
DO_TEST(testValueOr);
|
||||
DO_EVENT_TEST(testSwap);
|
||||
DO_EVENT_TEST(testReset);
|
||||
DO_EVENT_TEST(testEmplace);
|
||||
DO_EVENT_TEST(testMakeOptional);
|
||||
DO_EVENT_TEST(testMemoryRange);
|
||||
|
||||
return retval;
|
||||
}
|
||||
Reference in New Issue
Block a user