Syntax of get for Variant and any_cast for Any
It took me a little while to figure out that get<T> on a variant is really just the moral equivalent of dynamic_cast<T> with a funny syntax. I'd like to understand why, given a variant v, this syntax is used T* p = get<T>(&v); T& r = get<T>(v); instead of this: T* p = variant_cast<T*>(&v); T& r = variant_cast<T&>(v); any_cast on pointers also uses what I consider to be a counterintuitive syntax, using this (for an any object a) T* p = any_cast<T>(&a) instead of this: T* p = any_cast<T*>(&a) However, any_cast to a reference works with a syntax analogous to dynamic_cast. Implementing the "expected" syntax doesn't seem to be too difficult. For example, here's my quick cut at variant_cast: template<typename T, typename U1, typename U2, typename U3> T variant_cast(boost::variant<U1, U2, U3>& v) // ref { using namespace boost; BOOST_STATIC_ASSERT(is_reference<T>::value); return get<remove_reference<T>::type>(v); } template<typename T, typename U1, typename U2, typename U3> T variant_cast(const boost::variant<U1, U2, U3>& v) //ref-to-const { using namespace boost; BOOST_STATIC_ASSERT(is_reference<T>::value); return get<remove_reference<T>::type>(v); } template<typename T, typename U1, typename U2, typename U3> T variant_cast(boost::variant<U1, U2, U3> *pv) // ptr { using namespace boost; BOOST_STATIC_ASSERT(is_pointer<T>::value); return get<remove_pointer<T>::type>(pv); } template<typename T, typename U1, typename U2, typename U3> T variant_cast(const boost::variant<U1, U2, U3> *pv) // ptr-to-const { using namespace boost; BOOST_STATIC_ASSERT(is_pointer<T>::value); return get<remove_pointer<T>::type>(pv); } In a thread last month, Marc Mutz posted this approach to "normalizing" the syntax of any_cast: template <typename T> class any_cast2 : noncopyable { const any & m_any; public: explicit any_cast2( const any & v ) : m_any( v ) {} operator T() const { return any_cast<T>( m_any ); } }; template <typename T> class any_cast2<T*> : noncopyable { any * const m_any; public: explicit any_cast2( any * v ) : m_any( v ) {} operator T * () const { return any_cast<T>( m_any ); } }; template <typename T> class any_cast2<const T*> : noncopyable { const any * const m_any; public: explicit any_cast2( const any * v ) : m_any( v ) {} operator const T * () const { return any_cast<T>( m_any ); } }; template <typename T> class any_cast2<T&> : noncopyable { any & m_any; public: explicit any_cast2( any & v ) : m_any( v ) {} operator T & () const { return any_cast<T&>( m_any ); } }; template <typename T> class any_cast2<const T&> : noncopyable { const any & m_any; public: explicit any_cast2( const any & v ) : m_any( v ) {} operator const T & () const { return any_cast<const T&>( m_any ); } }; Maybe there are problems to these approaches -- I've hardly investigated them. But I would be interested to know if there is a good reason for the syntaxes for dynamic_cast, any_cast, and get to vary so much. Thanks, Scott
On Mar 10, 2006, at 2:41 PM, Scott Meyers wrote:
It took me a little while to figure out that get<T> on a variant is really just the moral equivalent of dynamic_cast<T> with a funny syntax. I'd like to understand why, given a variant v, this syntax is used
T* p = get<T>(&v); T& r = get<T>(v);
instead of this:
T* p = variant_cast<T*>(&v); T& r = variant_cast<T&>(v); [snip] Maybe there are problems to these approaches -- I've hardly investigated them. But I would be interested to know if there is a good reason for the syntaxes for dynamic_cast, any_cast, and get to vary so much.
I think the answer is more archeological than technical :) We can't overload dynamic_cast, which is too bad: that's the right name for this sort of thing. If only someone would go ahead and write the C++0x proposal to add overloading of the cast operators, smart pointers, any, variant, python::object, and many other classes would get much more intuitive syntax. But I digress... any_cast() came around first. It probably should have had the syntax "T* p = any_cast<T*>(&a)" that you suggest. Kevlin may even have thought of that, but any and any_cast came into Boost back in the dark days when MSVC 6.0 dominated the C++ world. That means no remove_reference, no remove_pointer; I'm speculating here, but it wouldn't be the first time that the lack of suitable compilers forced Boost libraries into a suboptimal syntax. Only Kevlin would know for sure. get() came along later, when we realized that multiple types, including variant, python objects, and other dynamically-typed entities, have the same "extract as the static type T" semantics. It was built mainly to mimic any_cast, but work with a variety of types, not all of which were named "variant" (so, no "variant_cast"). In an ideal world, I think we'd end up using "dynamic_cast" for everything. Instead, we use the name "get", and I think the "any_cast<T>(&a)" is probably a holdover from the dark days that we should change. Doug
Doug Gregor wrote:
We can't overload dynamic_cast, which is too bad: that's the right name for this sort of thing. If only someone would go ahead and write the C++0x proposal to add overloading of the cast operators, smart pointers, any, variant, python::object, and many other classes would get much more intuitive syntax. But I digress...
FWIW, I'm told that the deadline for new language proposals is past, otherwise I'd seriously consider writing up a proposal to add "raw" strings to C++ to make experimenting with regex easier. But *I* digress...
In an ideal world, I think we'd end up using "dynamic_cast" for everything. Instead, we use the name "get", and I think the "any_cast<T>(&a)" is probably a holdover from the dark days that we should change.
My only other knowledge of "get" is from tuple, where "get" takes a compile-time constant, not a type, as an argument. With that background, I found the use of "get" for variant initially confusing. I think it'd be better to find a name with the "_cast" suffix and to explicitly specify pointer and reference types (as with dynamic_cast) and adopt that convention. I don't know enough about the various Boost libraries to know whether that is a reasonable suggestion. Scott
Doug Gregor <dgregor@cs.indiana.edu> writes:
We can't overload dynamic_cast, which is too bad: that's the right name for this sort of thing.
Maybe; I'm not so sure. xxx_cast implies capability that you probably wouldn't get from variant, like the ability to tell if some base class of the currently-stored type were available. -- Dave Abrahams Boost Consulting www.boost-consulting.com
participants (3)
-
David Abrahams
-
Doug Gregor
-
Scott Meyers