[any] checked convertion to reference?

Currently, boost::any supports two kinds of conversion: 1. Unchecked conversion to pointer (NULL is returned if conversion is impossible) 2. Checked conversion to value type (exception is thrown if conversion is impossible) I'd like to have another variant -- checked conversion to reference. The following code does not compile: boost::any a = 1; int& i = boost::any_cast<int&>(a); There are two issues: 1. Documentation says: If passed a value or reference, it returns a copy of the value content if successful. But even if I change the above to boost::any a = 1; int i = boost::any_cast<int&>(a); it does not compile. Docs are at least inaccurate. 2. Can we make the above work? I attach a patch which implements casting to reference, and passes all the existing test. I'd like this behaviour because currently program_options contains code such as: template<class T> T& variable_value::as() { T* r = boost::any_cast<T>(&v); if (!r) throw boost::bad_any_cast(); return *r; } which is plain ugly. With the patch, the above can be written as template<class T> T& variable_value::as() { return boost::any_cast<T&>(v); } - Volodya

Vladimir Prus wrote:
Currently, boost::any supports two kinds of conversion:
1. Unchecked conversion to pointer (NULL is returned if conversion is impossible) 2. Checked conversion to value type (exception is thrown if conversion is impossible)
I'd like to have another variant -- checked conversion to reference.
[...]
I'm very in favor of this. It has been proposed multiple times and Kevlin once told me he is going to extend any_cast this way, but it seems he is too busy. Stefan

Stefan Slapeta wrote:
I'm very in favor of this. It has been proposed multiple times and Kevlin once told me he is going to extend any_cast this way, but it seems he is too busy.
Reviewing a small patch usually takes less time then writing it. Has anybody with a patch contacted him? If not, I can do this. Here's the patch: http://lists.boost.org/MailArchives/boost/msg76565.php -- Alexander

Alexander Nasonov wrote:
Stefan Slapeta wrote:
I'm very in favor of this. It has been proposed multiple times and Kevlin once told me he is going to extend any_cast this way, but it seems he is too busy.
Reviewing a small patch usually takes less time then writing it. Has anybody with a patch contacted him? If not, I can do this. Here's the patch: http://lists.boost.org/MailArchives/boost/msg76565.php
Given that overall reaction is positive, I'll make the change, starting with the 'cast to reference' logic I wanted in the first place. The only difference between my version and the one you mention above is this snippet in 'any_cast': #ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION // Please use BOOST_BROKEN_COMPILER_TYPE_TRAITS_SPECIALIZATION macro // to generate specialization of remove_reference for your class // See type traits library documentation for details BOOST_STATIC_ASSERT(!::boost::is_reference<nonref>::value); #endif It looks to me that this assert will always be triggered when your pass reference type and BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION is defined, right? The assert will fire even if used has specialized remove_reference, no? If that's true, the comment is not correct and casting to reference is just not supported for broken compilers. I see that your patch also: 1. Strips cv-qualification in constructor (which is reasonable). 2. Strips cv-qualification when casting to pointer. Why the second aspect is needed? - Volodya

Given that overall reaction is positive, I'll make the change, starting with the 'cast to reference' logic I wanted in the first place.
The only difference between my version and the one you mention above is this snippet in 'any_cast':
#ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION // Please use BOOST_BROKEN_COMPILER_TYPE_TRAITS_SPECIALIZATION macro // to generate specialization of remove_reference for your class // See type traits library documentation for details BOOST_STATIC_ASSERT(!::boost::is_reference<nonref>::value); #endif
It looks to me that this assert will always be triggered when your
Vladimir Prus wrote: pass
reference type and BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION is defined, right? The assert will fire even if used has specialized remove_reference, no? If that's true, the comment is not correct and casting to reference is just not supported for broken compilers.
On broken compilers the assert for any_cast<int cv&> fires only if this thing is not in TU before first use of any_cast<int v&>: BOOST_BROKEN_COMPILER_TYPE_TRAITS_SPECIALIZATION(int) IMHO, if it's there, everything should work. BTW, there is a testcase in my earlier post in that thread. You can try to comment one such workaround to see the difference on a broken compiler or with BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION defined. Be careful, don't mix up the correct patch with previous version that comes with the test.
I see that your patch also: 1. Strips cv-qualification in constructor (which is reasonable).
const is already stripped because ctor's argument is 'const ValueType & value'. So, it's only for volatile. remove_cv is used only to avoid inclusion of remove_volatile.hpp.
2. Strips cv-qualification when casting to pointer.
Why the second aspect is needed?
I forgot to mention that the original version has small problem with any_cast to const type. If any stores T and you cast it to T const you're
Quoting myself: trying
this sequence of static_casts:
holder<T>* -> (in ctor) -> placeholder* -> (in any_cast) -> holder<T> const>*
Obviously, it's not getting back to original holder<T> type.
-- Alexander Nasonov

