168 changes: 168 additions & 0 deletions include/boost/variant/polymorphic_get.hpp
@@ -0,0 +1,168 @@
//-----------------------------------------------------------------------------
// boost variant/polymorphic_get.hpp header file
// See http://www.boost.org for updates, documentation, and revision history.
//-----------------------------------------------------------------------------
//
// Copyright (c) 2013 Antony Polukhin
//
// 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)

#ifndef BOOST_VARIANT_POLYMORPHIC_GET_HPP
#define BOOST_VARIANT_POLYMORPHIC_GET_HPP

#include <exception>

#include "boost/config.hpp"
#include "boost/detail/workaround.hpp"
#include "boost/throw_exception.hpp"
#include "boost/utility/addressof.hpp"
#include "boost/variant/variant_fwd.hpp"
#include "boost/variant/get.hpp"

#include "boost/type_traits/add_reference.hpp"
#include "boost/type_traits/add_pointer.hpp"
#include "boost/type_traits/is_base_of.hpp"

namespace boost {

//////////////////////////////////////////////////////////////////////////
// class bad_polymorphic_get
//
// The exception thrown in the event of a failed get of a value.
//
class bad_polymorphic_get
: public bad_get
{
public: // std::exception implementation

virtual const char * what() const BOOST_NOEXCEPT_OR_NOTHROW
{
return "boost::bad_polymorphic_get: "
"failed value get using boost::polymorphic_get";
}

};

//////////////////////////////////////////////////////////////////////////
// function template get<T>
//
// Retrieves content of given variant object if content is of type T.
// Otherwise: pointer ver. returns 0; reference ver. throws bad_get.
//

namespace detail { namespace variant {

// (detail) class template get_polymorphic_visitor
//
// Generic static visitor that: if the value is of the specified
// type or of a type derived from specified, returns a pointer
// to the value it visits; else a null pointer.
//
template <typename Base>
struct get_polymorphic_visitor
{
private: // private typedefs
typedef typename add_pointer<Base>::type pointer;
typedef typename add_reference<Base>::type reference;

pointer get(reference operand, boost::true_type) const BOOST_NOEXCEPT
{
return boost::addressof(operand);
}

template <class T>
pointer get(T&, boost::false_type) const BOOST_NOEXCEPT
{
return static_cast<pointer>(0);
}

public: // visitor interfaces
typedef pointer result_type;

template <typename U>
pointer operator()(U& operand) const BOOST_NOEXCEPT
{
typedef boost::integral_constant<
bool,
boost::is_base_of<Base, U>::value || boost::is_same<Base, U>::value
> tag_t;
return get(operand, tag_t());
}
};

}} // namespace detail::variant

#if !BOOST_WORKAROUND(__BORLANDC__, BOOST_TESTED_AT(0x0551))
# define BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE(t)
#else
# define BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE(t) \
, t* = 0
#endif

template <typename U, BOOST_VARIANT_ENUM_PARAMS(typename T) >
inline
typename add_pointer<U>::type
polymorphic_get(
boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >* operand
BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE(U)
) BOOST_NOEXCEPT
{
typedef typename add_pointer<U>::type U_ptr;
if (!operand) return static_cast<U_ptr>(0);

detail::variant::get_polymorphic_visitor<U> v;
return operand->apply_visitor(v);
}

template <typename U, BOOST_VARIANT_ENUM_PARAMS(typename T) >
inline
typename add_pointer<const U>::type
polymorphic_get(
const boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >* operand
BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE(U)
) BOOST_NOEXCEPT
{
typedef typename add_pointer<const U>::type U_ptr;
if (!operand) return static_cast<U_ptr>(0);

detail::variant::get_polymorphic_visitor<const U> v;
return operand->apply_visitor(v);
}

template <typename U, BOOST_VARIANT_ENUM_PARAMS(typename T) >
inline
typename add_reference<U>::type
polymorphic_get(
boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >& operand
BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE(U)
)
{
typedef typename add_pointer<U>::type U_ptr;
U_ptr result = polymorphic_get<U>(&operand);

if (!result)
boost::throw_exception(bad_polymorphic_get());
return *result;
}

template <typename U, BOOST_VARIANT_ENUM_PARAMS(typename T) >
inline
typename add_reference<const U>::type
polymorphic_get(
const boost::variant< BOOST_VARIANT_ENUM_PARAMS(T) >& operand
BOOST_VARIANT_AUX_GET_EXPLICIT_TEMPLATE_TYPE(U)
)
{
typedef typename add_pointer<const U>::type U_ptr;
U_ptr result = polymorphic_get<const U>(&operand);

if (!result)
boost::throw_exception(bad_polymorphic_get());
return *result;
}

} // namespace boost

