From 00b6a53f7e413b710437ea70710571487615fcce Mon Sep 17 00:00:00 2001 From: Antony Polukhin Date: Mon, 10 Nov 2014 15:02:20 +0400 Subject: [PATCH] Add polymorphic_pointer_cast --- cast.htm | 28 +++++--- include/boost/polymorphic_pointer_cast.hpp | 13 +++- test/polymorphic_cast_test.cpp | 83 +++++++++++++++++++++- 3 files changed, 113 insertions(+), 11 deletions(-) diff --git a/cast.htm b/cast.htm index 4330d89..00e7d0d 100644 --- a/cast.htm +++ b/cast.htm @@ -30,7 +30,8 @@ polymorphic_cast and polymorphic_downcast function templates designed to complement the C++ built-in casts.

The header boost/polymorphic_pointer_cast.hpp provides - polymorphic_pointer_downcast function template. + polymorphic_pointer_cast and + polymorphic_pointer_downcast function templates.

The program cast_test.cpp can be used to verify these function templates work as expected.

@@ -79,7 +80,7 @@ Warning: Because polymorphic_downcast uses assert(), it violates the One Definition Rule (ODR) if NDEBUG is inconsistently defined across translation units. [See ISO Std 3.2] -

+

For crosscasts, or when the success of a cast can only be known at runtime, or when efficiency is not important, polymorphic_cast is preferred.

@@ -89,13 +90,18 @@ whether a given interface is supported; in that case a return of 0 isn't an error condition.

-

While polymorphic_downcast works with built-in pointer types only, - polymorphic_pointer_downcast is a more generic version +

While polymorphic_downcast and polymorphic_cast work with built-in pointer types only, + polymorphic_pointer_downcast and polymorphic_pointer_cast are more generic versions with support for any pointer type for which the following expressions would be valid:

-   static_pointer_cast<Derived>(p);
  dynamic_pointer_cast<Derived>(p);


- This includes C++ built-in pointers, std::shared_ptr, boost::shared_ptr, boost::intrusive_ptr, etc.

-

polymorphic_cast, polymorphic_downcast and polymorphic_pointer_downcast synopsis

+

For polymorphic_pointer_downcast:

+   static_pointer_cast<Derived>(p);
  dynamic_pointer_cast<Derived>(p);


+ +

For polymorphic_pointer_cast:

+   dynamic_pointer_cast<Derived>(p);
  !p; // conversion to bool with negation


+

This includes C++ built-in pointers, std::shared_ptr, boost::shared_ptr, boost::intrusive_ptr, etc.

+ +

polymorphic_cast, polymorphic_downcast, polymorphic_pointer_cast and polymorphic_pointer_downcast synopsis

