Boost logo

Boost Users :

Subject: Re: [Boost-users] large variant performance compared (50 elements)
From: Paul (peebor_at_[hidden])
Date: 2011-01-15 09:54:47


It looks like we are heading for an alternative approach (again). Based
on the boost::variant i'm writing my own variant that is optimized to
only hold shared_ptr's.

First test seem promising i can finally create variants with 200 types
in 2 sec compilation :) However binary invokation is even here quickly a
problem, its generating 'paths' quadratically, 50 bounded types produce
2500 paths....

Maybe anyone can provide some feedback on following implementation?

Thanks,
Paul

   template<typename Typelist>
   class CLoPtrVariant
   {
   public:
     CLoPtrVariant()
       : m_uiSelect(0)
     {
       // precondition assertions
       BOOST_STATIC_ASSERT((boost::mpl::is_sequence<Typelist>::value));
     }

     template<typename Type>
     CLoPtrVariant(const boost::shared_ptr<Type>& rspValue)
     {
       assign(rspValue);
     }

     CLoPtrVariant(const CLoPtrVariant& rOperand)
     {
       m_spSelect = rOperand.m_spSelect;
       m_uiSelect = rOperand.m_uiSelect;
     }

     template<typename Typelist2>
     CLoPtrVariant(const CLoPtrVariant<Typelist2>& rOperand)
     {
       CConvertVariant<Typelist> convert(*this);
       apply_visitor(convert, rOperand);
     }

     template<typename Type>
     CLoPtrVariant& operator=(const boost::shared_ptr<Type>& rspValue)
     {
       assign(rspValue);
       return *this;
     }

     template<typename Type>
     CLoPtrVariant& operator=(boost::shared_ptr<Type>& rspValue)
     {
       assign(rspValue);
       return *this;
     }

     bool operator<(const CLoPtrVariant& rRhs) const
     {
       if(m_uiSelect != rRhs.m_uiSelect)
       {
         return (m_uiSelect < rRhs.m_uiSelect);
       }
       else
       {
         return (m_spSelect < rRhs.m_spSelect);
       }
     }

     bool operator==(const CLoPtrVariant& rRhs) const
     {
       return (m_uiSelect == rRhs.m_uiSelect && m_spSelect ==
rRhs.m_spSelect);
     }

     bool operator!=(const CLoPtrVariant& rRhs) const
     {
       return !(*this == rRhs);
     }

     template<typename VisitorType>
     typename VisitorType::result_type apply_visitor(VisitorType&
rVisitor) const
     {
       switch (m_uiSelect)
       {
#ifdef BOOST_PP_LOCAL_ITERATE
#define BOOST_PP_LOCAL_MACRO(n) \
       case n: \
         return apply_visitor_impl<VisitorType, boost::mpl::int_<n>
>(rVisitor); \
         break;
#define BOOST_PP_LOCAL_LIMITS (0, 100) //Notice increasing this number
slows down compilation times, espacially binary visitation decreases
quadratic
#include BOOST_PP_LOCAL_ITERATE()
       default:
         BOOST_ASSERT(m_uiSelect); //Out of bounds; CLoPtrVariant is
missing code to access the sequence at given index, increase the
upperlimit in BOOST_PP_LOCAL_LIMITS
         return VisitorType::result_type();
#endif //BOOST_PP_LOCAL_ITERATE
       }
     }

     size_t GetWhich() const
     {
       return m_uiSelect;
     }

     void SetWhich(size_t which)
     {
       LO_CHECK(!m_spSelect);
       m_uiSelect = which;
     }

   private:
     template<typename Type>
     void assign(const boost::shared_ptr<Type>& rspValue)
     {
       typedef boost::mpl::find<Typelist, boost::shared_ptr<Type>
>::type pos_t;
       typedef boost::mpl::end<Typelist>::type end_t;
       BOOST_STATIC_ASSERT((!boost::is_same<pos_t, end_t>::value));
       m_spSelect = boost::static_pointer_cast<void>(rspValue);
       m_uiSelect = pos_t::pos::value;
     }

     template<typename VisitorType, typename IndexType>
     inline typename VisitorType::result_type
apply_visitor_impl(VisitorType& rVisitor, boost::mpl::true_
/*is_unrolled_t*/) const
     {
       typedef boost::mpl::at<Typelist, IndexType>::type type_t;
       return
rVisitor(boost::static_pointer_cast<type_t::value_type>(m_spSelect));
     }

     template<typename VisitorType, typename IndexType>
     inline typename VisitorType::result_type
apply_visitor_impl(VisitorType& rVisitor, boost::mpl::false_
/*is_unrolled_t*/) const
     {
       //Should never be here at runtime; only required to block code
generation that deref's the sequence out of bounds
       BOOST_ASSERT(false);
       return VisitorType::result_type();
     }

     template<typename VisitorType, typename IndexType>
     inline typename VisitorType::result_type
apply_visitor_impl(VisitorType& rVisitor) const
     {
       typedef typename boost::mpl::less<IndexType,
boost::mpl::size<Typelist>::type>::type is_unrolled_t;
       return apply_visitor_impl<VisitorType, IndexType>(rVisitor,
is_unrolled_t());
     }

   private:
     boost::shared_ptr<void> m_spSelect;
     size_t m_uiSelect;
   };

   //Helper function-template to construct the variant type
   template<typename Typelist>
   struct make_variant_over
   {
   public:
     typedef CLoPtrVariant<Typelist> type;
   };

   //Unary visitation
   template<typename Visitor, typename Visitable>
   inline typename Visitor::result_type apply_visitor(const Visitor&
visitor, Visitable& visitable)
   {
     return visitable.apply_visitor(visitor);
   }

   template<typename Visitor, typename Visitable>
   inline typename Visitor::result_type apply_visitor(Visitor& visitor,
Visitable& visitable)
   {
     return visitable.apply_visitor(visitor);
   }

   //Binary visitation
   template <typename Visitor, typename Visitable2>
   class CBinaryUnwrap1
   {
   public:
     typedef typename Visitor::result_type result_type;

   private:
     Visitor& visitor_;
     Visitable2& visitable2_;

   public:
     CBinaryUnwrap1(Visitor& visitor, Visitable2& visitable2)
       : visitor_(visitor)
       , visitable2_(visitable2)
     {
     }

   public:
     template<typename Value1>
     result_type operator()(Value1& value1)
     {
       CBinaryUnwrap2<Visitor, Value1> unwrapper(visitor_, value1);
       return apply_visitor(unwrapper, visitable2_);
     }
   };

   template<typename Visitor, typename Value1>
   class CBinaryUnwrap2
   {
   public:
     typedef typename Visitor::result_type result_type;

   private:
     Visitor& visitor_;
     Value1& value1_;

   public:
     CBinaryUnwrap2(Visitor& visitor, Value1& value1)
       : visitor_(visitor)
       , value1_(value1)
     {
     }

   public:
     template <typename Value2>
     result_type operator()(Value2& value2)
     {
       return visitor_(value1_, value2);
     }
   };

   template<typename Visitor, typename Visitable1, typename Visitable2>
   inline typename Visitor::result_type apply_visitor(const Visitor&
visitor, Visitable1& visitable1, Visitable2& visitable2)
   {
     CBinaryUnwrap1<const Visitor, Visitable2> unwrapper(visitor,
visitable2);
     return apply_visitor(unwrapper, visitable1);
   }

   template<typename Visitor, typename Visitable1, typename Visitable2>
   inline typename Visitor::result_type apply_visitor(Visitor& visitor,
Visitable1& visitable1, Visitable2& visitable2)
   {
     CBinaryUnwrap1<Visitor, Visitable2> unwrapper(visitor, visitable2);
     return apply_visitor(unwrapper, visitable1);
   }

   //Base class for visitor classes
   template<typename R = void>
   class static_visitor
   {
   public:
     typedef R result_type;

   protected:
     // for use as base class only
     static_visitor() { }
     ~static_visitor() { }
   };

   template<typename Base, typename Derived>
   struct is_base_of_smartptr
     : boost::is_base_of<typename Base::value_type, typename
