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
5 changes: 3 additions & 2 deletions src/rpp/rpp/disposables/callback_disposable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,6 +12,7 @@

#include <rpp/disposables/fwd.hpp>
#include <rpp/disposables/details/base_disposable.hpp>
#include <rpp/disposables/disposable_wrapper.hpp>

namespace rpp
{
Expand Down Expand Up @@ -42,8 +43,8 @@ class callback_disposable final : public details::base_disposable
};

template<rpp::constraint::is_nothrow_invocable Fn>
auto make_callback_disposable(Fn&& invocable)
disposable_wrapper make_callback_disposable(Fn&& invocable)
{
return std::make_shared<rpp::callback_disposable<std::decay_t<Fn>>>(std::forward<Fn>(invocable));
return disposable_wrapper::make<rpp::callback_disposable<std::decay_t<Fn>>>(std::forward<Fn>(invocable));
}
} // namespace rpp
3 changes: 1 addition & 2 deletions src/rpp/rpp/disposables/composite_disposable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -17,7 +17,6 @@
#include <rpp/disposables/interface_composite_disposable.hpp>

#include <atomic>
#include <memory>

namespace rpp
{
Expand Down Expand Up @@ -64,7 +63,7 @@ class composite_disposable_impl : public interface_composite_disposable

void add(disposable_wrapper disposable) override
{
if (disposable.is_disposed() || disposable.get_original().get() == this)
if (disposable.is_disposed() || disposable.lock().get() == this)
return;

while (true)
Expand Down
236 changes: 158 additions & 78 deletions src/rpp/rpp/disposables/disposable_wrapper.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -10,147 +10,227 @@

#pragma once

#include <rpp/defs.hpp>
#include <rpp/disposables/fwd.hpp>
#include <rpp/disposables/interface_disposable.hpp>
#include <rpp/utils/utils.hpp>

#include <memory>
#include <variant>

namespace rpp
namespace rpp::details
{
/**
* @brief Wrapper over disposable_ptr to prevent manual checking over nullptr/is_disposed()
* @details Can keep weak_ptr in case of not owning disposable
*
* @ingroup disposables
*/
template<rpp::constraint::decayed_type TDisposable>
class disposable_wrapper_impl
class enable_wrapper_from_this;

template<rpp::constraint::decayed_type TDisposable>
class auto_dispose_wrapper final
{
struct weak_tag
public:
static_assert(std::derived_from<TDisposable, interface_disposable>);

template<typename... TArgs>
requires (std::constructible_from<TDisposable, TArgs&&...> && !rpp::constraint::variadic_decayed_same_as<auto_dispose_wrapper, TArgs...>)
explicit auto_dispose_wrapper(TArgs&&... args)
: m_data{std::forward<TArgs>(args)...}
{
};
}

template<std::derived_from<TDisposable> TT = TDisposable>
explicit disposable_wrapper_impl(weak_tag, std::weak_ptr<TT> disposable)
requires std::derived_from<TT, interface_disposable>
: m_disposable{std::move(disposable)}
auto_dispose_wrapper(const auto_dispose_wrapper&) = delete;
auto_dispose_wrapper(auto_dispose_wrapper&&) noexcept = delete;

~auto_dispose_wrapper() noexcept
{
// static_cast<interface_disposable&>(m_data).dispose_impl(rpp::interface_disposable::Mode::Destroying);
}

TDisposable* get() { return &m_data; }

private:
RPP_NO_UNIQUE_ADDRESS TDisposable m_data;
};

class disposable_wrapper_base
{
public:
disposable_wrapper_impl() = default;
bool operator==(const disposable_wrapper_base& other) const
{
return get().first == other.get().first;
}

template<std::derived_from<TDisposable> TT = TDisposable>
disposable_wrapper_impl(std::shared_ptr<TT>&& disposable)
requires std::derived_from<TT, interface_disposable>
: m_disposable{std::static_pointer_cast<TDisposable>(std::move(disposable))}
bool is_disposed() const noexcept
{
if (const auto locked = get().first)
return locked->is_disposed();
return true;
}

template<std::derived_from<TDisposable> TT = TDisposable>
disposable_wrapper_impl(const std::shared_ptr<TT>& disposable)
requires std::derived_from<TT, interface_disposable>
: m_disposable{std::static_pointer_cast<TDisposable>(disposable)}
void dispose() const noexcept
{
if (const auto locked = get().first)
locked->dispose();
}

static disposable_wrapper_impl from_shared(std::shared_ptr<TDisposable> disposable)
protected:
explicit disposable_wrapper_base(std::shared_ptr<interface_disposable>&& disposable)
: m_disposable{std::move(disposable)}
{
return disposable_wrapper_impl{std::move(disposable)};
}

static disposable_wrapper_impl from_weak(std::weak_ptr<TDisposable> disposable)
explicit disposable_wrapper_base(std::weak_ptr<interface_disposable>&& disposable)
: m_disposable{std::move(disposable)}
{
return disposable_wrapper_impl{weak_tag{}, std::move(disposable)};
}

bool operator==(const disposable_wrapper_impl& other) const
disposable_wrapper_base() = default;

std::pair<std::shared_ptr<interface_disposable>, bool> get() const noexcept
{
return raw_pointer() == other.raw_pointer();
if (const auto ptr_ptr = std::get_if<std::shared_ptr<interface_disposable>>(&m_disposable))
return {*ptr_ptr, true};

if (const auto ptr_ptr = std::get_if<std::weak_ptr<interface_disposable>>(&m_disposable))
return {ptr_ptr->lock(), false};

return {nullptr, true};
}

bool is_disposed() const noexcept
private:
std::variant<std::monostate, std::shared_ptr<interface_disposable>, std::weak_ptr<interface_disposable>> m_disposable;
};

}

namespace rpp
{
/**
* @brief Wrapper to keep disposable. Any disposable have to be created right from this wrapper with help of `make` function.
* @details Member functions is safe to call even if internal disposable is gone. Also it provides access to "raw" shared_ptr and it can be nullptr in case of disposable empty/ptr gone.
* @details Can keep weak_ptr in case of not owning disposable
*
* @ingroup disposables
*/
template<rpp::constraint::decayed_type TDisposable>
class disposable_wrapper_impl final : public details::disposable_wrapper_base
{
using TDefaultMake = std::conditional_t<std::same_as<TDisposable, interface_composite_disposable>, composite_disposable, TDisposable>;
public:
template<constraint::decayed_type TTarget>
friend class disposable_wrapper_impl;

template<rpp::constraint::decayed_type TTarget>
friend class details::enable_wrapper_from_this;

bool operator==(const disposable_wrapper_impl&) const = default;

/**
* @brief Way to create disposable_wrapper. Passed `TTarget` type can be any type derived from `TDisposable`.
*/
template<std::derived_from<TDisposable> TTarget = TDefaultMake, typename... TArgs>
requires (std::constructible_from<TTarget, TArgs&&...>)
static disposable_wrapper_impl make(TArgs&& ...args)
{
if (const auto locked = get_original())
return locked->is_disposed();
return true;
const auto ptr = std::make_shared<details::auto_dispose_wrapper<TTarget>>(std::forward<TArgs>(args)...);
auto base_ptr = std::shared_ptr<TDisposable>{ptr, static_cast<TDisposable*>(ptr->get())};
if constexpr (rpp::utils::is_base_of_v<TDisposable, rpp::details::enable_wrapper_from_this>)
{
base_ptr->set_weak_self(std::weak_ptr<interface_disposable>(base_ptr));
}
return disposable_wrapper_impl{std::static_pointer_cast<interface_disposable>(std::move(base_ptr))};
}

void dispose() const noexcept
/**
* @brief Creates disposable_wrapper which behaves like disposed disposable
*/
static disposable_wrapper_impl empty()
{
if (const auto locked = get_original())
locked->dispose();
return disposable_wrapper_impl{};
}

template<rpp::constraint::is_nothrow_invocable Fn>
disposable_wrapper add(Fn&& invocable) requires std::derived_from<TDisposable, interface_composite_disposable>
{
auto d = make_callback_disposable(std::forward<Fn>(invocable));
add(d);
return d;
}

void add(disposable_wrapper other) const
requires std::derived_from<TDisposable, interface_composite_disposable>
void add(disposable_wrapper other) const requires std::derived_from<TDisposable, interface_composite_disposable>
{
if (const auto locked = get_original())
if (const auto locked = lock())
locked->add(std::move(other));
else
other.dispose();
}

void remove(const disposable_wrapper& other) const
requires std::derived_from<TDisposable, interface_composite_disposable>
void remove(const disposable_wrapper& other) const requires std::derived_from<TDisposable, interface_composite_disposable>
{
if (const auto locked = get_original())
if (const auto locked = lock())
locked->remove(other);
}

void clear() const
requires std::derived_from<TDisposable, interface_composite_disposable>
void clear() const requires std::derived_from<TDisposable, interface_composite_disposable>
{
if (const auto locked = get_original())
if (const auto locked = lock())
locked->clear();
}

std::shared_ptr<TDisposable> get_original() const noexcept
std::shared_ptr<TDisposable> lock() const noexcept
{
if (const auto ptr_ptr = std::get_if<std::shared_ptr<TDisposable>>(&m_disposable))
return *ptr_ptr;

if (const auto ptr_ptr = std::get_if<std::weak_ptr<TDisposable>>(&m_disposable))
return ptr_ptr->lock();

return nullptr;
return std::static_pointer_cast<TDisposable>(get().first);
}

operator disposable_wrapper_impl<interface_disposable>() const
disposable_wrapper_impl as_weak() const
{
if (const auto ptr_ptr = std::get_if<std::shared_ptr<TDisposable>>(&m_disposable))
return disposable_wrapper::from_shared(*ptr_ptr);

if (const auto ptr_ptr = std::get_if<std::weak_ptr<TDisposable>>(&m_disposable))
return disposable_wrapper::from_weak(*ptr_ptr);

return rpp::disposable_wrapper{};
auto [locked, is_shared] = get();
if (is_shared)
return disposable_wrapper_impl{std::weak_ptr<interface_disposable>{locked}};
return *this;
}

bool has_underlying() const
template<constraint::decayed_type TTarget>
requires rpp::constraint::static_pointer_convertible_to<TDisposable, TTarget>
operator disposable_wrapper_impl<TTarget>() const
{
if (const auto ptr_ptr = std::get_if<std::shared_ptr<TDisposable>>(&m_disposable))
return ptr_ptr->use_count() != 0;

if (const auto ptr_ptr = std::get_if<std::weak_ptr<TDisposable>>(&m_disposable))
return ptr_ptr->use_count() != 0;

return false;
auto [locked, is_shared] = get();
if (!locked)
return rpp::disposable_wrapper_impl<TTarget>::empty();

auto res = disposable_wrapper_impl<TTarget>{std::move(locked)};
if (is_shared)
return res;

return res.as_weak();
}
private:
const TDisposable* raw_pointer() const {
if (const auto ptr_ptr = std::get_if<std::shared_ptr<TDisposable>>(&m_disposable))
return ptr_ptr->get();
using details::disposable_wrapper_base::disposable_wrapper_base;
};
}

namespace rpp::details
{
template<rpp::constraint::decayed_type TStrategy>
class enable_wrapper_from_this
{
public:
template<rpp::constraint::decayed_type TSource>
friend class rpp::disposable_wrapper_impl;

if (const auto ptr_ptr = std::get_if<std::weak_ptr<TDisposable>>(&m_disposable))
if (const auto shared = ptr_ptr->lock())
return shared.get();
protected:
enable_wrapper_from_this() = default;

return nullptr;
void set_weak_self(std::weak_ptr<interface_disposable> weak)
{
m_weak = std::move(weak);
}

public:
disposable_wrapper_impl<TStrategy> wrapper_from_this() const
{
return disposable_wrapper_impl<TStrategy>(std::static_pointer_cast<interface_disposable>(m_weak.lock()));
}

private:
std::variant<std::monostate, std::shared_ptr<TDisposable>, std::weak_ptr<TDisposable>> m_disposable;
std::weak_ptr<interface_disposable> m_weak{};
};
}
}
2 changes: 1 addition & 1 deletion src/rpp/rpp/disposables/fwd.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -56,5 +56,5 @@ class callback_disposable;
class refcount_disposable;

template<rpp::constraint::is_nothrow_invocable Fn>
auto make_callback_disposable(Fn&& invocable);
disposable_wrapper make_callback_disposable(Fn&& invocable);
} // namespace rpp
1 change: 0 additions & 1 deletion src/rpp/rpp/disposables/interface_composite_disposable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -11,7 +11,6 @@
#pragma once

#include <rpp/disposables/callback_disposable.hpp>
#include <rpp/disposables/disposable_wrapper.hpp>
#include <rpp/disposables/interface_disposable.hpp>

namespace rpp
Expand Down
4 changes: 0 additions & 4 deletions src/rpp/rpp/disposables/interface_disposable.hpp
Original file line number Diff line number Diff line change
Expand Up @@ -12,12 +12,8 @@

#include <rpp/disposables/fwd.hpp>

#include <memory>

namespace rpp
{
using disposable_ptr = std::shared_ptr<interface_disposable>;

/**
* @brief Interface of disposable
*
Expand Down
Loading