Skip to content
Draft
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
13 changes: 5 additions & 8 deletions include/proxy/v4/detail/core.h
Original file line number Diff line number Diff line change
Expand Up @@ -617,10 +617,7 @@ struct copy_dispatch {
}
};
struct destroy_dispatch {
template <class T>
PRO4D_STATIC_CALL(void, T& self) noexcept(std::is_nothrow_destructible_v<T>) {
std::destroy_at(&self);
}
PRO4D_STATIC_CALL(void, auto&&) noexcept {}
};
template <class D, class ONE, class OE, constraint_level C>
struct lifetime_meta_traits : std::type_identity<void> {};
Expand Down Expand Up @@ -925,8 +922,8 @@ struct facade_traits : specialization_t<facade_conv_traits_impl,
void(void*) const, F::copyability>,
lifetime_meta_t<relocate_dispatch, void(void*) && noexcept,
void(void*) &&, F::relocatability>,
lifetime_meta_t<destroy_dispatch, void() noexcept, void(),
F::destructibility>,
lifetime_meta_t<destroy_dispatch, void() && noexcept,
void() &&, F::destructibility>,
typename facade_traits::conv_meta,
typename facade_traits::refl_meta>>;
using indirect_accessor = composite_t<
Expand Down Expand Up @@ -1331,8 +1328,8 @@ class proxy : public detail::facade_traits<F>::direct_accessor,
if constexpr (F::destructibility != constraint_level::trivial) {
if (meta_.has_value()) {
invoke<detail::destroy_dispatch,
void() noexcept(F::destructibility ==
constraint_level::nothrow)>(*this);
void() && noexcept(F::destructibility ==
constraint_level::nothrow)>(std::move(*this));
}
}
}
Expand Down
78 changes: 78 additions & 0 deletions tests/proxy_lifetime_tests.cpp
Original file line number Diff line number Diff line change
Expand Up @@ -26,6 +26,14 @@ struct TestTrivialFacade
::support_destruction<pro::constraint_level::trivial> //
::build {};

struct TestThrowingDestructionFacade
: pro::facade_builder //
::add_convention<utils::spec::FreeToString, std::string()> //
::support_copy<pro::constraint_level::nontrivial> //
::support_relocation<pro::constraint_level::nontrivial> //
::support_destruction<pro::constraint_level::nontrivial> //
::build {};

struct TestRttiFacade : pro::facade_builder //
::add_direct_reflection<utils::RttiReflector> //
::add_facade_with_substitution<TestFacade> //
Expand Down Expand Up @@ -274,6 +282,22 @@ TEST(ProxyLifetimeTests, TestMoveConstrction_FromNull) {
ASSERT_FALSE(p2.has_value());
}

TEST(ProxyLifetimeTests, TestDestruction_Exception) {
utils::LifetimeTracker tracker;
std::vector<utils::LifetimeOperation> expected_ops;
auto destroy = [&] {
pro::proxy<detail::TestThrowingDestructionFacade> p{
std::in_place_type<utils::LifetimeTracker::ThrowingDestructionSession>,
&tracker};
};
tracker.ThrowOnNextDestruction();
ASSERT_THROW(destroy(), utils::DestructionFailure);
expected_ops.emplace_back(1,
utils::LifetimeOperationType::kValueConstruction);
expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction);
ASSERT_TRUE(tracker.GetOperations() == expected_ops);
}

TEST(ProxyLifetimeTests, TestNullAssignment_FromNullptr_ToValue) {
utils::LifetimeTracker tracker;
std::vector<utils::LifetimeOperation> expected_ops;
Expand Down Expand Up @@ -358,6 +382,31 @@ TEST(ProxyLifetimeTests, TestPolyAssignment_ToValue_Exception) {
ASSERT_TRUE(tracker.GetOperations() == expected_ops);
}

TEST(ProxyLifetimeTests, TestPolyAssignment_ToValue_DestructionException) {
utils::LifetimeTracker tracker;
std::vector<utils::LifetimeOperation> expected_ops;
{
pro::proxy<detail::TestThrowingDestructionFacade> p{
std::in_place_type<utils::LifetimeTracker::ThrowingDestructionSession>,
&tracker};
expected_ops.emplace_back(1,
utils::LifetimeOperationType::kValueConstruction);
utils::LifetimeTracker::Session session{&tracker};
expected_ops.emplace_back(2,
utils::LifetimeOperationType::kValueConstruction);
tracker.ThrowOnNextDestruction();
ASSERT_THROW(p = session, utils::DestructionFailure);
ASSERT_FALSE(p.has_value());
expected_ops.emplace_back(3,
utils::LifetimeOperationType::kCopyConstruction);
expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction);
expected_ops.emplace_back(3, utils::LifetimeOperationType::kDestruction);
ASSERT_TRUE(tracker.GetOperations() == expected_ops);
}
expected_ops.emplace_back(2, utils::LifetimeOperationType::kDestruction);
ASSERT_TRUE(tracker.GetOperations() == expected_ops);
}

