Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 55 additions & 12 deletions src/rpp/rpp/disposables/details/container.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,7 +12,6 @@
#include <rpp/disposables/disposable_wrapper.hpp>
#include <rpp/utils/exceptions.hpp>

#include <array>
#include <algorithm>
#include <vector>

Expand Down Expand Up @@ -61,7 +60,7 @@ template<size_t Count>
class dynamic_disposables_container : public dynamic_disposables_container_base
{
public:
dynamic_disposables_container()
dynamic_disposables_container()
: dynamic_disposables_container_base{Count}
{}
};
Expand All @@ -71,46 +70,90 @@ class static_disposables_container
{
public:
static_disposables_container() = default;
static_disposables_container(const static_disposables_container&) = delete;
static_disposables_container& operator=(const static_disposables_container& other) = delete;

static_disposables_container& operator=(static_disposables_container&& other) noexcept
{
if (this == &other)
return *this;

m_size = other.m_size;
for(size_t i =0; i < m_size; ++i)
std::construct_at(get(i), std::move(*other.get(i)));

other.clear();
return *this;
}

static_disposables_container(static_disposables_container&& other) noexcept
{
*this = std::move(other);
}

~static_disposables_container() noexcept
{
clear();
}

void push_back(const rpp::disposable_wrapper& d)
{
if (m_size >= Count)
throw rpp::utils::more_disposables_than_expected{"static_disposables_container obtained more disposables than expected"};
m_data[m_size++] = d;
std::construct_at(get(m_size++), d);
}

void push_back(rpp::disposable_wrapper&& d)
{
if (m_size >= Count)
throw rpp::utils::more_disposables_than_expected{"static_disposables_container obtained more disposables than expected"};
m_data[m_size++] = std::move(d);
std::construct_at(get(m_size++), std::move(d));
}

void remove(const rpp::disposable_wrapper& d)
{
auto itr = std::remove(m_data.begin(), m_data.end(), d);
while(itr != m_data.end()) {
(*itr++) = disposable_wrapper{};
--m_size;
for (size_t i = 0; i < m_size;)
{
if (*get(i) != d)
{
++i;
continue;
}

for(size_t j = i+1; j < m_size; ++j)
*get(j-1) = std::move(*get(j));

std::destroy_at(get(--m_size));
}
}

void dispose() const
{
for (size_t i =0; i < m_size; ++i) {
m_data[i].dispose();
get(i)->dispose();
}
}

void clear()
{
m_data = std::array<rpp::disposable_wrapper, Count>{};
for (size_t i =0; i < m_size; ++i)
std::destroy_at(get(i));
m_size = 0;
}

private:
mutable std::array<rpp::disposable_wrapper, Count> m_data{};
size_t m_size{};
const rpp::disposable_wrapper* get(size_t i) const
{
return std::launder(reinterpret_cast<const rpp::disposable_wrapper*>(&m_data[i*sizeof(rpp::disposable_wrapper)]));
}
rpp::disposable_wrapper* get(size_t i)
{
return std::launder(reinterpret_cast<rpp::disposable_wrapper*>(&m_data[i*sizeof(rpp::disposable_wrapper)]));
}

private:
alignas(rpp::disposable_wrapper) std::byte m_data[sizeof(rpp::disposable_wrapper) * Count]{};
size_t m_size{};
};

struct none_disposables_container
Expand Down
95 changes: 92 additions & 3 deletions src/tests/rpp/test_disposables.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -68,10 +68,10 @@ TEMPLATE_TEST_CASE("disposable keeps state", "", rpp::details::disposables::dyna
CHECK(!other->is_disposed());
d.add(other);
CHECK(!other->is_disposed());

d.clear();
CHECK(other->is_disposed());

CHECK(!d.is_disposed());
}
SECTION("calling clear on disposed disposable")
Expand Down Expand Up @@ -183,7 +183,7 @@ TEST_CASE("refcount disposable dispose underlying in case of reaching zero")
{
auto d = refcount->add_ref();
CHECK(d.is_disposed());

refcounted.dispose();
CHECK(underlying->dispose_count == 1);
CHECK(refcounted.is_disposed());
Expand Down Expand Up @@ -241,4 +241,93 @@ TEST_CASE("composite_disposable correctly handles exception")
d.dispose();
CHECK(d1.is_disposed());
CHECK(!d2.is_disposed());
}

TEST_CASE("static_disposable_container works as expected")
{
rpp::details::disposables::static_disposables_container<2> container{};

auto d1 = rpp::composite_disposable_wrapper{std::make_shared<rpp::composite_disposable>()};
auto d2 = rpp::composite_disposable_wrapper{std::make_shared<rpp::composite_disposable>()};

SECTION("dispose empty")
{
container.dispose();
}

container.push_back(d1);
container.push_back(d2);

SECTION("dispose with added disposable")
{
container.dispose();
CHECK(d1.is_disposed());
CHECK(d2.is_disposed());
}

SECTION("clear with added disposable")
{
container.clear();
container.dispose();
CHECK(!d1.is_disposed());
CHECK(!d2.is_disposed());
SECTION("add cleared and dispose")
{
container.push_back(d1);
CHECK(!d1.is_disposed());
container.dispose();
CHECK(d1.is_disposed());
CHECK(!d2.is_disposed());
}
}

SECTION("remove with added disposable")
{
container.remove(d1);
container.dispose();
CHECK(!d1.is_disposed());
CHECK(d2.is_disposed());
SECTION("add removed and dispose")
{
container.push_back(d1);
CHECK(!d1.is_disposed());
container.dispose();
CHECK(d1.is_disposed());
}
}

SECTION("move container")
{
auto other = std::move(container);
SECTION("dispose original")
{
container.dispose(); // NOLINT
CHECK(!d1.is_disposed());
CHECK(!d2.is_disposed());
}

SECTION("dispose copied")
{
other.dispose();
CHECK(d1.is_disposed());
CHECK(d2.is_disposed());
}
SECTION("move back")
{
container = std::move(other);
SECTION("dispose copied")
{
other.dispose(); // NOLINT
CHECK(!d1.is_disposed());
CHECK(!d2.is_disposed());
}

SECTION("dispose original")
{
container.dispose();
CHECK(d1.is_disposed());
CHECK(d2.is_disposed());
}
}
}
}