mirror of
https://github.com/dolphin-emu/dolphin.git
synced 2025-06-28 01:49:33 -06:00
Merge pull request #13587 from jordan-woyak/manual-value
Common: Move some duplicate container element construction logic into a ManuallyConstructedValue template.
This commit is contained in:
@ -8,7 +8,8 @@
|
||||
|
||||
#include <atomic>
|
||||
#include <cassert>
|
||||
#include <memory>
|
||||
|
||||
#include "Common/TypeUtils.h"
|
||||
|
||||
namespace Common
|
||||
{
|
||||
@ -38,7 +39,7 @@ public:
|
||||
template <typename... Args>
|
||||
void Emplace(Args&&... args)
|
||||
{
|
||||
std::construct_at(&m_write_ptr->value.data, std::forward<Args>(args)...);
|
||||
m_write_ptr->value.Construct(std::forward<Args>(args)...);
|
||||
|
||||
Node* const new_ptr = new Node;
|
||||
m_write_ptr->next = new_ptr;
|
||||
@ -54,14 +55,14 @@ public:
|
||||
}
|
||||
|
||||
// The following are only safe from the "consumer thread":
|
||||
T& Front() { return m_read_ptr->value.data; }
|
||||
const T& Front() const { return m_read_ptr->value.data; }
|
||||
T& Front() { return m_read_ptr->value.Ref(); }
|
||||
const T& Front() const { return m_read_ptr->value.Ref(); }
|
||||
|
||||
void Pop()
|
||||
{
|
||||
assert(!Empty());
|
||||
|
||||
std::destroy_at(&Front());
|
||||
m_read_ptr->value.Destroy();
|
||||
|
||||
Node* const old_node = m_read_ptr;
|
||||
m_read_ptr = old_node->next;
|
||||
@ -94,14 +95,7 @@ public:
|
||||
private:
|
||||
struct Node
|
||||
{
|
||||
// union allows value construction to be deferred until Push.
|
||||
union Value
|
||||
{
|
||||
T data;
|
||||
Value() {}
|
||||
~Value() {}
|
||||
} value;
|
||||
|
||||
ManuallyConstructedValue<T> value;
|
||||
Node* next;
|
||||
};
|
||||
|
||||
|
@ -7,9 +7,13 @@
|
||||
#include <cstddef>
|
||||
#include <utility>
|
||||
|
||||
#include "Common/TypeUtils.h"
|
||||
|
||||
namespace Common
|
||||
{
|
||||
|
||||
// TODO C++26: Replace with std::inplace_vector.
|
||||
|
||||
// An std::vector-like container that uses no heap allocations but is limited to a maximum size.
|
||||
template <typename T, size_t MaxSize>
|
||||
class SmallVector final
|
||||
@ -59,13 +63,13 @@ public:
|
||||
value_type& emplace_back(Args&&... args)
|
||||
{
|
||||
assert(m_size < MaxSize);
|
||||
return *::new (&m_array[m_size++ * sizeof(value_type)]) value_type{std::forward<Args>(args)...};
|
||||
return m_array[m_size++].Construct(std::forward<Args>(args)...);
|
||||
}
|
||||
|
||||
void pop_back()
|
||||
{
|
||||
assert(m_size > 0);
|
||||
std::destroy_at(data() + --m_size);
|
||||
m_array[--m_size].Destroy();
|
||||
}
|
||||
|
||||
value_type& operator[](size_t i)
|
||||
@ -79,11 +83,11 @@ public:
|
||||
return data()[i];
|
||||
}
|
||||
|
||||
auto data() { return std::launder(reinterpret_cast<value_type*>(m_array.data())); }
|
||||
auto data() { return m_array.data()->Ptr(); }
|
||||
auto begin() { return data(); }
|
||||
auto end() { return data() + m_size; }
|
||||
|
||||
auto data() const { return std::launder(reinterpret_cast<const value_type*>(m_array.data())); }
|
||||
auto data() const { return m_array.data()->Ptr(); }
|
||||
auto begin() const { return data(); }
|
||||
auto end() const { return data() + m_size; }
|
||||
|
||||
@ -106,7 +110,7 @@ public:
|
||||
void clear() { resize(0); }
|
||||
|
||||
private:
|
||||
alignas(value_type) std::array<std::byte, MaxSize * sizeof(value_type)> m_array;
|
||||
std::array<ManuallyConstructedValue<T>, MaxSize> m_array;
|
||||
size_t m_size = 0;
|
||||
};
|
||||
|
||||
|
@ -3,6 +3,7 @@
|
||||
#pragma once
|
||||
|
||||
#include <cstddef>
|
||||
#include <memory>
|
||||
#include <type_traits>
|
||||
|
||||
namespace Common
|
||||
@ -82,4 +83,54 @@ static_assert(!IsNOf<int, 1, int, int>::value);
|
||||
static_assert(IsNOf<int, 2, int, int>::value);
|
||||
static_assert(IsNOf<int, 2, int, short>::value); // Type conversions ARE allowed
|
||||
static_assert(!IsNOf<int, 2, int, char*>::value);
|
||||
|
||||
// Lighter than optional<T> but you must manage object lifetime yourself.
|
||||
// You must call Destroy if you call Construct.
|
||||
// Useful for containers.
|
||||
template <typename T>
|
||||
class ManuallyConstructedValue
|
||||
{
|
||||
public:
|
||||
template <typename... Args>
|
||||
T& Construct(Args&&... args)
|
||||
{
|
||||
static_assert(sizeof(ManuallyConstructedValue) == sizeof(T));
|
||||
|
||||
// TODO: Remove placement-new version when we can require Clang 16.
|
||||
#if defined(__cpp_aggregate_paren_init) && (__cpp_aggregate_paren_init >= 201902L)
|
||||
return *std::construct_at(&m_value.data, std::forward<Args>(args)...);
|
||||
#else
|
||||
return *::new (&m_value.data) T{std::forward<Args>(args)...};
|
||||
#endif
|
||||
}
|
||||
|
||||
void Destroy() { std::destroy_at(&m_value.data); }
|
||||
|
||||
T* Ptr() { return &m_value.data; }
|
||||
const T* Ptr() const { return &m_value.data; }
|
||||
T& Ref() { return m_value.data; }
|
||||
const T& Ref() const { return m_value.data; }
|
||||
|
||||
T* operator->() { return Ptr(); }
|
||||
const T* operator->() const { return Ptr(); }
|
||||
T& operator*() { return Ref(); }
|
||||
const T& operator*() const { return Ref(); }
|
||||
|
||||
private:
|
||||
union Value
|
||||
{
|
||||
// The union allows this object's automatic construction to be avoided.
|
||||
T data;
|
||||
|
||||
Value() {}
|
||||
~Value() {}
|
||||
|
||||
Value& operator=(const Value&) = delete;
|
||||
Value(const Value&) = delete;
|
||||
Value& operator=(Value&&) = delete;
|
||||
Value(Value&&) = delete;
|
||||
|
||||
} m_value;
|
||||
};
|
||||
|
||||
} // namespace Common
|
||||
|
Reference in New Issue
Block a user