TEST(ProxyLifetimeTests, TestPolyAssignment_FromValue_ToNull) {
utils::LifetimeTracker tracker;
std::vector<utils::LifetimeOperation> expected_ops;
Expand Down Expand Up @@ -630,6 +679,35 @@ TEST(ProxyLifetimeTests, TestCopyAssignment_FromValue_ToValue_Exception) {
ASSERT_TRUE(tracker.GetOperations() == expected_ops);
}

TEST(ProxyLifetimeTests,
TestCopyAssignment_FromValue_ToValue_DestructionException) {
utils::LifetimeTracker tracker;
std::vector<utils::LifetimeOperation> expected_ops;
{
pro::proxy<detail::TestThrowingDestructionFacade> p1{
std::in_place_type<utils::LifetimeTracker::ThrowingDestructionSession>,
&tracker};
expected_ops.emplace_back(1,
utils::LifetimeOperationType::kValueConstruction);
pro::proxy<detail::TestThrowingDestructionFacade> p2{
std::in_place_type<utils::LifetimeTracker::Session>, &tracker};
expected_ops.emplace_back(2,
utils::LifetimeOperationType::kValueConstruction);
tracker.ThrowOnNextDestruction();
ASSERT_THROW(p1 = p2, utils::DestructionFailure);
ASSERT_FALSE(p1.has_value());
ASSERT_TRUE(p2.has_value());
ASSERT_EQ(ToString(*p2), "Session 2");
expected_ops.emplace_back(3,
utils::LifetimeOperationType::kCopyConstruction);
expected_ops.emplace_back(1, utils::LifetimeOperationType::kDestruction);
expected_ops.emplace_back(3, utils::LifetimeOperationType::kDestruction);
ASSERT_TRUE(tracker.GetOperations() == expected_ops);
}
expected_ops.emplace_back(2, utils::LifetimeOperationType::kDestruction);
ASSERT_TRUE(tracker.GetOperations() == expected_ops);
}

TEST(ProxyLifetimeTests, TestCopyAssignment_FromValue_ToSelf) {
utils::LifetimeTracker tracker;
std::vector<utils::LifetimeOperation> expected_ops;
Expand Down
17 changes: 16 additions & 1 deletion tests/utils.h
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,8 @@ struct ConstructionFailure : std::exception {
LifetimeOperationType type_;
};

struct DestructionFailure : std::exception {};

class LifetimeTracker {
public:
LifetimeTracker() = default;
Expand Down Expand Up @@ -69,13 +71,25 @@ class LifetimeTracker {
return "Session " + std::to_string(self.id_);
}

private:
protected:
int id_;
LifetimeTracker* const host_;
};

class ThrowingDestructionSession : public Session {
public:
using Session::Session;
~ThrowingDestructionSession() noexcept(false) {
if (host_->throw_on_next_destruction_) {
host_->throw_on_next_destruction_ = false;
throw DestructionFailure{};
}
}
};

const std::vector<LifetimeOperation>& GetOperations() const { return ops_; }
void ThrowOnNextConstruction() { throw_on_next_construction_ = true; }
void ThrowOnNextDestruction() { throw_on_next_destruction_ = true; }

private:
int AllocateId(LifetimeOperationType operation_type) {
Expand All @@ -89,6 +103,7 @@ class LifetimeTracker {

int max_id_ = 0;
bool throw_on_next_construction_ = false;
bool throw_on_next_destruction_ = false;
std::vector<LifetimeOperation> ops_;
};

Expand Down
Loading