Alexander Nasonov wrote:
Given that overall reaction is positive, I'll make the change, starting with the 'cast to reference' logic I wanted in the first place.
The only difference between my version and the one you mention above is this snippet in 'any_cast':
#ifdef BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION // Please use BOOST_BROKEN_COMPILER_TYPE_TRAITS_SPECIALIZATION macro // to generate specialization of remove_reference for your class // See type traits library documentation for details BOOST_STATIC_ASSERT(!::boost::is_reference<nonref>::value); #endif
It looks to me that this assert will always be triggered when your
Vladimir Prus wrote: pass
reference type and BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION is defined, right? The assert will fire even if used has specialized remove_reference, no? If that's true, the comment is not correct and casting to reference is just not supported for broken compilers.
On broken compilers the assert for any_cast<int cv&> fires only if this thing is not in TU before first use of any_cast<int v&>:
BOOST_BROKEN_COMPILER_TYPE_TRAITS_SPECIALIZATION(int)
IMHO, if it's there, everything should work. BTW, there is a testcase in my earlier post in that thread. You can try to comment one such workaround to see the difference on a broken compiler or with BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION defined.
I think you're right, and trying gcc + BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION confirms that. I've committed this part of the patch.
I see that your patch also: 1. Strips cv-qualification in constructor (which is reasonable).
const is already stripped because ctor's argument is 'const ValueType & value'. So, it's only for volatile. remove_cv is used only to avoid inclusion of remove_volatile.hpp.
Does this use case matters, btw?
2. Strips cv-qualification when casting to pointer.
Why the second aspect is needed?
Quoting myself:
I forgot to mention that the original version has small problem with any_cast to const type. If any stores T and you cast it to T const you're trying this sequence of static_casts:
holder<T>* -> (in ctor) -> placeholder* -> (in any_cast) -> holder<T> const>*
Obviously, it's not getting back to original holder<T> type.
What about declaring that 'T' in all variants should have *no* cv-qualification? It's a bit strange that the user can cast to volatile type, and the any_cast will just throw that away. And cast *from* volalite any does not work, i.e. volatile boost::any a = 1; volatile int& i = boost::any_cast<volatile int&>(a); does not compile. So what's the point of allowing volatile type at all? - Volodya

Vladimir Prus wrote:
Alexander Nasonov wrote: I think you're right, and trying gcc + BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION confirms that. I've committed this part of the patch.
Great! One question. How to make sure that we don't break existing code on broken compilers? My plan is to extend existing testcase with more exotic use of _old_ functionality (details below) and to put _new_ functionality into separate testcase. Does it sound good to you?
const is already stripped because ctor's argument is 'const ValueType & value'. So, it's only for volatile. remove_cv is used only to avoid inclusion of remove_volatile.hpp.
Does this use case matters, btw?
I don't know. Even if it matters to some, I never saw their complains. Presumably, because this code snipset compiles and runs nicely: // candidate for inclusion into _old_ test int volatile i = 0; any a_i(i); any_cast<int>(a_i); int r = any_cast<int>(a_i); r = any_cast<int const>(a_i); r = any_cast<int volatile>(a_i); r = any_cast<int const volatile>(a_i); If not patched, it has a suble bug. a_i owns holder<int volatile> but any_cast function treats it as holder<int>, holder<int const>, holder<int volatile> and holder<int const volatile>, respectively. If you're going to fix this, please do it on both ends: in ctor and in any_cast. If you fix only any_cast end, you may end up with unsafe down cast over base pointer from holder<int volatile> to holder<int>. If you disable volatile at ctor end, some users (better if tests) will complain. Correct type for storing 'int cv' value in a_i is holder<int> (without cv!).
Quoting myself:
I forgot to mention that the original version has small problem with any_cast to const type. If any stores T and you cast it to T const you're trying this sequence of static_casts:
holder<T>* -> (in ctor) -> placeholder* -> (in any_cast) -> holder<T> const>*
Obviously, it's not getting back to original holder<T> type.
What about declaring that 'T' in all variants should have *no* cv-qualification? It's a bit strange that the user can cast to volatile type, and the any_cast will just throw that away. And cast *from* volalite any does not work, i.e.
volatile boost::any a = 1; volatile int& i = boost::any_cast<volatile int&>(a);
does not compile. So what's the point of allowing volatile type at all?
Why do you need volatile any? Safe casts to cv types and proper construction from volatile variable are things I was trying to fix. -- Alexander Nasonov

