diff --git a/src/main.cpp b/src/main.cpp index 6c16588..f8d35c7 100644 --- a/src/main.cpp +++ b/src/main.cpp @@ -4,6 +4,7 @@ #include "metric.hpp" #include "perfetto_categories.hpp" #include "process_collector.hpp" +#include "reference.hpp" #include "server.hpp" #include #include @@ -181,7 +182,7 @@ int main(int argc, char *argv[]) { #endif // Register the process collector for default metrics. - metric::register_collector(std::make_shared()); + metric::register_collector(make_ref()); std::string config_file = "config.toml"; diff --git a/src/metric.cpp b/src/metric.cpp index 09eef40..e1194f5 100644 --- a/src/metric.cpp +++ b/src/metric.cpp @@ -444,7 +444,7 @@ struct Metric { } static auto &get_collectors() { - using CollectorRegistry = std::vector>; + using CollectorRegistry = std::vector>; static CollectorRegistry *collectors = new CollectorRegistry(); return *collectors; } @@ -1803,7 +1803,7 @@ void reset_metrics_for_testing() { // when threads exit naturally } -void register_collector(std::shared_ptr collector) { +void register_collector(Ref collector) { std::unique_lock _{Metric::mutex}; ++Metric::registration_version; Metric::get_collectors().push_back(std::move(collector)); diff --git a/src/metric.hpp b/src/metric.hpp index dbd2ea5..457cd72 100644 --- a/src/metric.hpp +++ b/src/metric.hpp @@ -51,6 +51,7 @@ #include #include "arena.hpp" +#include "reference.hpp" namespace metric { @@ -255,12 +256,12 @@ struct Collector { /** * @brief Register a collector with the metrics system. * - * The system will hold a shared_ptr to the collector and call its collect() + * The system will hold a Ref to the collector and call its collect() * method during each metric rendering. * - * @param collector A shared_ptr to the collector to be registered. + * @param collector A Ref to the collector to be registered. */ -void register_collector(std::shared_ptr collector); +void register_collector(Ref collector); // Note: Histograms do not support callbacks due to their multi-value nature // (buckets + sum + count). Use static histogram metrics only. diff --git a/src/reference.hpp b/src/reference.hpp index 53d32d6..ca56a84 100644 --- a/src/reference.hpp +++ b/src/reference.hpp @@ -135,6 +135,18 @@ template struct Ref { } } + /** + * @brief Converting copy constructor for polymorphism (Derived -> Base) + */ + template + Ref(const Ref &other) noexcept + requires std::is_convertible_v + : ptr(other.ptr), control_block(other.control_block) { + if (control_block) { + control_block->increment_strong(); + } + } + /** * @brief Copy assignment operator */ @@ -150,6 +162,22 @@ template struct Ref { return *this; } + /** + * @brief Converting assignment operator for polymorphism (Derived -> Base) + */ + template + Ref &operator=(const Ref &other) noexcept + requires std::is_convertible_v + { + release(); + ptr = other.ptr; + control_block = other.control_block; + if (control_block) { + control_block->increment_strong(); + } + return *this; + } + /** * @brief Move constructor - transfers ownership */ @@ -159,6 +187,17 @@ template struct Ref { other.control_block = nullptr; } + /** + * @brief Converting move constructor for polymorphism (Derived -> Base) + */ + template + Ref(Ref &&other) noexcept + requires std::is_convertible_v + : ptr(other.ptr), control_block(other.control_block) { + other.ptr = nullptr; + other.control_block = nullptr; + } + /** * @brief Move assignment operator */ @@ -173,6 +212,22 @@ template struct Ref { return *this; } + /** + * @brief Converting move assignment operator for polymorphism (Derived -> + * Base) + */ + template + Ref &operator=(Ref &&other) noexcept + requires std::is_convertible_v + { + release(); + ptr = other.ptr; + control_block = other.control_block; + other.ptr = nullptr; + other.control_block = nullptr; + return *this; + } + /** * @brief Reset to empty state */ @@ -233,6 +288,7 @@ private: friend Ref make_ref(Args &&...args); template friend struct WeakRef; + template friend struct Ref; }; /** @@ -302,6 +358,83 @@ template struct WeakRef { } } + /** + * @brief Converting copy constructor from WeakRef for polymorphism + */ + template + WeakRef(const WeakRef &other) noexcept + requires std::is_convertible_v + : control_block(other.control_block) { + if (control_block) { + control_block->increment_weak(); + } + } + + /** + * @brief Converting copy constructor from Ref for polymorphism + */ + template + WeakRef(const Ref &ref) noexcept + requires std::is_convertible_v + : control_block(ref.control_block) { + if (control_block) { + control_block->increment_weak(); + } + } + + /** + * @brief Converting copy assignment from WeakRef for polymorphism + */ + template + WeakRef &operator=(const WeakRef &other) noexcept + requires std::is_convertible_v + { + release(); + control_block = other.control_block; + if (control_block) { + control_block->increment_weak(); + } + return *this; + } + + /** + * @brief Converting copy assignment from Ref for polymorphism + */ + template + WeakRef &operator=(const Ref &ref) noexcept + requires std::is_convertible_v + { + release(); + control_block = ref.control_block; + if (control_block) { + control_block->increment_weak(); + } + return *this; + } + + /** + * @brief Converting move constructor from WeakRef for polymorphism + */ + template + WeakRef(WeakRef &&other) noexcept + requires std::is_convertible_v + : control_block(other.control_block) { + other.control_block = nullptr; + } + + /** + * @brief Converting move assignment from WeakRef for polymorphism + */ + template + WeakRef &operator=(WeakRef &&other) noexcept + requires std::is_convertible_v + { + release(); + control_block = other.control_block; + other.control_block = nullptr; + return *this; + } + /** * @brief Copy assignment from WeakRef */ @@ -393,6 +526,7 @@ private: } template friend struct Ref; + template friend struct WeakRef; }; /** diff --git a/src/server.hpp b/src/server.hpp index 2db4710..357b911 100644 --- a/src/server.hpp +++ b/src/server.hpp @@ -9,6 +9,7 @@ #include "config.hpp" #include "connection_handler.hpp" #include "connection_registry.hpp" +#include "reference.hpp" /** * High-performance multi-threaded server for handling network connections. diff --git a/tests/test_reference.cpp b/tests/test_reference.cpp index 7cd7d84..6968779 100644 --- a/tests/test_reference.cpp +++ b/tests/test_reference.cpp @@ -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(10, 20); + CHECK(derived_ref->get_value() == 30); // 10 + 20 + + // Convert Ref to Ref + Ref 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(5, 15); + auto base_ref = make_ref(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(7, 3); + Base *original_ptr = derived_ref.get(); + + // Move construct base from derived + Ref 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(8, 12); + auto base_ref = make_ref(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(6, 4); + CHECK(another_derived->get_value() == 24); // 6 * 4 + + // Convert to base + Ref 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(3, 7); + + // Create WeakRef + WeakRef weak_derived = derived_ref; + + // Convert to WeakRef + WeakRef 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(4, 6); + auto base_ref = make_ref(999); + + WeakRef weak_derived = derived_ref; + WeakRef 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 to WeakRef") { + auto derived_ref = make_ref(2, 8); + + // Create WeakRef directly from Ref + WeakRef 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(1, 9); + WeakRef weak_derived = derived_ref; + + // Move construct + WeakRef 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 empty_derived; + CHECK(!empty_derived); + + // Convert empty derived to base + Ref empty_base = empty_derived; + CHECK(!empty_base); + + // Move empty derived to base + Ref moved_base = std::move(empty_derived); + CHECK(!moved_base); + } + + SUBCASE("empty WeakRef conversions") { + WeakRef empty_weak_derived; + CHECK(!empty_weak_derived.lock()); + + // Convert empty weak derived to weak base + WeakRef empty_weak_base = empty_weak_derived; + CHECK(!empty_weak_base.lock()); + } + + SUBCASE("mixed Ref and WeakRef conversions") { + auto derived_ref = make_ref(5, 5); + + // Ref → WeakRef + WeakRef weak_base_from_ref = derived_ref; + + // WeakRef → Ref 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()); + } +}