Merge pull request #6 from Boris-Rasin/polymorphic_pointer_downcast

Add polymorphic_pointer_downcast function template (pull request #2)

Looks really good.
I'll move your implementation into a separate polymorphic_pointer_cast.hpp header to avoid Typeof dependencies for users that do not use `polymorphic_pointer_downcast`
This commit is contained in:
Antony Polukhin
2014-11-10 12:15:34 +03:00
4 changed files with 226 additions and 10 deletions

View File

@@ -26,10 +26,11 @@
<h2><a name="Cast Functions">Cast Functions</a></h2>
<p>The header <a href="../../boost/polymorphic_cast.hpp">boost/polymorphic_cast.hpp</a> provides <code>
<a href="#Polymorphic_cast">polymorphic_cast</a> and</code> <a href=
"#Polymorphic_cast"><code>polymorphic_downcast</code></a> function templates designed to
complement the C++ built-in casts.</p>
<p>The header <a href="../../boost/polymorphic_cast.hpp">boost/polymorphic_cast.hpp</a> provides
<code><a href="#Polymorphic_cast">polymorphic_cast</a></code>,
<code><a href="#Polymorphic_cast">polymorphic_downcast</a></code> and
<code><a href="#Polymorphic_cast">polymorphic_pointer_downcast</a></code>
function templates designed to complement the C++ built-in casts.</p>
<p>The program <a href="test/cast_test.cpp">cast_test.cpp</a> can be used to
verify these function templates work as expected.</p>
@@ -88,7 +89,13 @@
whether a given interface is supported; in that case a return of 0 isn't
an error condition.</p>
<h3>polymorphic_cast and polymorphic_downcast synopsis</h3>
<p>While <code>polymorphic_downcast</code> works with built-in pointer types only,
<code>polymorphic_pointer_downcast</code> is a more generic version
with support for any pointer type for which the following expressions would be valid:<br><br>
<code>&nbsp;&nbsp;static_pointer_cast&lt;Derived&gt;(p);<br>&nbsp;&nbsp;dynamic_pointer_cast&lt;Derived&gt;(p);</code><br><br>
This includes C++ built-in pointers, <code>std::shared_ptr, boost::shared_ptr, boost::intrusive_ptr</code>, etc.</p>
<h3>polymorphic_cast, polymorphic_downcast and polymorphic_pointer_downcast synopsis</h3>
<blockquote>
<pre>namespace boost {
@@ -103,6 +110,11 @@ inline Derived polymorphic_downcast(Base* x);
// Effects: assert( dynamic_cast&lt;Derived&gt;(x) == x );
// Returns: static_cast&lt;Derived&gt;(x)
template &lt;class Derived, class Base&gt;
inline auto polymorphic_pointer_downcast(Base x);
// Effects: assert( dynamic_pointer_cast&lt;Derived&gt;(x) == x );
// Returns: static_pointer_cast&lt;Derived&gt;(x)
}
</pre>
</blockquote>
@@ -122,14 +134,40 @@ void f( Fruit * fruit ) {
</pre>
</blockquote>
<h3>polymorphic_pointer_downcast example</h3>
<blockquote>
<pre>#include &lt;boost/polymorphic_cast.hpp&gt;
class Fruit { public: virtual ~Fruit(){} };
class Banana : public Fruit {};
// use one of these:
typedef Fruit* FruitPtr;
typedef std::shared_ptr&lt;Fruit&gt; FruitPtr;
typedef boost::shared_ptr&lt;Fruit&gt; FruitPtr;
typedef boost::intrusive_ptr&lt;Fruit&gt; FruitPtr;
void f(FruitPtr fruit)
{
// ... logic which leads us to believe it is a banana
auto banana = boost::polymorphic_pointer_downcast&lt;Banana&gt;(fruit);
...
}
</pre>
</blockquote>
<h3>History</h3>
<p><code>polymorphic_cast</code> was suggested by Bjarne Stroustrup in "The C++
Programming Language".<br>
<code>polymorphic_downcast</code> was contributed by <a href=
"http://www.boost.org/people/dave_abrahams.htm">Dave Abrahams</a>.<code><br>
"http://www.boost.org/people/dave_abrahams.htm">Dave Abrahams</a>.<br>
<code>polymorphic_pointer_downcast</code> was contributed by <a href=
"http://www.boost.org/people/boris_rasin.htm">Boris Rasin</a>.<br>
An old
numeric_cast</code> that was contributed by <a href=
<code>numeric_cast</code> that was contributed by <a href=
"http://www.boost.org/people/kevlin_henney.htm">Kevlin Henney</a> is now superseeded by the <a href="../numeric/conversion/doc/html/index.html">Boost Numeric Conversion Library</a></p>
<hr>

View File

@@ -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 <boost/config.hpp>
# include <boost/assert.hpp>
# include <boost/pointer_cast.hpp>
# include <boost/utility/declval.hpp>
# include <boost/typeof/typeof.hpp>
# include <typeinfo>
namespace boost
@@ -86,6 +91,41 @@ namespace boost
return static_cast<Target>(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 <typename Target, typename Source>
struct static_pointer_cast_result
{
#ifdef BOOST_NO_CXX11_DECLTYPE
BOOST_TYPEOF_NESTED_TYPEDEF_TPL(nested, static_pointer_cast<Target>(boost::declval<Source>()))
typedef typename nested::type type;
#else
typedef decltype(static_pointer_cast<Target>(boost::declval<Source>())) type;
#endif
};
}
template <typename Target, typename Source>
inline typename detail::static_pointer_cast_result<Target, Source>::type
polymorphic_pointer_downcast (const Source& x)
{
BOOST_ASSERT(dynamic_pointer_cast<Target> (x) == x);
return static_pointer_cast<Target> (x);
}
} // namespace boost
#endif // BOOST_POLYMORPHIC_CAST_HPP

View File

@@ -27,8 +27,8 @@ Standard's built-in casts.</p>
Conversion Library is
supplied by several headers:</p>
<ul>
<li>The <a href="cast.htm">boost/cast</a> header provides <b>polymorphic_cast&lt;&gt;</b>
and <b>polymorphic_downcast&lt;&gt;</b> to perform safe casting between
<li>The <a href="cast.htm">boost/cast</a> header provides <b>polymorphic_cast&lt;&gt;</b>,
<b>polymorphic_downcast&lt;&gt;</b> and <b>polymorphic_pointer_downcast&lt;&gt;</b> to perform safe casting between
polymorphic types.<br>
</li>
<li>The <a href="../../doc/html/boost_lexical_cast.html">boost/lexical_cast</a> header provides <b>lexical_cast&lt;&gt;</b>

View File

@@ -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 <boost/polymorphic_cast.hpp>
#include <boost/smart_ptr/shared_ptr.hpp>
#include <boost/smart_ptr/intrusive_ptr.hpp>
#include <boost/smart_ptr/intrusive_ref_counter.hpp>
#include <boost/core/lightweight_test.hpp>
#include <string>
#include <memory>
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<Base>
{
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<Derived>( 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> base (new Derived);
boost::shared_ptr<Derived> derived = boost::polymorphic_pointer_downcast<Derived>( 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> base (new Derived);
boost::intrusive_ptr<Derived> derived = boost::polymorphic_pointer_downcast<Derived>( 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> base (new Derived);
std::shared_ptr<Derived> derived = boost::polymorphic_pointer_downcast<Derived>( 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<Derived>( 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> base (new Base);
int old_count = assertion_failed_count;
expect_assertion = true;
boost::polymorphic_pointer_downcast<Derived>( 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> base (new Base);
int old_count = assertion_failed_count;
expect_assertion = true;
boost::polymorphic_pointer_downcast<Derived>( 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> base (new Base);
int old_count = assertion_failed_count;
expect_assertion = true;
boost::polymorphic_pointer_downcast<Derived>( 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();
}