Derived::value_type>
   {
   };

   //Convert variant types
   template<typename Typelist>
   class CConvertVariant
     : public static_visitor<>
   {
   public:
     CConvertVariant(CLoPtrVariant<Typelist>& rVariant)
       : m_rVariant(rVariant)
     {
     }

     template<typename Pos, typename Type>
     void assign_variant(boost::shared_ptr<Type>& rValue,
boost::mpl::false_) //convertible
     {
       typedef boost::mpl::deref<Pos>::type type_t;
       m_rVariant = boost::static_pointer_cast<type_t::value_type>(rValue);
     }

     template<typename Pos, typename Type>
     void assign_variant(boost::shared_ptr<Type>& rValue,
boost::mpl::true_) //not convertible
     {
       BOOST_STATIC_ASSERT((boost::mpl::false_)); //Compiler error here
indicates that (one of) the variants bounded types is not convertible to
the target variant type
       m_rVariant = rValue;
     }

     template<typename T>
     void operator()(boost::shared_ptr<T>& rValue) //T is not const to
match the variant bounded types
     {
       typedef boost::mpl::find_if<Typelist,
is_base_of_smartptr<boost::mpl::_1, boost::shared_ptr<T> > >::type pos_t;
       typedef boost::mpl::end<Typelist>::type end_t;
       typedef boost::is_same<pos_t, end_t>::type not_convertible;
       assign_variant<pos_t>(rValue, not_convertible());
     }

   private:
     CLoPtrVariant<Typelist>& m_rVariant;
   };

   //Delayed visitation (std algorithm support)
   template<typename VisitorType>
   class CVisitDelayed
   {
   public:
     typedef typename VisitorType::result_type result_type;

   private:
     VisitorType& visitor_;

   public:
     explicit CVisitDelayed(VisitorType& visitor)
       : visitor_(visitor)
     {
     }

   public:
     //Unary
     template<typename Visitable>
     result_type operator()(Visitable& visitable)
     {
       return apply_visitor(visitor_, visitable);
     }

     //Binary
     template<typename Visitable1, typename Visitable2>
     result_type operator()(Visitable1& visitable1, Visitable2& visitable2)
     {
       return apply_visitor(visitor_, visitable1, visitable2);
     }
   };

   template<typename VisitorType>
   inline CVisitDelayed<VisitorType> apply_visitor(VisitorType& visitor)
   {
       return CVisitDelayed<VisitorType>(visitor);
   }


Boost-users list run by williamkempf at hotmail.com, kalb at libertysoft.com, bjorn.karlsson at readsoft.com, gregod at cs.rpi.edu, wekempf at cox.net