#endif // BOOST_VARIANT_POLYMORPHIC_GET_HPP
6 changes: 3 additions & 3 deletions include/boost/variant/recursive_variant.hpp
Expand Up @@ -116,7 +116,7 @@ struct substitute<
BOOST_MPL_AUX_LAMBDA_ARITY_PARAM(Arity)
>
{
#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && defined(BOOST_VARIANT_USE_VARIADIC_TEMPLATES)
#if !defined(BOOST_VARIANT_DO_NOT_USE_VARIADIC_TEMPLATES)

typedef ::boost::variant<
typename enable_recursive<
Expand All @@ -131,7 +131,7 @@ struct substitute<
>::type...
> type;

#else
#else // defined(BOOST_VARIANT_DO_NOT_USE_VARIADIC_TEMPLATES)

private: // helpers, for metafunction result (below)

Expand All @@ -154,7 +154,7 @@ struct substitute<
public: // metafunction result

typedef ::boost::variant< BOOST_VARIANT_ENUM_PARAMS(wknd_T) > type;
#endif
#endif // BOOST_VARIANT_DO_NOT_USE_VARIADIC_TEMPLATES workaround
};

#else // defined(BOOST_VARIANT_DETAIL_NO_SUBSTITUTE)
Expand Down
64 changes: 43 additions & 21 deletions include/boost/variant/variant_fwd.hpp
Expand Up @@ -25,16 +25,6 @@
#include "boost/preprocessor/enum_shifted_params.hpp"
#include "boost/preprocessor/repeat.hpp"

///////////////////////////////////////////////////////////////////////////////
// macro BOOST_VARIANT_LIMIT_TYPES
//
// Implementation-defined preprocessor symbol describing the actual
// length of variant's pseudo-variadic template parameter list.
//
#include "boost/mpl/limits/list.hpp"
#define BOOST_VARIANT_LIMIT_TYPES \
BOOST_MPL_LIMIT_LIST_SIZE

///////////////////////////////////////////////////////////////////////////////
// macro BOOST_VARIANT_NO_REFERENCE_SUPPORT
//
Expand Down Expand Up @@ -67,11 +57,34 @@
#include "boost/variant/detail/substitute_fwd.hpp"

#if defined(BOOST_VARIANT_DETAIL_NO_SUBSTITUTE) \
&& !defined(BOOST_VARIANT_NO_FULL_RECURSIVE_VARIANT_SUPPORT)
&& !defined(BOOST_VARIANT_NO_FULL_RECURSIVE_VARIANT_SUPPORT)
# define BOOST_VARIANT_NO_FULL_RECURSIVE_VARIANT_SUPPORT
#endif

#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && defined(BOOST_VARIANT_USE_VARIADIC_TEMPLATES)

///////////////////////////////////////////////////////////////////////////////
// macro BOOST_VARIANT_DO_NOT_USE_VARIADIC_TEMPLATES
//

/*
GCC before 4.0 had no variadic tempaltes;
GCC 4.6 has incomplete implementation of variadic templates.
MSVC2013 has variadic templates, but they have issues.
*/
#if defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) \
|| (defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ < 7)) \
|| (defined(_MSC_VER) && (_MSC_VER <= 1800)) \
|| defined(BOOST_NO_USING_DECLARATION_OVERLOADS_FROM_TYPENAME_BASE) \
|| defined (BOOST_VARIANT_NO_TYPE_SEQUENCE_SUPPORT)

#ifndef BOOST_VARIANT_DO_NOT_USE_VARIADIC_TEMPLATES
# define BOOST_VARIANT_DO_NOT_USE_VARIADIC_TEMPLATES
#endif

#endif

