Add polymorphism support to Ref
This commit is contained in:
@@ -4,6 +4,7 @@
|
|||||||
#include "metric.hpp"
|
#include "metric.hpp"
|
||||||
#include "perfetto_categories.hpp"
|
#include "perfetto_categories.hpp"
|
||||||
#include "process_collector.hpp"
|
#include "process_collector.hpp"
|
||||||
|
#include "reference.hpp"
|
||||||
#include "server.hpp"
|
#include "server.hpp"
|
||||||
#include <csignal>
|
#include <csignal>
|
||||||
#include <cstring>
|
#include <cstring>
|
||||||
@@ -181,7 +182,7 @@ int main(int argc, char *argv[]) {
|
|||||||
#endif
|
#endif
|
||||||
|
|
||||||
// Register the process collector for default metrics.
|
// Register the process collector for default metrics.
|
||||||
metric::register_collector(std::make_shared<ProcessCollector>());
|
metric::register_collector(make_ref<ProcessCollector>());
|
||||||
|
|
||||||
std::string config_file = "config.toml";
|
std::string config_file = "config.toml";
|
||||||
|
|
||||||
|
|||||||
@@ -444,7 +444,7 @@ struct Metric {
|
|||||||
}
|
}
|
||||||
|
|
||||||
static auto &get_collectors() {
|
static auto &get_collectors() {
|
||||||
using CollectorRegistry = std::vector<std::shared_ptr<Collector>>;
|
using CollectorRegistry = std::vector<Ref<Collector>>;
|
||||||
static CollectorRegistry *collectors = new CollectorRegistry();
|
static CollectorRegistry *collectors = new CollectorRegistry();
|
||||||
return *collectors;
|
return *collectors;
|
||||||
}
|
}
|
||||||
@@ -1803,7 +1803,7 @@ void reset_metrics_for_testing() {
|
|||||||
// when threads exit naturally
|
// when threads exit naturally
|
||||||
}
|
}
|
||||||
|
|
||||||
void register_collector(std::shared_ptr<Collector> collector) {
|
void register_collector(Ref<Collector> collector) {
|
||||||
std::unique_lock<std::mutex> _{Metric::mutex};
|
std::unique_lock<std::mutex> _{Metric::mutex};
|
||||||
++Metric::registration_version;
|
++Metric::registration_version;
|
||||||
Metric::get_collectors().push_back(std::move(collector));
|
Metric::get_collectors().push_back(std::move(collector));
|
||||||
|
|||||||
@@ -51,6 +51,7 @@
|
|||||||
#include <vector>
|
#include <vector>
|
||||||
|
|
||||||
#include "arena.hpp"
|
#include "arena.hpp"
|
||||||
|
#include "reference.hpp"
|
||||||
|
|
||||||
namespace metric {
|
namespace metric {
|
||||||
|
|
||||||
@@ -255,12 +256,12 @@ struct Collector {
|
|||||||
/**
|
/**
|
||||||
* @brief Register a collector with the metrics system.
|
* @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.
|
* 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> collector);
|
void register_collector(Ref<Collector> collector);
|
||||||
|
|
||||||
// Note: Histograms do not support callbacks due to their multi-value nature
|
// Note: Histograms do not support callbacks due to their multi-value nature
|
||||||
// (buckets + sum + count). Use static histogram metrics only.
|
// (buckets + sum + count). Use static histogram metrics only.
|
||||||
|
|||||||
@@ -135,6 +135,18 @@ template <typename T> struct Ref {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converting copy constructor for polymorphism (Derived -> Base)
|
||||||
|
*/
|
||||||
|
template <typename U>
|
||||||
|
Ref(const Ref<U> &other) noexcept
|
||||||
|
requires std::is_convertible_v<U *, T *>
|
||||||
|
: ptr(other.ptr), control_block(other.control_block) {
|
||||||
|
if (control_block) {
|
||||||
|
control_block->increment_strong();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Copy assignment operator
|
* @brief Copy assignment operator
|
||||||
*/
|
*/
|
||||||
@@ -150,6 +162,22 @@ template <typename T> struct Ref {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converting assignment operator for polymorphism (Derived -> Base)
|
||||||
|
*/
|
||||||
|
template <typename U>
|
||||||
|
Ref &operator=(const Ref<U> &other) noexcept
|
||||||
|
requires std::is_convertible_v<U *, T *>
|
||||||
|
{
|
||||||
|
release();
|
||||||
|
ptr = other.ptr;
|
||||||
|
control_block = other.control_block;
|
||||||
|
if (control_block) {
|
||||||
|
control_block->increment_strong();
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Move constructor - transfers ownership
|
* @brief Move constructor - transfers ownership
|
||||||
*/
|
*/
|
||||||
@@ -159,6 +187,17 @@ template <typename T> struct Ref {
|
|||||||
other.control_block = nullptr;
|
other.control_block = nullptr;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converting move constructor for polymorphism (Derived -> Base)
|
||||||
|
*/
|
||||||
|
template <typename U>
|
||||||
|
Ref(Ref<U> &&other) noexcept
|
||||||
|
requires std::is_convertible_v<U *, T *>
|
||||||
|
: ptr(other.ptr), control_block(other.control_block) {
|
||||||
|
other.ptr = nullptr;
|
||||||
|
other.control_block = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Move assignment operator
|
* @brief Move assignment operator
|
||||||
*/
|
*/
|
||||||
@@ -173,6 +212,22 @@ template <typename T> struct Ref {
|
|||||||
return *this;
|
return *this;
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converting move assignment operator for polymorphism (Derived ->
|
||||||
|
* Base)
|
||||||
|
*/
|
||||||
|
template <typename U>
|
||||||
|
Ref &operator=(Ref<U> &&other) noexcept
|
||||||
|
requires std::is_convertible_v<U *, T *>
|
||||||
|
{
|
||||||
|
release();
|
||||||
|
ptr = other.ptr;
|
||||||
|
control_block = other.control_block;
|
||||||
|
other.ptr = nullptr;
|
||||||
|
other.control_block = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Reset to empty state
|
* @brief Reset to empty state
|
||||||
*/
|
*/
|
||||||
@@ -233,6 +288,7 @@ private:
|
|||||||
friend Ref<U> make_ref(Args &&...args);
|
friend Ref<U> make_ref(Args &&...args);
|
||||||
|
|
||||||
template <typename U> friend struct WeakRef;
|
template <typename U> friend struct WeakRef;
|
||||||
|
template <typename U> friend struct Ref;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
@@ -302,6 +358,83 @@ template <typename T> struct WeakRef {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converting copy constructor from WeakRef for polymorphism
|
||||||
|
*/
|
||||||
|
template <typename U>
|
||||||
|
WeakRef(const WeakRef<U> &other) noexcept
|
||||||
|
requires std::is_convertible_v<U *, T *>
|
||||||
|
: control_block(other.control_block) {
|
||||||
|
if (control_block) {
|
||||||
|
control_block->increment_weak();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converting copy constructor from Ref for polymorphism
|
||||||
|
*/
|
||||||
|
template <typename U>
|
||||||
|
WeakRef(const Ref<U> &ref) noexcept
|
||||||
|
requires std::is_convertible_v<U *, T *>
|
||||||
|
: control_block(ref.control_block) {
|
||||||
|
if (control_block) {
|
||||||
|
control_block->increment_weak();
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converting copy assignment from WeakRef for polymorphism
|
||||||
|
*/
|
||||||
|
template <typename U>
|
||||||
|
WeakRef &operator=(const WeakRef<U> &other) noexcept
|
||||||
|
requires std::is_convertible_v<U *, T *>
|
||||||
|
{
|
||||||
|
release();
|
||||||
|
control_block = other.control_block;
|
||||||
|
if (control_block) {
|
||||||
|
control_block->increment_weak();
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converting copy assignment from Ref for polymorphism
|
||||||
|
*/
|
||||||
|
template <typename U>
|
||||||
|
WeakRef &operator=(const Ref<U> &ref) noexcept
|
||||||
|
requires std::is_convertible_v<U *, T *>
|
||||||
|
{
|
||||||
|
release();
|
||||||
|
control_block = ref.control_block;
|
||||||
|
if (control_block) {
|
||||||
|
control_block->increment_weak();
|
||||||
|
}
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converting move constructor from WeakRef for polymorphism
|
||||||
|
*/
|
||||||
|
template <typename U>
|
||||||
|
WeakRef(WeakRef<U> &&other) noexcept
|
||||||
|
requires std::is_convertible_v<U *, T *>
|
||||||
|
: control_block(other.control_block) {
|
||||||
|
other.control_block = nullptr;
|
||||||
|
}
|
||||||
|
|
||||||
|
/**
|
||||||
|
* @brief Converting move assignment from WeakRef for polymorphism
|
||||||
|
*/
|
||||||
|
template <typename U>
|
||||||
|
WeakRef &operator=(WeakRef<U> &&other) noexcept
|
||||||
|
requires std::is_convertible_v<U *, T *>
|
||||||
|
{
|
||||||
|
release();
|
||||||
|
control_block = other.control_block;
|
||||||
|
other.control_block = nullptr;
|
||||||
|
return *this;
|
||||||
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* @brief Copy assignment from WeakRef
|
* @brief Copy assignment from WeakRef
|
||||||
*/
|
*/
|
||||||
@@ -393,6 +526,7 @@ private:
|
|||||||
}
|
}
|
||||||
|
|
||||||
template <typename U> friend struct Ref;
|
template <typename U> friend struct Ref;
|
||||||
|
template <typename U> friend struct WeakRef;
|
||||||
};
|
};
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|||||||
@@ -9,6 +9,7 @@
|
|||||||
#include "config.hpp"
|
#include "config.hpp"
|
||||||
#include "connection_handler.hpp"
|
#include "connection_handler.hpp"
|
||||||
#include "connection_registry.hpp"
|
#include "connection_registry.hpp"
|
||||||
|
#include "reference.hpp"
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* High-performance multi-threaded server for handling network connections.
|
* High-performance multi-threaded server for handling network connections.
|
||||||
|
|||||||
@@ -19,6 +19,28 @@ struct Node {
|
|||||||
|
|
||||||
explicit Node(int d) : data(d) {}
|
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
|
} // anonymous namespace
|
||||||
|
|
||||||
TEST_CASE("Ref basic functionality") {
|
TEST_CASE("Ref basic functionality") {
|
||||||
@@ -257,3 +279,169 @@ TEST_CASE("WeakRef prevents circular references") {
|
|||||||
CHECK(!child->parent.lock());
|
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());
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user