Skip to content

Commit 9c5ff08

Browse files
committed
Added dynamic_pointer_cast
1 parent 299cea9 commit 9c5ff08

File tree

2 files changed

+274
-0
lines changed

2 files changed

+274
-0
lines changed

include/oup/observable_unique_ptr.hpp

Lines changed: 57 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -1490,6 +1490,63 @@ observer_ptr<U> const_pointer_cast(observer_ptr<T>&& ptr) {
14901490
return observer_ptr<U>(std::move(ptr), const_cast<U*>(ptr.raw_get()));
14911491
}
14921492

1493+
/// Perform a `dynamic_cast` for an `observable_unique_ptr`.
1494+
/** \param ptr The pointer to cast
1495+
* \note Ownership will be transfered to the returned pointer unless the cast
1496+
* fails, in which case ownership remains in the original pointer, std::bad_cast
1497+
* is thrown, and no memory is leaked. If the input pointer is null,
1498+
* the output pointer will also be null.
1499+
*/
1500+
template<typename U, typename T>
1501+
observable_unique_ptr<U> dynamic_pointer_cast(observable_unique_ptr<T>&& ptr) {
1502+
if (ptr == nullptr) {
1503+
return observable_unique_ptr<U>{};
1504+
}
1505+
1506+
U& casted_object = dynamic_cast<U&>(*ptr.get());
1507+
return observable_unique_ptr<U>(std::move(ptr), &casted_object);
1508+
}
1509+
1510+
/// Perform a `dynamic_cast` for an `observable_unique_ptr`.
1511+
/** \param ptr The pointer to cast
1512+
* \note Ownership will be transfered to the returned pointer unless the cast
1513+
* fails, in which case ownership remains in the original pointer, and
1514+
* no memory is leaked.
1515+
*/
1516+
template<typename U, typename T>
1517+
observable_sealed_ptr<U> dynamic_pointer_cast(observable_sealed_ptr<T>&& ptr) {
1518+
if (ptr == nullptr) {
1519+
return observable_sealed_ptr<U>{};
1520+
}
1521+
1522+
U& casted_object = dynamic_cast<U&>(*ptr.get());
1523+
return observable_sealed_ptr<U>(std::move(ptr), &casted_object);
1524+
}
1525+
1526+
/// Perform a `dynamic_cast` for an `observer_ptr`.
1527+
/** \param ptr The pointer to cast
1528+
* \note A new observer is returned, the input observer is not modified.
1529+
If the input pointer is null, or if the cast fails, the output pointer
1530+
will be null.
1531+
*/
1532+
template<typename U, typename T>
1533+
observer_ptr<U> dynamic_pointer_cast(const observer_ptr<T>& ptr) {
1534+
// NB: must use get() as dynamic cast of an expired pointer is UB
1535+
return observer_ptr<U>(ptr, dynamic_cast<U*>(ptr.get()));
1536+
}
1537+
1538+
/// Perform a `dynamic_cast` for an `observer_ptr`.
1539+
/** \param ptr The pointer to cast
1540+
* \note A new observer is returned, the input observer is set to null.
1541+
If the input pointer is null, or if the cast fails, the output pointer
1542+
will be null.
1543+
*/
1544+
template<typename U, typename T>
1545+
observer_ptr<U> dynamic_pointer_cast(observer_ptr<T>&& ptr) {
1546+
// NB: must use get() as dynamic cast of an expired pointer is UB
1547+
return observer_ptr<U>(std::move(ptr), dynamic_cast<U*>(ptr.get()));
1548+
}
1549+
14931550
}
14941551

14951552
#endif

tests/runtime_tests.cpp

