From 53a4d91a23af243397128775bb770fc9eef293c3 Mon Sep 17 00:00:00 2001 From: ADAM David Alan Martin Date: Sun, 31 Mar 2024 10:33:03 -0400 Subject: [PATCH] Add a unique pointer dynamic cast utility. --- Utility/CMakeLists.txt | 1 + Utility/derived_pointer_cast.h | 41 +++++++++++++ Utility/derived_pointer_cast.test/0.cc | 58 +++++++++++++++++++ .../derived_pointer_cast.test/CMakeLists.txt | 1 + 4 files changed, 101 insertions(+) create mode 100644 Utility/derived_pointer_cast.h create mode 100644 Utility/derived_pointer_cast.test/0.cc create mode 100644 Utility/derived_pointer_cast.test/CMakeLists.txt diff --git a/Utility/CMakeLists.txt b/Utility/CMakeLists.txt index 68244fa..e59a3c6 100644 --- a/Utility/CMakeLists.txt +++ b/Utility/CMakeLists.txt @@ -2,3 +2,4 @@ target_sources( alepha PRIVATE fancyTypeName.cc ) +add_subdirectory( derived_pointer_cast.test ) diff --git a/Utility/derived_pointer_cast.h b/Utility/derived_pointer_cast.h new file mode 100644 index 0000000..f112d98 --- /dev/null +++ b/Utility/derived_pointer_cast.h @@ -0,0 +1,41 @@ +static_assert( __cplusplus > 2020'99 ); + +#pragma once + +#include + +#include + +namespace Alepha::Hydrogen::Utility ::detail:: derived_pointer_cast_m +{ + inline namespace exports + { + template< typename Target, typename Source > + std::unique_ptr< Target > derived_pointer_cast( std::unique_ptr< Source > source ); + } + + template< typename Target, typename Source > + std::unique_ptr< Target > + exports::derived_pointer_cast( std::unique_ptr< Source > source ) + { + if( Target *const conv= dynamic_cast< Target * >( source.get() ); not conv ) + { + // TODO: More precise Alepha exception here. + throw std::bad_cast{}; + } + else + { + // It's safe to release here, because we're taking ownership below. + source.release(); + + // We reuse the conversion pointer so we can avoid a double-trip thru the + // RTTI pathway. + return std::unique_ptr< Target >{ conv }; + } + } +} + +namespace Alepha::Hydrogen::Utility::inline exports::inline derived_pointer_cast_m +{ + using namespace detail::derived_pointer_cast_m::exports; +} diff --git a/Utility/derived_pointer_cast.test/0.cc b/Utility/derived_pointer_cast.test/0.cc new file mode 100644 index 0000000..5cdfcca --- /dev/null +++ b/Utility/derived_pointer_cast.test/0.cc @@ -0,0 +1,58 @@ +static_assert( __cplusplus > 2020'99 ); + +#include "../derived_pointer_cast.h" + +#include +#include + +#include + +static auto init= Alepha::Utility::enroll <=[] +{ + using namespace Alepha::Testing::exports; + + struct Base + { + virtual ~Base()= default; + }; + + struct Derived : Base {}; + + struct Derived2 : Base {}; + + "Can `derived_pointer_cast` work on simple cases?"_test <=TableTest + < + []( std::function< std::unique_ptr< Base >() > makeBase ) + { + auto base= makeBase(); + return static_cast< bool >( Alepha::Utility::derived_pointer_cast< Derived >( std::move( base ) ) ); + } + > + ::Cases + { + { "Is derived", { [] { return std::make_unique< Derived >(); } }, true }, + { "Not derived", { [] { return std::make_unique< Base >(); } }, std::type_identity< std::bad_cast >{} }, + { "Other derived", { [] { return std::make_unique< Derived2 >(); } }, std::type_identity< std::bad_cast >{} }, + }; + + struct Alien + { + virtual ~Alien()= default; + }; + + struct AlienDerived : Alien {}; + + "Alien Test"_test <=TableTest + < + []( std::function< std::unique_ptr< Alien >() > makeBase ) + { + auto base= makeBase(); + return static_cast< bool >( Alepha::Utility::derived_pointer_cast< Derived >( std::move( base ) ) ); + } + > + ::Cases + { + { "Alien", { [] { return std::make_unique< Alien >(); } }, std::type_identity< std::bad_cast >{} }, + { "Alien Derived", { [] { return std::make_unique< AlienDerived >(); } }, std::type_identity< std::bad_cast >{} }, + }; +}; diff --git a/Utility/derived_pointer_cast.test/CMakeLists.txt b/Utility/derived_pointer_cast.test/CMakeLists.txt new file mode 100644 index 0000000..b099603 --- /dev/null +++ b/Utility/derived_pointer_cast.test/CMakeLists.txt @@ -0,0 +1 @@ +unit_test( 0 )