#if !defined(BOOST_VARIANT_DO_NOT_USE_VARIADIC_TEMPLATES)
#include <boost/preprocessor/seq/size.hpp>

#define BOOST_VARIANT_CLASS_OR_TYPENAME_TO_SEQ_class class)(
Expand Down Expand Up @@ -104,7 +117,7 @@
// Rationale: Cleaner, simpler code for clients of variant library. Minimal
// code modifications to move from C++03 to C++11.
//
// Without !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && defined(BOOST_VARIANT_USE_VARIADIC_TEMPLATES)
// With BOOST_VARIANT_DO_NOT_USE_VARIADIC_TEMPLATES defined
// will be used BOOST_VARIANT_ENUM_PARAMS and BOOST_VARIANT_ENUM_SHIFTED_PARAMS from below `#else`
//

Expand All @@ -117,7 +130,17 @@
BOOST_VARIANT_MAKE_VARIADIC( (BOOST_VARIANT_CLASS_OR_TYPENAME_TO_SEQ_ ## x), x) \
/**/

#else
#else // defined(BOOST_VARIANT_DO_NOT_USE_VARIADIC_TEMPLATES)

///////////////////////////////////////////////////////////////////////////////
// macro BOOST_VARIANT_LIMIT_TYPES
//
// Implementation-defined preprocessor symbol describing the actual
// length of variant's pseudo-variadic template parameter list.
//
#include "boost/mpl/limits/list.hpp"
#define BOOST_VARIANT_LIMIT_TYPES \
BOOST_MPL_LIMIT_LIST_SIZE

///////////////////////////////////////////////////////////////////////////////
// macro BOOST_VARIANT_RECURSIVE_VARIANT_MAX_ARITY
Expand Down Expand Up @@ -148,7 +171,8 @@
#define BOOST_VARIANT_ENUM_SHIFTED_PARAMS( param ) \
BOOST_PP_ENUM_SHIFTED_PARAMS(BOOST_VARIANT_LIMIT_TYPES, param)

#endif // BOOST_NO_CXX11_VARIADIC_TEMPLATES
#endif // BOOST_VARIANT_DO_NOT_USE_VARIADIC_TEMPLATES workaround


namespace boost {

Expand Down Expand Up @@ -188,7 +212,6 @@ struct convert_void< void_ >
//

#if defined(BOOST_NO_USING_DECLARATION_OVERLOADS_FROM_TYPENAME_BASE)

// (detail) tags voidNN -- NN defined on [0, BOOST_VARIANT_LIMIT_TYPES)
//
// Defines void types that are each unique and specializations of
Expand Down Expand Up @@ -217,11 +240,10 @@ BOOST_PP_REPEAT(

}} // namespace detail::variant

#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && defined(BOOST_VARIANT_USE_VARIADIC_TEMPLATES)
#if !defined(BOOST_VARIANT_DO_NOT_USE_VARIADIC_TEMPLATES)
# define BOOST_VARIANT_AUX_DECLARE_PARAMS BOOST_VARIANT_ENUM_PARAMS(typename T)
#else // defined(BOOST_VARIANT_DO_NOT_USE_VARIADIC_TEMPLATES)

#define BOOST_VARIANT_AUX_DECLARE_PARAMS BOOST_VARIANT_ENUM_PARAMS(typename T)

#else
///////////////////////////////////////////////////////////////////////////////
// (detail) macro BOOST_VARIANT_AUX_DECLARE_PARAM
//
Expand Down Expand Up @@ -250,7 +272,7 @@ BOOST_PP_REPEAT(
) \
/**/

#endif // BOOST_NO_CXX11_VARIADIC_TEMPLATES
#endif // BOOST_VARIANT_DO_NOT_USE_VARIADIC_TEMPLATES workaround

///////////////////////////////////////////////////////////////////////////////
// class template variant (concept inspired by Andrei Alexandrescu)
Expand Down
19 changes: 13 additions & 6 deletions perf/move_perf.cpp
@@ -1,4 +1,4 @@
// (C) Copyright Antony Polukhin 2012.
// (C) Copyright Antony Polukhin 2012-2014.
// Use, modification and distribution are subject to 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)
Expand Down Expand Up @@ -75,7 +75,7 @@ static void do_test(bool do_count_cleanup_time = false) {
{
scope sc("boost::variant(variant&&) moving speed");
for (std::size_t i = 0; i < c_run_count; ++i) {
data_to.push_back(std::move(data_from[i]));
data_to.push_back(boost::move(data_from[i]));
}

if (do_count_cleanup_time) {
Expand Down Expand Up @@ -108,7 +108,7 @@ static void do_test(bool do_count_cleanup_time = false) {
{
scope sc("boost::variant=(variant&&) moving speed on same types");
for (std::size_t i = 0; i < c_run_count; ++i) {
data_to[i] = std::move(data_from[i]);
data_to[i] = boost::move(data_from[i]);
}

if (do_count_cleanup_time) {
Expand Down Expand Up @@ -142,7 +142,7 @@ static void do_test(bool do_count_cleanup_time = false) {
{
scope sc("boost::variant=(variant&&) moving speed on different types");
for (std::size_t i = 0; i < c_run_count; ++i) {
data_to[i] = std::move(data_from[i]);
data_to[i] = boost::move(data_from[i]);
}

if (do_count_cleanup_time) {
Expand Down Expand Up @@ -176,7 +176,7 @@ static void do_test(bool do_count_cleanup_time = false) {
{
scope sc("boost::variant=(variant&&) moving speed on different types II");
for (std::size_t i = 0; i < c_run_count; ++i) {
data_to[i] = std::move(data_from[i]);
data_to[i] = boost::move(data_from[i]);
}

if (do_count_cleanup_time) {
Expand Down Expand Up @@ -210,7 +210,7 @@ static void do_test(bool do_count_cleanup_time = false) {
{
scope sc("boost::variant=(T&&) moving speed");
for (std::size_t i = 0; i < c_run_count; ++i) {
data_to[i] = std::move(s2[i]);
data_to[i] = boost::move(s2[i]);
}

if (do_count_cleanup_time) {
Expand All @@ -222,6 +222,13 @@ static void do_test(bool do_count_cleanup_time = false) {


int main () {

#ifndef BOOST_NO_CXX11_RVALUE_REFERENCES
std::cout << "# Running tests in C++11 mode (with rvalues).\n";
#else
std::cout << "# Running tests in C++03 mode (without rvalues).\n";
#endif

do_test(false);
do_test(true);
}
Expand Down
8 changes: 1 addition & 7 deletions test/Jamfile.v2
Expand Up @@ -16,13 +16,6 @@ project
: requirements
#<dependency>/boost/test//minimal
<toolset>msvc:<asynch-exceptions>on
<toolset>gcc-4.8:<cxxflags>-DBOOST_VARIANT_USE_VARIADIC_TEMPLATES
<toolset>gcc-4.9:<cxxflags>-DBOOST_VARIANT_USE_VARIADIC_TEMPLATES
<toolset>clang-3.0:<cxxflags>-DBOOST_VARIANT_USE_VARIADIC_TEMPLATES
<toolset>clang-3.1:<cxxflags>-DBOOST_VARIANT_USE_VARIADIC_TEMPLATES
<toolset>clang-3.2:<cxxflags>-DBOOST_VARIANT_USE_VARIADIC_TEMPLATES
<toolset>clang-3.3:<cxxflags>-DBOOST_VARIANT_USE_VARIADIC_TEMPLATES
<toolset>clang-3.4:<cxxflags>-DBOOST_VARIANT_USE_VARIADIC_TEMPLATES
;

test-suite variant
Expand All @@ -40,6 +33,7 @@ test-suite variant
[ run variant_reference_test.cpp ]
[ run variant_comparison_test.cpp ]
[ run variant_visit_test.cpp ]
[ run variant_polymorphic_get_test.cpp ]
[ run variant_multivisit_test.cpp ]
[ run hash_variant_test.cpp ]
[ run rvalue_test.cpp ]
Expand Down
32 changes: 31 additions & 1 deletion test/rvalue_test.cpp
Expand Up @@ -3,7 +3,7 @@
// See http://www.boost.org for updates, documentation, and revision history.
//-----------------------------------------------------------------------------
//
// Copyright (c) 2012-2013 Antony Polukhin
// Copyright (c) 2012-2014 Antony Polukhin
//
// Distributed under the Boost Software License, Version 1.0. (See
// accompanying file LICENSE_1_0.txt or copy at
Expand All @@ -14,6 +14,7 @@
#include "boost/test/minimal.hpp"
#include "boost/variant.hpp"
#include "boost/type_traits/is_nothrow_move_assignable.hpp"
#include "boost/mpl/bool.hpp"

// Most part of tests from this file require rvalue references support

Expand Down Expand Up @@ -256,6 +257,25 @@ void run_tricky_compilation_test()
v = nothrow_copyable_throw_movable();
}

template <typename T>
struct is_container : boost::mpl::false_ {};

template <typename T>
struct is_container<boost::variant<T> > : is_container<T> {};

template <BOOST_VARIANT_ENUM_PARAMS(typename T)>
struct is_container<boost::variant<BOOST_VARIANT_ENUM_PARAMS(T)> >
: boost::mpl::bool_<is_container<T0>::value
|| is_container<boost::variant<BOOST_VARIANT_ENUM_SHIFTED_PARAMS(T)> >::value>
{};

void run_is_container_compilation_test()
{
BOOST_CHECK((!is_container<boost::variant<double, int> >::value));
BOOST_CHECK((!is_container<boost::variant<double, int, char> >::value));
BOOST_CHECK((!is_container<boost::variant<double, int, char, float> >::value));
}

int test_main(int , char* [])
{
run();
Expand All @@ -264,5 +284,15 @@ int test_main(int , char* [])
run_moves_are_noexcept();
run_tricky_compilation_test();
run_const_rvalues();
run_is_container_compilation_test();

#if !defined(BOOST_NO_CXX11_VARIADIC_TEMPLATES) && defined(__GNUC__) && (__GNUC__ == 4) && (__GNUC_MINOR__ > 6)
# ifdef BOOST_VARIANT_DO_NOT_USE_VARIADIC_TEMPLATES
BOOST_CHECK(false &&
"Something wrong with macro definitions. GCC-4.7+ is known to work with variadic templates"
);
# endif
#endif

return 0;
}
58 changes: 58 additions & 0 deletions test/variant_polymorphic_get_test.cpp
@@ -0,0 +1,58 @@
//-----------------------------------------------------------------------------
// boost-libs variant/test/variant_visit_test.cpp source file
// See http://www.boost.org for updates, documentation, and revision history.
//-----------------------------------------------------------------------------
//
// Copyright (c) 2003
// Eric Friedman
//
// 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)

#include "boost/variant/variant.hpp"
#include "boost/variant/apply_visitor.hpp"
#include "boost/variant/static_visitor.hpp"
#include "boost/variant/polymorphic_get.hpp"
#include "boost/test/minimal.hpp"

struct base {int trash;};
struct derived1 : base{};
struct derived2 : base{};

struct vbase { short trash; virtual ~vbase(){} virtual int foo() const { return 0; } };
struct vderived1 : virtual vbase{ virtual int foo() const { return 1; } };
struct vderived2 : virtual vbase{ virtual int foo() const { return 3; } };
struct vderived3 : vderived1, vderived2 { virtual int foo() const { return 3; } };

int test_main(int , char* [])
{
typedef boost::variant<int, base, derived1, derived2> var_t;

var_t var1;
BOOST_CHECK(!boost::polymorphic_get<base>(&var1));

var1 = derived1();
BOOST_CHECK(boost::polymorphic_get<base>(&var1));

derived2 d;
d.trash = 777;
var_t var2 = d;
BOOST_CHECK(boost::polymorphic_get<base>(var2).trash == 777);

var2 = 777;
BOOST_CHECK(!boost::polymorphic_get<base>(&var2));
BOOST_CHECK(boost::polymorphic_get<int>(var2) == 777);

typedef boost::variant<int, vbase, vderived1, vderived2, vderived3> vvar_t;

vvar_t v = vderived3();
boost::polymorphic_get<vderived3>(v).trash = 777;
const vvar_t& cv = v;
BOOST_CHECK(boost::polymorphic_get<vbase>(cv).trash == 777);

BOOST_CHECK(boost::polymorphic_get<vbase>(cv).foo() == 3);
BOOST_CHECK(boost::polymorphic_get<vbase>(v).foo() == 3);

return boost::exit_success;
}