diff --git a/cast.htm b/cast.htm index 208b960..d913d72 100644 --- a/cast.htm +++ b/cast.htm @@ -26,10 +26,11 @@

Cast Functions

-

The header boost/polymorphic_cast.hpp provides - polymorphic_cast and polymorphic_downcast function templates designed to - complement the C++ built-in casts.

+

The header boost/polymorphic_cast.hpp provides + polymorphic_cast, + polymorphic_downcast and + polymorphic_pointer_downcast + function templates designed to complement the C++ built-in casts.

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

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

-

polymorphic_cast and polymorphic_downcast synopsis

+

While polymorphic_downcast works with built-in pointer types only, + polymorphic_pointer_downcast is a more generic version + 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

namespace boost {
@@ -103,6 +110,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 auto polymorphic_pointer_downcast(Base x);
+// Effects: assert( dynamic_pointer_cast<Derived>(x) == x );
+// Returns: static_pointer_cast<Derived>(x)
+
 }
 
@@ -122,14 +134,40 @@ void f( Fruit * fruit ) { +

polymorphic_pointer_downcast example

+ +
+
#include <boost/polymorphic_cast.hpp>
+
+class Fruit { public: virtual ~Fruit(){} };
+class Banana : public Fruit {};
+
+// use one of these:
+
+typedef Fruit* FruitPtr;
+typedef std::shared_ptr<Fruit> FruitPtr;
+typedef boost::shared_ptr<Fruit> FruitPtr;
+typedef boost::intrusive_ptr<Fruit> FruitPtr;
+
+void f(FruitPtr fruit)
+{
+  // ... logic which leads us to believe it is a banana
+  auto banana = boost::polymorphic_pointer_downcast<Banana>(fruit);
+  ...
+}
+
+
+

History

polymorphic_cast was suggested by Bjarne Stroustrup in "The C++ Programming Language".
polymorphic_downcast was contributed by Dave Abrahams.
+ "http://www.boost.org/people/dave_abrahams.htm">Dave Abrahams.
+ polymorphic_pointer_downcast was contributed by Boris Rasin.
An old - numeric_cast
that was contributed by numeric_cast that was contributed by Kevlin Henney is now superseeded by the Boost Numeric Conversion Library


diff --git a/include/boost/polymorphic_cast.hpp b/include/boost/polymorphic_cast.hpp index 03013c7..7221ade 100644 --- a/include/boost/polymorphic_cast.hpp +++ b/include/boost/polymorphic_cast.hpp @@ -1,6 +1,7 @@ // boost polymorphic_cast.hpp header file ----------------------------------------------// // (C) Copyright Kevlin Henney and Dave Abrahams 1999. +// (C) Copyright Boris Rasin 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) @@ -8,6 +9,7 @@ // See http://www.boost.org/libs/conversion for Documentation. // Revision History +// 08 Nov 14 Add polymorphic_pointer_downcast (Boris Rasin) // 09 Jun 14 "cast.hpp" was renamed to "polymorphic_cast.hpp" and // inclusion of numeric_cast was removed (Antony Polukhin) // 23 Jun 05 numeric_cast removed and redirected to the new verion (Fernando Cacciola) @@ -47,6 +49,9 @@ # include # include +# include +# include +# include # include namespace boost @@ -86,6 +91,41 @@ namespace boost return static_cast(x); } +// polymorphic_pointer_downcast --------------------------------------------// + + // BOOST_ASSERT() checked polymorphic downcast. Crosscasts prohibited. + // Supports any type with static_pointer_cast/dynamic_pointer_cast functions: + // built-in pointers, std::shared_ptr, boost::shared_ptr, boost::intrusive_ptr, etc. + + // WARNING: Because this cast uses BOOST_ASSERT(), it violates + // the One Definition Rule if used in multiple translation units + // where BOOST_DISABLE_ASSERTS, BOOST_ENABLE_ASSERT_HANDLER + // NDEBUG are defined inconsistently. + + // Contributed by Boris Rasin + + namespace detail + { + template + struct static_pointer_cast_result + { +#ifdef BOOST_NO_CXX11_DECLTYPE + BOOST_TYPEOF_NESTED_TYPEDEF_TPL(nested, static_pointer_cast(boost::declval())) + typedef typename nested::type type; +#else + typedef decltype(static_pointer_cast(boost::declval())) type; +#endif + }; + } + + template + inline typename detail::static_pointer_cast_result::type + polymorphic_pointer_downcast (const Source& x) + { + BOOST_ASSERT(dynamic_pointer_cast (x) == x); + return static_pointer_cast (x); + } + } // namespace boost #endif // BOOST_POLYMORPHIC_CAST_HPP diff --git a/index.html b/index.html index fbfe63a..6dc9a96 100644 --- a/index.html +++ b/index.html @@ -27,8 +27,8 @@ Standard's built-in casts.

Conversion Library is supplied by several headers:

    -
  • The boost/cast header provides polymorphic_cast<> - and polymorphic_downcast<> to perform safe casting between +
  • The boost/cast header provides polymorphic_cast<>, + polymorphic_downcast<> and polymorphic_pointer_downcast<> to perform safe casting between polymorphic types.
  • The boost/lexical_cast header provides lexical_cast<> diff --git a/test/polymorphic_cast_test.cpp b/test/polymorphic_cast_test.cpp index b3f1397..9c79152 100644 --- a/test/polymorphic_cast_test.cpp +++ b/test/polymorphic_cast_test.cpp @@ -4,6 +4,7 @@ // Copyright 1999 Beman Dawes // Copyright 1999 Dave Abrahams // Copyright 2014 Peter Dimov +// Copyright 2014 Boris Rasin // // Distributed under the Boost Software License, Version 1.0. // @@ -12,8 +13,12 @@ #define BOOST_ENABLE_ASSERT_HANDLER #include +#include +#include +#include #include #include +#include static bool expect_assertion = false; static int assertion_failed_count = 0; @@ -37,7 +42,7 @@ void boost::assertion_failed( char const * expr, char const * function, char con // -struct Base +struct Base : boost::intrusive_ref_counter { virtual ~Base() {} virtual std::string kind() { return "Base"; } @@ -115,6 +120,70 @@ static void test_polymorphic_downcast() delete base; } +static void test_polymorphic_pointer_downcast_builtin() +{ + Base * base = new Derived; + + Derived * derived = boost::polymorphic_pointer_downcast( base ); + + BOOST_TEST( derived != 0 ); + + if( derived != 0 ) + { + BOOST_TEST_EQ( derived->kind(), "Derived" ); + } + + // polymorphic_pointer_downcast can't do crosscasts + + delete base; +} + +static void test_polymorphic_pointer_downcast_boost_shared() +{ + boost::shared_ptr base (new Derived); + + boost::shared_ptr derived = boost::polymorphic_pointer_downcast( base ); + + BOOST_TEST( derived != 0 ); + + if( derived != 0 ) + { + BOOST_TEST_EQ( derived->kind(), "Derived" ); + } +} + +static void test_polymorphic_pointer_downcast_intrusive() +{ + boost::intrusive_ptr base (new Derived); + + boost::intrusive_ptr derived = boost::polymorphic_pointer_downcast( base ); + + BOOST_TEST( derived != 0 ); + + if( derived != 0 ) + { + BOOST_TEST_EQ( derived->kind(), "Derived" ); + } +} + +#ifndef BOOST_NO_CXX11_SMART_PTR + +static void test_polymorphic_pointer_downcast_std_shared() +{ + std::shared_ptr base (new Derived); + + std::shared_ptr derived = boost::polymorphic_pointer_downcast( base ); + + BOOST_TEST( derived != 0 ); + + if( derived != 0 ) + { + BOOST_TEST_EQ( derived->kind(), "Derived" ); + } +} + +#endif + static void test_polymorphic_cast_fail() { Base * base = new Base; @@ -139,12 +208,81 @@ static void test_polymorphic_downcast_fail() delete base; } +static void test_polymorphic_pointer_downcast_builtin_fail() +{ + Base * base = new Base; + + int old_count = assertion_failed_count; + expect_assertion = true; + + boost::polymorphic_pointer_downcast( base ); // should assert + + BOOST_TEST_EQ( assertion_failed_count, old_count + 1 ); + expect_assertion = false; + + delete base; +} + +static void test_polymorphic_pointer_downcast_boost_shared_fail() +{ + boost::shared_ptr base (new Base); + + int old_count = assertion_failed_count; + expect_assertion = true; + + boost::polymorphic_pointer_downcast( base ); // should assert + + BOOST_TEST_EQ( assertion_failed_count, old_count + 1 ); + expect_assertion = false; +} + +#ifndef BOOST_NO_CXX11_SMART_PTR + +static void test_polymorphic_pointer_downcast_std_shared_fail() +{ + std::shared_ptr base (new Base); + + int old_count = assertion_failed_count; + expect_assertion = true; + + boost::polymorphic_pointer_downcast( base ); // should assert + + BOOST_TEST_EQ( assertion_failed_count, old_count + 1 ); + expect_assertion = false; +} + +#endif + +static void test_polymorphic_pointer_downcast_intrusive_fail() +{ + boost::intrusive_ptr base (new Base); + + int old_count = assertion_failed_count; + expect_assertion = true; + + boost::polymorphic_pointer_downcast( base ); // should assert + + BOOST_TEST_EQ( assertion_failed_count, old_count + 1 ); + expect_assertion = false; +} + int main() { test_polymorphic_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_downcast_fail(); + test_polymorphic_pointer_downcast_builtin_fail(); + test_polymorphic_pointer_downcast_boost_shared_fail(); + test_polymorphic_pointer_downcast_intrusive_fail(); + +#ifndef BOOST_NO_CXX11_SMART_PTR + test_polymorphic_pointer_downcast_std_shared(); + test_polymorphic_pointer_downcast_std_shared_fail(); +#endif return boost::report_errors(); }