namespace boost {
@@ -110,6 +116,11 @@ inline Derived polymorphic_downcast(Base* x);
 // Effects: assert( dynamic_cast<Derived>(x) == x );
 // Returns: static_cast<Derived>(x)
 
+template <class Derived, class Base>
+inline Derived polymorphic_pointer_cast(Base* x);
+// Throws: std::bad_cast if ( dynamic_pointer_cast<Derived>(x) == 0 )
+// Returns: dynamic_pointer_cast<Derived>(x)
+
 template <class Derived, class Base>
 inline auto polymorphic_pointer_downcast(Base x);
 // Effects: assert( dynamic_pointer_cast<Derived>(x) == x );
@@ -165,7 +176,8 @@ void f(FruitPtr fruit)
      polymorphic_downcast was contributed by Dave Abrahams.
polymorphic_pointer_downcast was contributed by Boris Rasin.
+ "http://www.boost.org/people/boris_rasin.htm">Boris Rasin and + polymorphic_pointer_cast by Antony Polukhin.
An old numeric_cast that was contributed by Kevlin Henney is now superseeded by the Boost Numeric Conversion Library

diff --git a/include/boost/polymorphic_pointer_cast.hpp b/include/boost/polymorphic_pointer_cast.hpp index bcf6d0f..4d9b46c 100644 --- a/include/boost/polymorphic_pointer_cast.hpp +++ b/include/boost/polymorphic_pointer_cast.hpp @@ -1,5 +1,5 @@ // boost polymorphic_pointer_cast.hpp header file ----------------------------------------------// -// (C) Copyright Boris Rasin 2014. +// (C) Copyright Boris Rasin and Antony Polukhin 2014. // Distributed under the Boost // Software License, Version 1.0. (See accompanying file // LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt) @@ -61,6 +61,17 @@ namespace boost return static_pointer_cast (x); } + template + inline typename detail::static_pointer_cast_result::type + polymorphic_pointer_cast (const Source& x) + { + typename detail::static_pointer_cast_result::type tmp + = dynamic_pointer_cast (x); + if ( !tmp ) boost::throw_exception( std::bad_cast() ); + + return tmp; + } + } // namespace boost #endif // BOOST_CONVERSION_POLYMORPHIC_POINTER_CAST_HPP diff --git a/test/polymorphic_cast_test.cpp b/test/polymorphic_cast_test.cpp index 9a11068..db44850 100644 --- a/test/polymorphic_cast_test.cpp +++ b/test/polymorphic_cast_test.cpp @@ -1,10 +1,11 @@ // -// Test boost::polymorphic_cast, boost::polymorphic_downcast +// Test boost::polymorphic_cast, boost::polymorphic_downcast and +// boost::polymorphic_pointer_cast, boost::polymorphic_pointer_downcast // // Copyright 1999 Beman Dawes // Copyright 1999 Dave Abrahams // Copyright 2014 Peter Dimov -// Copyright 2014 Boris Rasin +// Copyright 2014 Boris Rasin, Antony Polukhin // // Distributed under the Boost Software License, Version 1.0. // @@ -103,6 +104,67 @@ static void test_polymorphic_cast() delete base; } +static void test_polymorphic_pointer_cast() +{ + Base * base = new Derived; + + Derived * derived; + + try + { + derived = boost::polymorphic_pointer_cast( base ); + + BOOST_TEST( derived != 0 ); + + if( derived != 0 ) + { + BOOST_TEST_EQ( derived->kind(), "Derived" ); + } + } + catch( std::bad_cast const& ) + { + BOOST_ERROR( "boost::polymorphic_pointer_cast( base ) threw std::bad_cast" ); + } + + Base2 * base2; + + try + { + base2 = boost::polymorphic_pointer_cast( base ); // crosscast + + BOOST_TEST( base2 != 0 ); + + if( base2 != 0 ) + { + BOOST_TEST_EQ( base2->kind2(), "Base2" ); + } + } + catch( std::bad_cast const& ) + { + BOOST_ERROR( "boost::polymorphic_pointer_cast( base ) threw std::bad_cast" ); + } + + boost::shared_ptr sp_base( base ); + boost::shared_ptr sp_base2; + try + { + sp_base2 = boost::polymorphic_pointer_cast( sp_base ); // crosscast + + BOOST_TEST( sp_base2 != 0 ); + + if( sp_base2 != 0 ) + { + BOOST_TEST_EQ( base2->kind2(), "Base2" ); + } + } + catch( std::bad_cast const& ) + { + BOOST_ERROR( "boost::polymorphic_pointer_cast( sp_base ) threw std::bad_cast" ); + } + + // we do not `delete base;` because sahred_ptr is holding base +} + static void test_polymorphic_downcast() { Base * base = new Derived; @@ -194,6 +256,21 @@ static void test_polymorphic_cast_fail() delete base; } +static void test_polymorphic_pointer_cast_fail() +{ + Base * base = new Base; + BOOST_TEST_THROWS( boost::polymorphic_pointer_cast( base ), std::bad_cast ); + delete base; + + BOOST_TEST_THROWS( boost::polymorphic_pointer_cast( boost::shared_ptr(new Base) ), std::bad_cast ); + +#ifndef BOOST_NO_CXX11_SMART_PTR + BOOST_TEST_THROWS( boost::polymorphic_pointer_cast( std::shared_ptr(new Base) ), std::bad_cast ); +#endif + + BOOST_TEST_THROWS( boost::polymorphic_pointer_cast( boost::intrusive_ptr(new Base) ), std::bad_cast ); +} + static void test_polymorphic_downcast_fail() { Base * base = new Base; @@ -270,11 +347,13 @@ static void test_polymorphic_pointer_downcast_intrusive_fail() int main() { test_polymorphic_cast(); + test_polymorphic_pointer_cast(); test_polymorphic_downcast(); test_polymorphic_pointer_downcast_builtin(); test_polymorphic_pointer_downcast_boost_shared(); test_polymorphic_pointer_downcast_intrusive(); test_polymorphic_cast_fail(); + test_polymorphic_pointer_cast_fail(); test_polymorphic_downcast_fail(); test_polymorphic_pointer_downcast_builtin_fail(); test_polymorphic_pointer_downcast_boost_shared_fail();