Lines changed: 217 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -3767,3 +3767,220 @@ TEST_CASE("const pointer cast observer move from null", "[pointer_cast]") {
37673767
REQUIRE(mem_track.leaks() == 0u);
37683768
REQUIRE(mem_track.double_del() == 0u);
37693769
}
3770+
3771+
TEST_CASE("dynamic pointer cast unique from valid", "[pointer_cast]") {
3772+
memory_tracker mem_track;
3773+
3774+
{
3775+
test_object_derived* raw_ptr = new test_object_derived;
3776+
test_ptr ptr_orig{raw_ptr};
3777+
test_ptr_derived ptr = oup::dynamic_pointer_cast<test_object_derived>(std::move(ptr_orig));
3778+
3779+
REQUIRE(instances == 1);
3780+
REQUIRE(ptr_orig == nullptr);
3781+
REQUIRE(ptr.get() == raw_ptr);
3782+
}
3783+
3784+
REQUIRE(instances == 0);
3785+
REQUIRE(mem_track.leaks() == 0u);
3786+
REQUIRE(mem_track.double_del() == 0u);
3787+
}
3788+
3789+
TEST_CASE("dynamic pointer cast unique from invalid", "[pointer_cast]") {
3790+
memory_tracker mem_track;
3791+
3792+
{
3793+
test_ptr ptr_orig{new test_object_observer_from_this};
3794+
3795+
REQUIRE_THROWS_AS(
3796+
oup::dynamic_pointer_cast<test_object_derived>(std::move(ptr_orig)),
3797+
std::bad_cast);
3798+
3799+
REQUIRE(instances == 1);
3800+
REQUIRE(ptr_orig != nullptr);
3801+
}
3802+
3803+
REQUIRE(instances == 0);
3804+
REQUIRE(mem_track.leaks() == 0u);
3805+
REQUIRE(mem_track.double_del() == 0u);
3806+
}
3807+
3808+
TEST_CASE("dynamic pointer cast unique from null", "[pointer_cast]") {
3809+
memory_tracker mem_track;
3810+
3811+
{
3812+
test_ptr ptr_orig;
3813+
test_ptr_derived ptr = oup::dynamic_pointer_cast<test_object_derived>(std::move(ptr_orig));
3814+
3815+
REQUIRE(instances == 0);
3816+
REQUIRE(ptr_orig == nullptr);
3817+
REQUIRE(ptr == nullptr);
3818+
}
3819+
3820+
REQUIRE(instances == 0);
3821+
REQUIRE(mem_track.leaks() == 0u);
3822+
REQUIRE(mem_track.double_del() == 0u);
3823+
}
3824+
3825+
TEST_CASE("dynamic pointer cast sealed from valid", "[pointer_cast]") {
3826+
memory_tracker mem_track;
3827+
3828+
{
3829+
test_sptr_derived ptr_init = oup::make_observable_sealed<test_object_derived>();
3830+
test_object_derived* raw_ptr = ptr_init.get();
3831+
test_sptr ptr_orig{std::move(ptr_init)};
3832+
test_sptr_derived ptr = oup::dynamic_pointer_cast<test_object_derived>(std::move(ptr_orig));
3833+
3834+
REQUIRE(instances == 1);
3835+
REQUIRE(ptr_orig == nullptr);
3836+
REQUIRE(ptr.get() == raw_ptr);
3837+
}
3838+
3839+
REQUIRE(instances == 0);
3840+
REQUIRE(mem_track.leaks() == 0u);
3841+
REQUIRE(mem_track.double_del() == 0u);
3842+
}
3843+
3844+
TEST_CASE("dynamic pointer cast sealed from invalid", "[pointer_cast]") {
3845+
memory_tracker mem_track;
3846+
3847+
{
3848+
test_sptr ptr_orig{oup::make_observable_sealed<test_object_observer_from_this>()};
3849+
3850+
REQUIRE_THROWS_AS(
3851+
oup::dynamic_pointer_cast<test_object_derived>(std::move(ptr_orig)),
3852+
std::bad_cast);
3853+
3854+
REQUIRE(instances == 1);
3855+
REQUIRE(ptr_orig != nullptr);
3856+
}
3857+
3858+
REQUIRE(instances == 0);
3859+
REQUIRE(mem_track.leaks() == 0u);
3860+
REQUIRE(mem_track.double_del() == 0u);
3861+
}
3862+
3863+
TEST_CASE("dynamic pointer cast sealed from null", "[pointer_cast]") {
3864+
memory_tracker mem_track;
3865+
3866+
{
3867+
test_sptr ptr_orig;
3868+
test_sptr_derived ptr = oup::dynamic_pointer_cast<test_object_derived>(std::move(ptr_orig));
3869+
3870+
REQUIRE(instances == 0);
3871+
REQUIRE(ptr_orig == nullptr);
3872+
REQUIRE(ptr == nullptr);
3873+
}
3874+
3875+
REQUIRE(instances == 0);
3876+
REQUIRE(mem_track.leaks() == 0u);
3877+
REQUIRE(mem_track.double_del() == 0u);
3878+
}
3879+
3880+
TEST_CASE("dynamic pointer cast observer copy from valid", "[pointer_cast]") {
3881+
memory_tracker mem_track;
3882+
3883+
{
3884+
test_sptr_derived ptr_owner = oup::make_observable_sealed<test_object_derived>();
3885+
test_object_derived* raw_ptr = ptr_owner.get();
3886+
test_optr ptr_orig{ptr_owner};
3887+
test_optr_derived ptr = oup::dynamic_pointer_cast<test_object_derived>(ptr_orig);
3888+
3889+
REQUIRE(instances == 1);
3890+
REQUIRE(ptr_orig.get() == raw_ptr);
3891+
REQUIRE(ptr.get() == raw_ptr);
3892+
}
3893+
3894+
REQUIRE(instances == 0);
3895+
REQUIRE(mem_track.leaks() == 0u);
3896+
REQUIRE(mem_track.double_del() == 0u);
3897+
}
3898+
3899+
TEST_CASE("dynamic pointer cast observer copy from invalid", "[pointer_cast]") {
3900+
memory_tracker mem_track;
3901+
3902+
{
3903+
test_sptr_from_this ptr_owner = oup::make_observable_sealed<test_object_observer_from_this>();
3904+
test_optr ptr_orig{ptr_owner};
3905+
test_optr_derived ptr = oup::dynamic_pointer_cast<test_object_derived>(ptr_orig);
3906+
3907+
REQUIRE(instances == 1);
3908+
REQUIRE(ptr.get() == nullptr);
3909+
REQUIRE(ptr_orig.get() != nullptr);
3910+
}
3911+
3912+
REQUIRE(instances == 0);
3913+
REQUIRE(mem_track.leaks() == 0u);
3914+
REQUIRE(mem_track.double_del() == 0u);
3915+
}
3916+
3917+
TEST_CASE("dynamic pointer cast observer copy from null", "[pointer_cast]") {
3918+
memory_tracker mem_track;
3919+
3920+
{
3921+
test_optr ptr_orig;
3922+
test_optr_derived ptr = oup::dynamic_pointer_cast<test_object_derived>(ptr_orig);
3923+
3924+
REQUIRE(instances == 0);
3925+
REQUIRE(ptr_orig == nullptr);
3926+
REQUIRE(ptr == nullptr);
3927+
}
3928+
3929+
REQUIRE(instances == 0);
3930+
REQUIRE(mem_track.leaks() == 0u);
3931+
REQUIRE(mem_track.double_del() == 0u);
3932+
}
3933+
3934+
TEST_CASE("dynamic pointer cast observer move from valid", "[pointer_cast]") {
3935+
memory_tracker mem_track;
3936+
3937+
{
3938+
test_sptr_derived ptr_owner = oup::make_observable_sealed<test_object_derived>();
3939+
test_object_derived* raw_ptr = ptr_owner.get();
3940+
test_optr ptr_orig{ptr_owner};
3941+
test_optr_derived ptr = oup::dynamic_pointer_cast<test_object_derived>(std::move(ptr_orig));
3942+
3943+
REQUIRE(instances == 1);
3944+
REQUIRE(ptr_orig == nullptr);
3945+
REQUIRE(ptr.get() == raw_ptr);
3946+
}
3947+
3948+
REQUIRE(instances == 0);
3949+
REQUIRE(mem_track.leaks() == 0u);
3950+
REQUIRE(mem_track.double_del() == 0u);
3951+
}
3952+
3953+
TEST_CASE("dynamic pointer cast observer move from invalid", "[pointer_cast]") {
3954+
memory_tracker mem_track;
3955+
3956+
{
3957+
test_sptr_from_this ptr_owner = oup::make_observable_sealed<test_object_observer_from_this>();
3958+
test_optr ptr_orig{ptr_owner};
3959+
test_optr_derived ptr = oup::dynamic_pointer_cast<test_object_derived>(std::move(ptr_orig));
3960+
3961+
REQUIRE(instances == 1);
3962+
REQUIRE(ptr.get() == nullptr);
3963+
REQUIRE(ptr_orig.get() == nullptr);
3964+
}
3965+
3966+
REQUIRE(instances == 0);
3967+
REQUIRE(mem_track.leaks() == 0u);
3968+
REQUIRE(mem_track.double_del() == 0u);
3969+
}
3970+
3971+
TEST_CASE("dynamic pointer cast observer move from null", "[pointer_cast]") {
3972+
memory_tracker mem_track;
3973+
3974+
{
3975+
test_optr ptr_orig;
3976+
test_optr_derived ptr = oup::dynamic_pointer_cast<test_object_derived>(std::move(ptr_orig));
3977+
3978+
REQUIRE(instances == 0);
3979+
REQUIRE(ptr_orig == nullptr);
3980+
REQUIRE(ptr == nullptr);
3981+
}
3982+
3983+
REQUIRE(instances == 0);
3984+
REQUIRE(mem_track.leaks() == 0u);
3985+
REQUIRE(mem_track.double_del() == 0u);
3986+
}

0 commit comments

Comments
 (0)