Require explicit copies for Ref/WeakRef

This commit is contained in:
2025-09-12 11:59:56 -04:00
parent 674ff581e7
commit f89868058a
4 changed files with 145 additions and 162 deletions

View File

@@ -52,9 +52,9 @@ TEST_CASE("Ref basic functionality") {
CHECK((*ref).value == 42);
}
SUBCASE("copy construction increments reference count") {
SUBCASE("explicit copy increments reference count") {
auto ref1 = make_ref<TestObject>(123);
auto ref2 = ref1;
auto ref2 = ref1.copy();
CHECK(ref1);
CHECK(ref2);
@@ -63,11 +63,11 @@ TEST_CASE("Ref basic functionality") {
CHECK(ref2->value == 123);
}
SUBCASE("copy assignment works correctly") {
SUBCASE("explicit copy assignment works correctly") {
auto ref1 = make_ref<TestObject>(100);
auto ref2 = make_ref<TestObject>(200);
ref2 = ref1;
ref2 = ref1.copy();
CHECK(ref1.get() == ref2.get());
CHECK(ref1->value == 100);
CHECK(ref2->value == 100);
@@ -109,7 +109,7 @@ TEST_CASE("Ref basic functionality") {
TEST_CASE("WeakRef basic functionality") {
SUBCASE("construction from Ref") {
auto ref = make_ref<TestObject>(333);
WeakRef<TestObject> weak_ref = ref;
WeakRef<TestObject> weak_ref = ref.as_weak();
auto locked = weak_ref.lock();
CHECK(locked);
@@ -121,7 +121,7 @@ TEST_CASE("WeakRef basic functionality") {
WeakRef<TestObject> weak_ref;
{
auto ref = make_ref<TestObject>(444);
weak_ref = ref;
weak_ref = ref.as_weak();
}
// ref goes out of scope, object should be destroyed
@@ -131,8 +131,8 @@ TEST_CASE("WeakRef basic functionality") {
SUBCASE("copy and move semantics") {
auto ref = make_ref<TestObject>(666);
WeakRef<TestObject> weak1 = ref;
WeakRef<TestObject> weak2 = weak1; // copy
WeakRef<TestObject> weak1 = ref.as_weak();
WeakRef<TestObject> weak2 = weak1.copy(); // explicit copy
WeakRef<TestObject> weak3 = std::move(weak1); // move
auto locked2 = weak2.lock();
@@ -160,7 +160,7 @@ TEST_CASE("Ref thread safety") {
start_latch.arrive_and_wait();
for (int j = 0; j < copies_per_thread; ++j) {
auto copy = ref;
auto copy = ref.copy();
CHECK(copy);
CHECK(copy->value == 777);
}
@@ -191,7 +191,7 @@ TEST_CASE("Control block cleanup race condition test") {
WeakRef<TestObject> ptr2;
auto setup = [&]() {
ptr1 = make_ref<TestObject>(0);
ptr2 = ptr1;
ptr2 = ptr1.as_weak();
};
// Barrier for synchronization - 2 participants (main thread + worker thread)
@@ -243,7 +243,7 @@ TEST_CASE("WeakRef prevents circular references") {
// Create object and weak reference
{
auto ref = make_ref<TestObject>(123);
weak_ref = ref;
weak_ref = ref.as_weak();
// Should be able to lock while object exists
auto locked = weak_ref.lock();
@@ -262,8 +262,8 @@ TEST_CASE("WeakRef prevents circular references") {
auto child = make_ref<Node>(2);
// Create potential cycle
parent->next = child; // Strong reference: parent → child
child->parent = parent; // WeakRef: child ⇝ parent (breaks cycle)
parent->next = child.copy(); // Strong reference: parent → child
child->parent = parent.as_weak(); // WeakRef: child ⇝ parent (breaks cycle)
CHECK(parent->data == 1);
CHECK(child->data == 2);
@@ -286,7 +286,7 @@ TEST_CASE("Polymorphic Ref conversions") {
CHECK(derived_ref->get_value() == 30); // 10 + 20
// Convert Ref<Derived> to Ref<Base>
Ref<Base> base_ref = derived_ref;
Ref<Base> base_ref = derived_ref.copy();
CHECK(base_ref);
CHECK(base_ref->get_value() == 30); // Virtual dispatch works
CHECK(base_ref->base_value == 10);
@@ -303,7 +303,7 @@ TEST_CASE("Polymorphic Ref conversions") {
CHECK(base_ref->get_value() == 100);
// Assign derived to base
base_ref = derived_ref;
base_ref = derived_ref.copy();
CHECK(base_ref->get_value() == 20); // 5 + 15
CHECK(base_ref.get() == derived_ref.get());
}
@@ -338,7 +338,7 @@ TEST_CASE("Polymorphic Ref conversions") {
CHECK(another_derived->get_value() == 24); // 6 * 4
// Convert to base
Ref<Base> base_ref = another_derived;
Ref<Base> base_ref = another_derived.copy();
CHECK(base_ref->get_value() == 24); // Virtual dispatch
CHECK(base_ref.get() == another_derived.get());
}
@@ -349,10 +349,10 @@ TEST_CASE("Polymorphic WeakRef conversions") {
auto derived_ref = make_ref<Derived>(3, 7);
// Create WeakRef<Derived>
WeakRef<Derived> weak_derived = derived_ref;
WeakRef<Derived> weak_derived = derived_ref.as_weak();
// Convert to WeakRef<Base>
WeakRef<Base> weak_base = weak_derived;
WeakRef<Base> weak_base = weak_derived.copy();
// Both should lock to same object
auto locked_derived = weak_derived.lock();
@@ -368,11 +368,11 @@ TEST_CASE("Polymorphic WeakRef conversions") {
auto derived_ref = make_ref<Derived>(4, 6);
auto base_ref = make_ref<Base>(999);
WeakRef<Derived> weak_derived = derived_ref;
WeakRef<Base> weak_base = base_ref;
WeakRef<Derived> weak_derived = derived_ref.as_weak();
WeakRef<Base> weak_base = base_ref.as_weak();
// Assign derived weak ref to base weak ref
weak_base = weak_derived;
weak_base = weak_derived.copy();
auto locked = weak_base.lock();
CHECK(locked);
@@ -384,7 +384,7 @@ TEST_CASE("Polymorphic WeakRef conversions") {
auto derived_ref = make_ref<Derived>(2, 8);
// Create WeakRef<Base> directly from Ref<Derived>
WeakRef<Base> weak_base = derived_ref;
WeakRef<Base> weak_base = derived_ref.as_weak();
auto locked = weak_base.lock();
CHECK(locked);
@@ -394,7 +394,7 @@ TEST_CASE("Polymorphic WeakRef conversions") {
SUBCASE("WeakRef move operations") {
auto derived_ref = make_ref<Derived>(1, 9);
WeakRef<Derived> weak_derived = derived_ref;
WeakRef<Derived> weak_derived = derived_ref.as_weak();
// Move construct
WeakRef<Base> weak_base = std::move(weak_derived);
@@ -414,7 +414,7 @@ TEST_CASE("Polymorphic edge cases") {
CHECK(!empty_derived);
// Convert empty derived to base
Ref<Base> empty_base = empty_derived;
Ref<Base> empty_base = empty_derived.copy();
CHECK(!empty_base);
// Move empty derived to base
@@ -427,7 +427,7 @@ TEST_CASE("Polymorphic edge cases") {
CHECK(!empty_weak_derived.lock());
// Convert empty weak derived to weak base
WeakRef<Base> empty_weak_base = empty_weak_derived;
WeakRef<Base> empty_weak_base = empty_weak_derived.copy();
CHECK(!empty_weak_base.lock());
}
@@ -435,7 +435,7 @@ TEST_CASE("Polymorphic edge cases") {
auto derived_ref = make_ref<Derived>(5, 5);
// Ref<Derived> → WeakRef<Base>
WeakRef<Base> weak_base_from_ref = derived_ref;
WeakRef<Base> weak_base_from_ref = derived_ref.as_weak();
// WeakRef<Base> → Ref<Base> via lock
auto base_ref_from_weak = weak_base_from_ref.lock();
@@ -457,5 +457,5 @@ TEST_CASE("Self-referencing WeakRef pattern") {
WeakRef<SelfReferencing> self_;
};
auto x = make_ref<SelfReferencing>();
x->self_ = x;
x->self_ = x.as_weak();
}