Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Make static container without array #510

Merged
merged 5 commits into from
Jan 22, 2024
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 @@
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 @@
{
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;

Check warning on line 79 in src/rpp/rpp/disposables/details/container.hpp

View check run for this annotation

Codecov / codecov/patch

src/rpp/rpp/disposables/details/container.hpp#L79

Added line #L79 was not covered by tests

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());
}
}
}
}
Loading