Add polymorphism support to Ref

This commit is contained in:
2025-09-11 14:15:52 -04:00
parent 9a8d4feedd
commit 5d932bf36c
6 changed files with 331 additions and 6 deletions

View File

@@ -19,6 +19,28 @@ struct Node {
explicit Node(int d) : data(d) {}
};
// Classes for polymorphism testing
struct Base {
int base_value;
explicit Base(int v) : base_value(v) {}
virtual ~Base() = default;
virtual int get_value() const { return base_value; }
};
struct Derived : public Base {
int derived_value;
explicit Derived(int base_v, int derived_v)
: Base(base_v), derived_value(derived_v) {}
int get_value() const override { return base_value + derived_value; }
};
struct AnotherDerived : public Base {
int another_value;
explicit AnotherDerived(int base_v, int another_v)
: Base(base_v), another_value(another_v) {}
int get_value() const override { return base_value * another_value; }
};
} // anonymous namespace
TEST_CASE("Ref basic functionality") {
@@ -257,3 +279,169 @@ TEST_CASE("WeakRef prevents circular references") {
CHECK(!child->parent.lock());
}
}
TEST_CASE("Polymorphic Ref conversions") {
SUBCASE("copy construction from derived to base") {
auto derived_ref = make_ref<Derived>(10, 20);
CHECK(derived_ref->get_value() == 30); // 10 + 20
// Convert Ref<Derived> to Ref<Base>
Ref<Base> base_ref = derived_ref;
CHECK(base_ref);
CHECK(base_ref->get_value() == 30); // Virtual dispatch works
CHECK(base_ref->base_value == 10);
// Both should point to same object
CHECK(base_ref.get() == derived_ref.get());
}
SUBCASE("copy assignment from derived to base") {
auto derived_ref = make_ref<Derived>(5, 15);
auto base_ref = make_ref<Base>(100);
// Before assignment
CHECK(base_ref->get_value() == 100);
// Assign derived to base
base_ref = derived_ref;
CHECK(base_ref->get_value() == 20); // 5 + 15
CHECK(base_ref.get() == derived_ref.get());
}
SUBCASE("move construction from derived to base") {
auto derived_ref = make_ref<Derived>(7, 3);
Base *original_ptr = derived_ref.get();
// Move construct base from derived
Ref<Base> base_ref = std::move(derived_ref);
CHECK(base_ref);
CHECK(base_ref->get_value() == 10); // 7 + 3
CHECK(base_ref.get() == original_ptr);
CHECK(!derived_ref); // Original should be empty after move
}
SUBCASE("move assignment from derived to base") {
auto derived_ref = make_ref<Derived>(8, 12);
auto base_ref = make_ref<Base>(200);
Base *derived_ptr = derived_ref.get();
// Move assign
base_ref = std::move(derived_ref);
CHECK(base_ref);
CHECK(base_ref->get_value() == 20); // 8 + 12
CHECK(base_ref.get() == derived_ptr);
CHECK(!derived_ref); // Should be empty after move
}
SUBCASE("multiple inheritance levels") {
auto another_derived = make_ref<AnotherDerived>(6, 4);
CHECK(another_derived->get_value() == 24); // 6 * 4
// Convert to base
Ref<Base> base_ref = another_derived;
CHECK(base_ref->get_value() == 24); // Virtual dispatch
CHECK(base_ref.get() == another_derived.get());
}
}
TEST_CASE("Polymorphic WeakRef conversions") {
SUBCASE("WeakRef copy construction from derived to base") {
auto derived_ref = make_ref<Derived>(3, 7);
// Create WeakRef<Derived>
WeakRef<Derived> weak_derived = derived_ref;
// Convert to WeakRef<Base>
WeakRef<Base> weak_base = weak_derived;
// Both should lock to same object
auto locked_derived = weak_derived.lock();
auto locked_base = weak_base.lock();
CHECK(locked_derived);
CHECK(locked_base);
CHECK(locked_derived.get() == locked_base.get());
CHECK(locked_base->get_value() == 10); // 3 + 7
}
SUBCASE("WeakRef copy assignment from derived to base") {
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;
// Assign derived weak ref to base weak ref
weak_base = weak_derived;
auto locked = weak_base.lock();
CHECK(locked);
CHECK(locked->get_value() == 10); // 4 + 6
CHECK(locked.get() == derived_ref.get());
}
SUBCASE("WeakRef from Ref<Derived> to WeakRef<Base>") {
auto derived_ref = make_ref<Derived>(2, 8);
// Create WeakRef<Base> directly from Ref<Derived>
WeakRef<Base> weak_base = derived_ref;
auto locked = weak_base.lock();
CHECK(locked);
CHECK(locked->get_value() == 10); // 2 + 8
CHECK(locked.get() == derived_ref.get());
}
SUBCASE("WeakRef move operations") {
auto derived_ref = make_ref<Derived>(1, 9);
WeakRef<Derived> weak_derived = derived_ref;
// Move construct
WeakRef<Base> weak_base = std::move(weak_derived);
// Original should be empty, new should work
CHECK(!weak_derived.lock());
auto locked = weak_base.lock();
CHECK(locked);
CHECK(locked->get_value() == 10); // 1 + 9
}
}
TEST_CASE("Polymorphic edge cases") {
SUBCASE("empty Ref conversions") {
Ref<Derived> empty_derived;
CHECK(!empty_derived);
// Convert empty derived to base
Ref<Base> empty_base = empty_derived;
CHECK(!empty_base);
// Move empty derived to base
Ref<Base> moved_base = std::move(empty_derived);
CHECK(!moved_base);
}
SUBCASE("empty WeakRef conversions") {
WeakRef<Derived> empty_weak_derived;
CHECK(!empty_weak_derived.lock());
// Convert empty weak derived to weak base
WeakRef<Base> empty_weak_base = empty_weak_derived;
CHECK(!empty_weak_base.lock());
}
SUBCASE("mixed Ref and WeakRef conversions") {
auto derived_ref = make_ref<Derived>(5, 5);
// Ref<Derived> → WeakRef<Base>
WeakRef<Base> weak_base_from_ref = derived_ref;
// WeakRef<Base> → Ref<Base> via lock
auto base_ref_from_weak = weak_base_from_ref.lock();
CHECK(base_ref_from_weak);
CHECK(base_ref_from_weak->get_value() == 10); // 5 + 5
CHECK(base_ref_from_weak.get() == derived_ref.get());
}
}