Alexander Nasonov wrote:
Vladimir Prus wrote:
Alexander Nasonov wrote: I think you're right, and trying gcc + BOOST_NO_TEMPLATE_PARTIAL_SPECIALIZATION confirms that. I've committed this part of the patch.
Great!
One question. How to make sure that we don't break existing code on broken compilers?
To clarify: the change I've committed only adds cast to reference. I postponed the other parts until we finish our discussion. So for now, all old code still works.
My plan is to extend existing testcase with more exotic use of _old_ functionality (details below) and to put _new_ functionality into separate testcase. Does it sound good to you?
The only new functionality that won't work on broken compilers is cast to reference of user-defined type for which remove_reference was not specified. Do you think we need to test for this explicitly?
const is already stripped because ctor's argument is 'const ValueType & value'. So, it's only for volatile. remove_cv is used only to avoid inclusion of remove_volatile.hpp.
Does this use case matters, btw?
I don't know. Even if it matters to some, I never saw their complains.
Neither did I.
Presumably, because this code snipset compiles and runs nicely:
// candidate for inclusion into _old_ test int volatile i = 0; any a_i(i); any_cast<int>(a_i); int r = any_cast<int>(a_i); r = any_cast<int const>(a_i); r = any_cast<int volatile>(a_i); r = any_cast<int const volatile>(a_i);
If not patched, it has a suble bug. a_i owns holder<int volatile> but any_cast function treats it as holder<int>, holder<int const>, holder<int volatile> and holder<int const volatile>, respectively.
Yea. I think it will never bite in practice, though, because 'const' or 'volatile' won't affect the object layout and IIRC, 'typeid' strips top-level cv qualifiers.
If you're going to fix this, please do it on both ends: in ctor and in any_cast. If you fix only any_cast end, you may end up with unsafe down cast over base pointer from holder<int volatile> to holder<int>. If you disable volatile at ctor end, some users (better if tests) will complain.
Correct type for storing 'int cv' value in a_i is holder<int> (without cv!).
Yes, that's true.
What about declaring that 'T' in all variants should have *no* cv-qualification? It's a bit strange that the user can cast to volatile type, and the any_cast will just throw that away. And cast *from* volalite any does not work, i.e.
volatile boost::any a = 1; volatile int& i = boost::any_cast<volatile int&>(a);
does not compile. So what's the point of allowing volatile type at all?
Why do you need volatile any? Safe casts to cv types and proper construction from volatile variable are things I was trying to fix.
I don't need volatile any, that's the point. I find it clearer if docs say 'You can pass cv-qualified types to 'any_cast'', then if they say 'you can cast to volatile type but you can't cast from volatile any'. - Volodya

On Feb 16, 2005, at 12:02 PM, Stefan Slapeta wrote:
Vladimir Prus wrote:
Currently, boost::any supports two kinds of conversion: 1. Unchecked conversion to pointer (NULL is returned if conversion is impossible) 2. Checked conversion to value type (exception is thrown if conversion is impossible) I'd like to have another variant -- checked conversion to reference.
[...]
I'm very in favor of this. It has been proposed multiple times and Kevlin once told me he is going to extend any_cast this way, but it seems he is too busy.
The patch looks good. Vladimir, please check it in. A documentation update and an announcement of the change for the Boost 1.33.0 web page would also be appreciated. Doug

Vladimir Prus wrote:
[...]
BTW, doesn't look good for VC 6 at the moment: http://tinyurl.com/6bsaa Stefan

Stefan Slapeta wrote:
BTW, doesn't look good for VC 6 at the moment: http://tinyurl.com/6bsaa
I can't tell you exactly what happens because I don't have VC6. I can only guess. First suspicious thing is new test_cast_to_reference function in any_test.cpp. Can you check how it compiles with original any_test.cpp (cvs revision 1.4), please? -- Alexander Nasonov

Alexander Nasonov wrote:
I can't tell you exactly what happens because I don't have VC6. I can only guess. First suspicious thing is new test_cast_to_reference function in any_test.cpp. Can you check how it compiles with original any_test.cpp (cvs revision 1.4), please?
The problem is, I don't have VC 6 installed on my machine anymore, so I'm afraid can't investigate on this. Anybody else? Stefan
participants (5)
-
Alexander Nasonov
-
Doug Gregor
-
Mikhail Glushenkov
-
Stefan Slapeta
-
Vladimir Prus