From 7880adef89bde253a982b01275b92d73ba18f5e6 Mon Sep 17 00:00:00 2001 From: Josh Holtrop Date: Tue, 24 Feb 2026 15:48:41 -0500 Subject: [PATCH] Add static_assert to upcasting constructor; add rcp_dynamic_cast() --- include/rcp.h | 10 +++++++++- test/tests.cpp | 27 +++++++++++++++++++++++++++ 2 files changed, 36 insertions(+), 1 deletion(-) diff --git a/include/rcp.h b/include/rcp.h index 51d28ec..50a6d7d 100644 --- a/include/rcp.h +++ b/include/rcp.h @@ -23,6 +23,7 @@ #pragma once #include +#include /** * Reference-counting pointer. @@ -97,10 +98,11 @@ public: } } - /* Casting constructor: allows rcp -> rcp */ + /* Upcasting constructor: allows rcp -> rcp */ template rcp(const rcp & other) : ptr(static_cast(other.get_raw())) { + static_assert(std::is_base_of::value, "rcp: implicit cast must be an upcast"); if (ptr) { ptr->rcp_inc(); @@ -154,6 +156,12 @@ public: } }; +template +rcp rcp_dynamic_cast(const rcp & other) +{ + return rcp(dynamic_cast(other.get_raw())); +} + #define rcp_managed_root(classname) \ public: \ void rcp_inc() const \ diff --git a/test/tests.cpp b/test/tests.cpp index ac8a3de..dcca39a 100644 --- a/test/tests.cpp +++ b/test/tests.cpp @@ -181,6 +181,30 @@ void test_move_assignment_releases_existing() assert(mybase_destruct == destructed_before + 2); } +void test_upcast() +{ + rcp derived = MyDerived::create(1.5); + rcp base = derived; + assert(base); + assert(base->x == 1); +} + +void test_dynamic_cast_success() +{ + rcp derived = MyDerived::create(1.5); + rcp base = derived; + rcp back = rcp_dynamic_cast(base); + assert(back); + assert(back->v == 1.5); +} + +void test_dynamic_cast_failure() +{ + rcp base = MyBase::create(1, 2); + rcp derived = rcp_dynamic_cast(base); + assert(!derived); +} + int main(int argc, char * argv[]) { test_class_hierarchy(); @@ -192,5 +216,8 @@ int main(int argc, char * argv[]) test_move_constructor(); test_move_assignment(); test_move_assignment_releases_existing(); + test_upcast(); + test_dynamic_cast_success(); + test_dynamic_cast_failure(); return 0; }