Merge pull request #100 from lioncash/noexcept

rstl: Make constructors/assignment operators of reserved_vector conditionally noexcept
This commit is contained in:
Phillip Stephens 2019-10-03 23:08:23 -07:00 committed by GitHub
commit 1044b0f1ee
No known key found for this signature in database
GPG Key ID: 4AEE18F83AFDEB23
1 changed files with 126 additions and 55 deletions

View File

@ -1,11 +1,14 @@
#pragma once #pragma once
#include <vector>
#include <algorithm> #include <algorithm>
#include <type_traits>
#include <cstdlib> #include <cstdlib>
#include <optional> #include <optional>
#include "logvisor/logvisor.hpp" #include <type_traits>
#include <vector>
#ifndef NDEBUG
#include <logvisor/logvisor.hpp>
#endif
namespace rstl { namespace rstl {
@ -226,6 +229,26 @@ protected:
*/ */
template <class T, size_t N> template <class T, size_t N>
class reserved_vector : public _reserved_vector_base<T> { class reserved_vector : public _reserved_vector_base<T> {
using base = _reserved_vector_base<T>;
public:
using value_type = T;
using pointer = value_type*;
using const_pointer = const value_type*;
using reference = value_type&;
using const_reference = const value_type&;
using difference_type = std::ptrdiff_t;
using size_type = std::size_t;
using iterator = typename base::iterator;
using const_iterator = typename base::const_iterator;
using reverse_iterator = typename base::reverse_iterator;
using const_reverse_iterator = typename base::const_reverse_iterator;
private:
union alignas(T) storage_t { union alignas(T) storage_t {
struct { struct {
} _dummy; } _dummy;
@ -239,102 +262,124 @@ class reserved_vector : public _reserved_vector_base<T> {
const T& _value(std::ptrdiff_t idx) const { return x4_data[idx]._value; } const T& _value(std::ptrdiff_t idx) const { return x4_data[idx]._value; }
template <typename Tp> template <typename Tp>
static void static void
destroy(Tp& t, std::enable_if_t<std::is_destructible<Tp>::value && !std::is_trivially_destructible<Tp>::value>* = 0) { destroy(Tp& t, std::enable_if_t<std::is_destructible_v<Tp> && !std::is_trivially_destructible_v<Tp>>* = nullptr) {
t.Tp::~Tp(); t.Tp::~Tp();
} }
template <typename Tp> template <typename Tp>
static void static void
destroy(Tp& t, std::enable_if_t<!std::is_destructible<Tp>::value || std::is_trivially_destructible<Tp>::value>* = 0) { destroy(Tp& t, std::enable_if_t<!std::is_destructible_v<Tp> || std::is_trivially_destructible_v<Tp>>* = nullptr) {
} }
public: public:
using base = _reserved_vector_base<T>; reserved_vector() noexcept(std::is_nothrow_constructible_v<T>) : x0_size(0) {}
using iterator = typename base::iterator;
using const_iterator = typename base::const_iterator;
using reverse_iterator = typename base::reverse_iterator;
using const_reverse_iterator = typename base::const_reverse_iterator;
reserved_vector() : x0_size(0) {}
template <size_t LN> template <size_t LN>
reserved_vector(const T(&l)[LN]) reserved_vector(const T (&l)[LN]) noexcept(std::is_nothrow_copy_constructible_v<T>)
: x0_size(LN) { : x0_size(LN) {
static_assert(LN <= N, "initializer array too large for reserved_vector"); static_assert(LN <= N, "initializer array too large for reserved_vector");
for (size_t i = 0; i < LN; ++i) for (size_t i = 0; i < LN; ++i) {
::new (static_cast<void*>(std::addressof(_value(i)))) T(l[i]); ::new (static_cast<void*>(std::addressof(_value(i)))) T(l[i]);
}
} }
reserved_vector(const reserved_vector& other) : x0_size(other.x0_size) { reserved_vector(const reserved_vector& other) noexcept(std::is_nothrow_copy_constructible_v<T>)
for (size_t i = 0; i < x0_size; ++i) : x0_size(other.x0_size) {
for (size_t i = 0; i < x0_size; ++i) {
::new (static_cast<void*>(std::addressof(_value(i)))) T(other._value(i)); ::new (static_cast<void*>(std::addressof(_value(i)))) T(other._value(i));
}
} }
reserved_vector& operator=(const reserved_vector& other) { reserved_vector& operator=(const reserved_vector& other) noexcept(std::is_nothrow_copy_assignable_v<T>) {
size_t i = 0; size_t i = 0;
if (other.x0_size > x0_size) { if (other.x0_size > x0_size) {
for (; i < x0_size; ++i) for (; i < x0_size; ++i) {
_value(i) = other._value(i); _value(i) = other._value(i);
for (; i < other.x0_size; ++i) }
for (; i < other.x0_size; ++i) {
::new (static_cast<void*>(std::addressof(_value(i)))) T(other._value(i)); ::new (static_cast<void*>(std::addressof(_value(i)))) T(other._value(i));
}
} else if (other.x0_size < x0_size) { } else if (other.x0_size < x0_size) {
for (; i < other.x0_size; ++i) for (; i < other.x0_size; ++i) {
_value(i) = other._value(i); _value(i) = other._value(i);
if (std::is_destructible<T>::value && !std::is_trivially_destructible<T>::value) }
for (; i < x0_size; ++i)
if constexpr (std::is_destructible_v<T> && !std::is_trivially_destructible_v<T>) {
for (; i < x0_size; ++i) {
destroy(_value(i)); destroy(_value(i));
}
}
} else { } else {
for (; i < other.x0_size; ++i) for (; i < other.x0_size; ++i) {
_value(i) = other._value(i); _value(i) = other._value(i);
}
} }
x0_size = other.x0_size; x0_size = other.x0_size;
return *this; return *this;
} }
reserved_vector(reserved_vector&& other) : x0_size(other.x0_size) { reserved_vector(reserved_vector&& other) noexcept(std::is_nothrow_move_constructible_v<T>) : x0_size(other.x0_size) {
for (size_t i = 0; i < x0_size; ++i) for (size_t i = 0; i < x0_size; ++i) {
::new (static_cast<void*>(std::addressof(_value(i)))) T(std::forward<T>(other._value(i))); ::new (static_cast<void*>(std::addressof(_value(i)))) T(std::forward<T>(other._value(i)));
}
} }
reserved_vector& operator=(reserved_vector&& other) { reserved_vector& operator=(reserved_vector&& other) noexcept(
std::is_nothrow_move_assignable_v<T>&& std::is_nothrow_move_constructible_v<T>) {
size_t i = 0; size_t i = 0;
if (other.x0_size > x0_size) { if (other.x0_size > x0_size) {
for (; i < x0_size; ++i) for (; i < x0_size; ++i) {
_value(i) = std::forward<T>(other._value(i)); _value(i) = std::forward<T>(other._value(i));
for (; i < other.x0_size; ++i) }
for (; i < other.x0_size; ++i) {
::new (static_cast<void*>(std::addressof(_value(i)))) T(std::forward<T>(other._value(i))); ::new (static_cast<void*>(std::addressof(_value(i)))) T(std::forward<T>(other._value(i)));
}
} else if (other.x0_size < x0_size) { } else if (other.x0_size < x0_size) {
for (; i < other.x0_size; ++i) for (; i < other.x0_size; ++i) {
_value(i) = std::forward<T>(other._value(i)); _value(i) = std::forward<T>(other._value(i));
if (std::is_destructible<T>::value && !std::is_trivially_destructible<T>::value) }
for (; i < x0_size; ++i)
if constexpr (std::is_destructible_v<T> && !std::is_trivially_destructible_v<T>) {
for (; i < x0_size; ++i) {
destroy(_value(i)); destroy(_value(i));
}
}
} else { } else {
for (; i < other.x0_size; ++i) for (; i < other.x0_size; ++i) {
_value(i) = std::forward<T>(other._value(i)); _value(i) = std::forward<T>(other._value(i));
}
} }
x0_size = other.x0_size; x0_size = other.x0_size;
return *this; return *this;
} }
~reserved_vector() { ~reserved_vector() {
if (std::is_destructible<T>::value && !std::is_trivially_destructible<T>::value) if constexpr (std::is_destructible_v<T> && !std::is_trivially_destructible_v<T>) {
for (size_t i = 0; i < x0_size; ++i) for (size_t i = 0; i < x0_size; ++i) {
destroy(_value(i)); destroy(_value(i));
}
}
} }
void push_back(const T& d) { void push_back(const T& d) {
#ifndef NDEBUG #ifndef NDEBUG
if (x0_size == N) if (x0_size == N) {
Log.report(logvisor::Fatal, fmt("push_back() called on full rstl::reserved_vector.")); Log.report(logvisor::Fatal, fmt("push_back() called on full rstl::reserved_vector."));
}
#endif #endif
::new (static_cast<void*>(std::addressof(_value(x0_size)))) T(d); ::new (static_cast<void*>(std::addressof(_value(x0_size)))) T(d);
++x0_size; ++x0_size;
} }
void push_back(T&& d) { void push_back(T&& d) {
#ifndef NDEBUG #ifndef NDEBUG
if (x0_size == N) if (x0_size == N) {
Log.report(logvisor::Fatal, fmt("push_back() called on full rstl::reserved_vector.")); Log.report(logvisor::Fatal, fmt("push_back() called on full rstl::reserved_vector."));
}
#endif #endif
::new (static_cast<void*>(std::addressof(_value(x0_size)))) T(std::forward<T>(d)); ::new (static_cast<void*>(std::addressof(_value(x0_size)))) T(std::forward<T>(d));
++x0_size; ++x0_size;
} }
@ -342,34 +387,41 @@ public:
template <class... _Args> template <class... _Args>
void emplace_back(_Args&&... args) { void emplace_back(_Args&&... args) {
#ifndef NDEBUG #ifndef NDEBUG
if (x0_size == N) if (x0_size == N) {
Log.report(logvisor::Fatal, fmt("emplace_back() called on full rstl::reserved_vector.")); Log.report(logvisor::Fatal, fmt("emplace_back() called on full rstl::reserved_vector."));
}
#endif #endif
::new (static_cast<void*>(std::addressof(_value(x0_size)))) T(std::forward<_Args>(args)...); ::new (static_cast<void*>(std::addressof(_value(x0_size)))) T(std::forward<_Args>(args)...);
++x0_size; ++x0_size;
} }
void pop_back() { void pop_back() {
#ifndef NDEBUG #ifndef NDEBUG
if (x0_size == 0) if (x0_size == 0) {
Log.report(logvisor::Fatal, fmt("pop_back() called on empty rstl::reserved_vector.")); Log.report(logvisor::Fatal, fmt("pop_back() called on empty rstl::reserved_vector."));
}
#endif #endif
--x0_size; --x0_size;
destroy(_value(x0_size)); destroy(_value(x0_size));
} }
iterator insert(const_iterator pos, const T& value) { iterator insert(const_iterator pos, const T& value) {
#ifndef NDEBUG #ifndef NDEBUG
if (x0_size == N) if (x0_size == N) {
Log.report(logvisor::Fatal, fmt("insert() called on full rstl::reserved_vector.")); Log.report(logvisor::Fatal, fmt("insert() called on full rstl::reserved_vector."));
}
#endif #endif
auto target_it = base::_const_cast_iterator(pos); auto target_it = base::_const_cast_iterator(pos);
if (pos == cend()) { if (pos == cend()) {
::new (static_cast<void*>(std::addressof(_value(x0_size)))) T(value); ::new (static_cast<void*>(std::addressof(_value(x0_size)))) T(value);
} else { } else {
::new (static_cast<void*>(std::addressof(_value(x0_size)))) T(std::forward<T>(_value(x0_size - 1))); ::new (static_cast<void*>(std::addressof(_value(x0_size)))) T(std::forward<T>(_value(x0_size - 1)));
for (auto it = end() - 1; it != target_it; --it) for (auto it = end() - 1; it != target_it; --it) {
*it = std::forward<T>(*(it - 1)); *it = std::forward<T>(*(it - 1));
}
*target_it = value; *target_it = value;
} }
++x0_size; ++x0_size;
@ -386,8 +438,9 @@ public:
::new (static_cast<void*>(std::addressof(_value(x0_size)))) T(std::forward<T>(value)); ::new (static_cast<void*>(std::addressof(_value(x0_size)))) T(std::forward<T>(value));
} else { } else {
::new (static_cast<void*>(std::addressof(_value(x0_size)))) T(std::forward<T>(_value(x0_size - 1))); ::new (static_cast<void*>(std::addressof(_value(x0_size)))) T(std::forward<T>(_value(x0_size - 1)));
for (auto it = end() - 1; it != target_it; --it) for (auto it = end() - 1; it != target_it; --it) {
*it = std::forward<T>(*(it - 1)); *it = std::forward<T>(*(it - 1));
}
*target_it = std::forward<T>(value); *target_it = std::forward<T>(value);
} }
++x0_size; ++x0_size;
@ -396,59 +449,75 @@ public:
void resize(size_t size) { void resize(size_t size) {
#ifndef NDEBUG #ifndef NDEBUG
if (size > N) if (size > N) {
Log.report(logvisor::Fatal, fmt("resize() call overflows rstl::reserved_vector.")); Log.report(logvisor::Fatal, fmt("resize() call overflows rstl::reserved_vector."));
}
#endif #endif
if (size > x0_size) { if (size > x0_size) {
for (size_t i = x0_size; i < size; ++i) for (size_t i = x0_size; i < size; ++i) {
::new (static_cast<void*>(std::addressof(_value(i)))) T(); ::new (static_cast<void*>(std::addressof(_value(i)))) T();
}
x0_size = size; x0_size = size;
} else if (size < x0_size) { } else if (size < x0_size) {
if (std::is_destructible<T>::value && !std::is_trivially_destructible<T>::value) if constexpr (std::is_destructible_v<T> && !std::is_trivially_destructible_v<T>) {
for (size_t i = size; i < x0_size; ++i) for (size_t i = size; i < x0_size; ++i) {
destroy(_value(i)); destroy(_value(i));
}
}
x0_size = size; x0_size = size;
} }
} }
void resize(size_t size, const T& value) { void resize(size_t size, const T& value) {
#ifndef NDEBUG #ifndef NDEBUG
if (size > N) if (size > N) {
Log.report(logvisor::Fatal, fmt("resize() call overflows rstl::reserved_vector.")); Log.report(logvisor::Fatal, fmt("resize() call overflows rstl::reserved_vector."));
}
#endif #endif
if (size > x0_size) { if (size > x0_size) {
for (size_t i = x0_size; i < size; ++i) for (size_t i = x0_size; i < size; ++i) {
::new (static_cast<void*>(std::addressof(_value(i)))) T(value); ::new (static_cast<void*>(std::addressof(_value(i)))) T(value);
}
x0_size = size; x0_size = size;
} else if (size < x0_size) { } else if (size < x0_size) {
if (std::is_destructible<T>::value && !std::is_trivially_destructible<T>::value) if constexpr (std::is_destructible_v<T> && !std::is_trivially_destructible_v<T>) {
for (size_t i = size; i < x0_size; ++i) for (size_t i = size; i < x0_size; ++i) {
destroy(_value(i)); destroy(_value(i));
}
}
x0_size = size; x0_size = size;
} }
} }
iterator erase(const_iterator pos) { iterator erase(const_iterator pos) {
#ifndef NDEBUG #ifndef NDEBUG
if (x0_size == 0) if (x0_size == 0) {
Log.report(logvisor::Fatal, fmt("erase() called on empty rstl::reserved_vector.")); Log.report(logvisor::Fatal, fmt("erase() called on empty rstl::reserved_vector."));
}
#endif #endif
for (auto it = base::_const_cast_iterator(pos) + 1; it != end(); ++it)
for (auto it = base::_const_cast_iterator(pos) + 1; it != end(); ++it) {
*(it - 1) = std::forward<T>(*it); *(it - 1) = std::forward<T>(*it);
}
--x0_size; --x0_size;
destroy(_value(x0_size)); destroy(_value(x0_size));
return base::_const_cast_iterator(pos); return base::_const_cast_iterator(pos);
} }
void pop_front() { void pop_front() {
if (x0_size != 0) if (x0_size != 0) {
erase(begin()); erase(begin());
}
} }
void clear() { void clear() {
if (std::is_destructible<T>::value && !std::is_trivially_destructible<T>::value) if constexpr (std::is_destructible_v<T> && !std::is_trivially_destructible_v<T>) {
for (auto it = begin(); it != end(); ++it) for (auto it = begin(); it != end(); ++it) {
destroy(*it); destroy(*it);
}
}
x0_size = 0; x0_size = 0;
} }
@ -479,15 +548,17 @@ public:
T& operator[](size_t idx) { T& operator[](size_t idx) {
#ifndef NDEBUG #ifndef NDEBUG
if (idx >= x0_size) if (idx >= x0_size) {
Log.report(logvisor::Fatal, fmt("out of bounds access on reserved_vector.")); Log.report(logvisor::Fatal, fmt("out of bounds access on reserved_vector."));
}
#endif #endif
return _value(idx); return _value(idx);
} }
const T& operator[](size_t idx) const { const T& operator[](size_t idx) const {
#ifndef NDEBUG #ifndef NDEBUG
if (idx >= x0_size) if (idx >= x0_size) {
Log.report(logvisor::Fatal, fmt("out of bounds access on reserved_vector.")); Log.report(logvisor::Fatal, fmt("out of bounds access on reserved_vector."));
}
#endif #endif
return _value(idx); return _value(idx);
} }