From 6a224fa5e9c972925fc6cff00619ea8c5a18b373 Mon Sep 17 00:00:00 2001 From: Boris Rasin Date: Sun, 9 Nov 2014 00:02:44 +0200 Subject: [PATCH 1/2] =add polymorphic_pointer_downcast function template --- cast.htm | 28 +++++++++++++++------ include/boost/polymorphic_cast.hpp | 40 ++++++++++++++++++++++++++++++ index.html | 4 +-- test/polymorphic_cast_test.cpp | 36 +++++++++++++++++++++++++++ 4 files changed, 99 insertions(+), 9 deletions(-) diff --git a/cast.htm b/cast.htm index 208b960..860ac17 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)
+
 }
 
@@ -127,9 +139,11 @@ void f( Fruit * fruit ) {

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..b8a500c 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. // @@ -115,6 +116,24 @@ static void test_polymorphic_downcast() delete base; } +static void test_polymorphic_pointer_downcast() +{ + 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_cast_fail() { Base * base = new Base; @@ -139,12 +158,29 @@ static void test_polymorphic_downcast_fail() delete base; } +static void test_polymorphic_pointer_downcast_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; +} + int main() { test_polymorphic_cast(); test_polymorphic_downcast(); + test_polymorphic_pointer_downcast(); test_polymorphic_cast_fail(); test_polymorphic_downcast_fail(); + test_polymorphic_pointer_downcast_fail(); return boost::report_errors(); } From 582fc91adb10fa4c6ea124d36b34cbfdfc760a45 Mon Sep 17 00:00:00 2001 From: Boris Rasin Date: Sun, 9 Nov 2014 15:37:56 +0200 Subject: [PATCH 2/2] add more tests and documentation example for polymorphic_pointer_downcast --- cast.htm | 24 +++++++ test/polymorphic_cast_test.cpp | 112 +++++++++++++++++++++++++++++++-- 2 files changed, 131 insertions(+), 5 deletions(-) diff --git a/cast.htm b/cast.htm index 860ac17..d913d72 100644 --- a/cast.htm +++ b/cast.htm @@ -134,6 +134,30 @@ 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++ diff --git a/test/polymorphic_cast_test.cpp b/test/polymorphic_cast_test.cpp index b8a500c..9c79152 100644 --- a/test/polymorphic_cast_test.cpp +++ b/test/polymorphic_cast_test.cpp @@ -13,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; @@ -38,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"; } @@ -116,7 +120,7 @@ static void test_polymorphic_downcast() delete base; } -static void test_polymorphic_pointer_downcast() +static void test_polymorphic_pointer_downcast_builtin() { Base * base = new Derived; @@ -134,6 +138,52 @@ static void test_polymorphic_pointer_downcast() 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; @@ -158,7 +208,7 @@ static void test_polymorphic_downcast_fail() delete base; } -static void test_polymorphic_pointer_downcast_fail() +static void test_polymorphic_pointer_downcast_builtin_fail() { Base * base = new Base; @@ -173,14 +223,66 @@ static void test_polymorphic_pointer_downcast_fail() 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(); + 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_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(); }