RE: [boost] boost::any: explicit constructor?

(Note that the example above avoids the issue of implicit conversion (it won't compile), because it takes the parameter by non-const reference. With the current implementation, this is a good trick to use.) I agree that the constructor should be made explicit. I seem to recall that we discussed this on the list about a year ago, and the main objection against an explicit constructor was that the behavior across different compilers wasn't consistent (some compilers didn't like explicit template constructors at all). But now, a year later, perhaps it's time to go ahead and make the change? It's unfortunate that it's a change that will break code; perhaps some legacy mode needs to be provided. Anyway, there have been several requests for this and thus far no strong opinions against it. Bjorn

In message <3D8559AE95B4D611B02C0002557C6C8BA4DB75@STH-EXCH>, Bjorn.Karlsson@readsoft.com writes
The main point to take away from this is that this is not a problem ;->
I have yet to see a convincing reason to make it explicit. Autoboxing has been a part of boost::any's design from day 1, which existed before Boost -- and, indeed, the "autoboxing" terminology. Supporting such conversions was intentional. There is no more reason to make the converting constructor of boost::any explicit than there is to make the converting constructor of std::string explicit, or the conversion from int to long explicit, or the conversion from T * to void *, or indeed any other widening conversion. Of course, there is nothing to stop programmers adopting an explicit style, ie boost::any bar(1); bar = boost::any(2); void foo(const any &); foo(boost::any(3)); But there is no reason to prevent them from using the widening conversion style, ie boost::any bar = 1; bar = 2; void foo(const any &); foo(3); The choice is a matter of personal preference, but there is more convenience available in having the implicit conversion than there is safety to be achieved by making it explicit. The important point is that implicit conversions from boost::any should not be allowed, and they never have been. Kevlin PS And thanks for copying me on this, Bjorn. -- ____________________________________________________________ Kevlin Henney phone: +44 117 942 2990 mailto:kevlin@curbralan.com mobile: +44 7801 073 508 http://www.curbralan.com fax: +44 870 052 2289 Curbralan: Consultancy + Training + Development + Review ____________________________________________________________

Kevlin Henney wrote:
I don't understand the meaning of neither first "this", nor the second "this" :-(
If we go further, there's no reason for explicit ctors at all? I think that quite the contrary, unless there's very compelling reason why construction should be implicit, it should be explicit, since that's safer.
How is this going to make things safer? I my case I was passing expression of type boost::any to function which took boost::any. Refactoring changed the type, and code suddenly broke. I'm not sure the above would have helped.
That's quite subjective. When conversion to boost::any you actually cross the boundary between statically type-safe code and completely dynamically typed code. Doing that implicitly does not seem good for me. For a better mix of safely and convenience, it's possible to introduce 'reset' method: boost::any bar(1); bar.reset(2);
The important point is that implicit conversions from boost::any should not be allowed, and they never have been.
And that's another question. Conversions *from* boost::any are quite verbose. So why try to make boost::any initialisation concise, if further usage is still verbose? - Volodya

In message <200402261036.28459.ghost@cs.msu.su>, Vladimir Prus <ghost@cs.msu.su> writes
The absence of an 'explicit' is not a problem, and is therefore not something that needs a solution. The problem you had was with a non-conforming compiler: it is not the job of a library to fix all the breakages in a compiler. Sorry for not being clearer.
Of course not, please don't exaggerate. If you read my email and take the time to study more carefully the role that conversions play in the language you will appreciate that 'explicit' is a powerful and useful tool that is intended to disable implicit conversions where they are harmful and meaningless. I cannot see how you would "go further" to reach your conclusion. Many programmers fail to distinguish between narrowing and widening conversions, and arrive at unnecessarily constraining guidelines as a result. Conversions play a role in supporting the concept of substitutability, which is a significantly broader concept than the conventional application of LSP in OO might lead you to believe.
That may be a guideline that some C++ programmers have chosen to follow, but it is unnecessarily strict and not really in the spirit of the language, IMHO. That is their preference, and there is nothing in wrong in having such a preference, but it is not a preference that boost::any's design enforces. Implicit conversions should only be supported where they are safe and have a strong semantic and transparent correspondence, so where there is some notional substitutability between of source type wrt the target type, eg int to long, derived pointer to base pointer, any value to boost::any. A stricter guideline does not buy you any more real safety and it is inconvenient for the common case, so I cannot see why it would be a reasonable design goal. So, by and large, single-argument constructors should be 'explicit': for entity types, function object types, exception types, etc this is pretty much a rule; for value types it is a consideration or recommendation, but not a blanket rule.
You were using a broken compiler: that was the root cause of the problem.
Ugh. No. That again misses the point and transparency of value-based programming. C++ is a language that has good support for value-based programming. It can support meaningful conversions and restrict meaningless ones, at the discretion of the programmer. If you want Ada, you can Google for it ;->
Again, if you study the nature and role of conversions in more detail you will see that your conclusion does not follow. Not all kinds of conversion have the same weight and consequences, and they should be treated accordingly: narrowing conversions are potentially dangerous, and need to be marked in code; widening conversions are not, and do not. HTH Kevlin -- ____________________________________________________________ Kevlin Henney phone: +44 117 942 2990 mailto:kevlin@curbralan.com mobile: +44 7801 073 508 http://www.curbralan.com fax: +44 870 052 2289 Curbralan: Consultancy + Training + Development + Review ____________________________________________________________

Kevlin Henney wrote:
There's no problem with compiler actually. The real code used "const boost::any&", and the typo is only in the email.
Because you wrote that exlicit constructor would give more inconvenience than safety, and that same subjective judgement can be used in almost all cases. The experience in my case is very simple. I had two bugs because constructor is not explicit. I've just made it explicit locally, recomplied and got no complication errors, so convenience was not hurted.
I'm afraid that I prefer to use number of bugs and number of characters as the primary metric, because they don't allow different interpretation.
As I've explained, it's not the case. - Volodya

In message <200402261346.10248.ghost@cs.msu.su>, Vladimir Prus <ghost@cs.msu.su> writes
There's no problem with compiler actually. The real code used "const boost::any&", and the typo is only in the email.
In which case you got advertised behaviour, just not the behaviour that you expected :->
Because you wrote that exlicit constructor would give more inconvenience than safety, and that same subjective judgement can be used in almost all cases.
Inconvenience has both a subjective element and an objective element. One aspect of convenience in boost::any is that because it can hold any value -- as its name suggests -- it does so, and does so as transparently and consistently as possible. It is safe, in exactly the way that void * isn't, but that does not mean that all accidental misuse is prevented or preventable -- even though it is type safe, there is still an element of placing trust in the programmer.
I outlined in a previous email the intended syntax for use. To break this would be more than a little inconvenient, and certainly against the spirit of its design. Making the constructor 'explicit' would break the compilation of other code that is quite reasonable -- perhaps not yours, because you are following the style I outlined as an alternative in a previous response -- but it would still break it and for no good reason, eg std::map<std::string, boost::any> table; ... table[key] = 0; // reasonable usage Making the constructor 'explicit' will still not trap all occurrences of the type of error you made: class variable { public: boost::any &value(); ... }; void do_something(const pair<std::string, boost::any> &); ... variable foo; do_something(std::make_pair(key, foo)); // oops, should have been foo.value Given that you cannot trap all of the errors all of the time, the question then becomes where should the compromise be and what are the trade-offs? I favour the current situation for a number of reasons, including transparency of use, which I cited as a motivating convenience, and consistency. It is also entirely possible that you could reconsider the interfaces or style of design that led you to arrange your interfaces as you did. Sometimes such usage errors point to other areas of design tension.
I'm afraid that I prefer to use number of bugs and number of characters as the primary metric, because they don't allow different interpretation.
I am happy to use this metric as well, but you will find that it is not free of interpretation. However, be aware that it is not a metric of either safety or convenience: it is a measure of bugs and the number of characters. When and how did your bugs manifest themselves? Kevlin -- ____________________________________________________________ Kevlin Henney phone: +44 117 942 2990 mailto:kevlin@curbralan.com mobile: +44 7801 073 508 http://www.curbralan.com fax: +44 870 052 2289 Curbralan: Consultancy + Training + Development + Review ____________________________________________________________

In message <001901c3fc77$64c7cf70$1d00a8c0@pdimov2>, Peter Dimov <pdimov@mmltd.net> writes
It was the spirit and sense of the expression rather than specifically the mechanism that I was referring to -- boost::any currently achieves this through a templated assignment operator. There was an implied (in)consistency that I should perhaps have stressed more clearly. Kevlin -- ____________________________________________________________ Kevlin Henney phone: +44 117 942 2990 mailto:kevlin@curbralan.com mobile: +44 7801 073 508 http://www.curbralan.com fax: +44 870 052 2289 Curbralan: Consultancy + Training + Development + Review ____________________________________________________________

On 2/26/04 2:36 AM, "Vladimir Prus" <ghost@cs.msu.su> wrote:
The way I generally decide to make a single-argument constructor explicit or not is: Is the argument configuration data, or is supposed to be equivalent to the new object? If the argument is configuration data, then the constructor is explicit. If the new object is supposed to have some sort of equivalence to the argument, then the constructor is not explicit. The whole point of boost::any is that _any_ kind of (data) object can be stored in it; that any object can be converted to a boost::any object. I feel that the constructor should stay non-explicit then. -- Daryle Walker Mac, Internet, and Video Game Junkie darylew AT hotmail DOT com

Kevlin Henney <kevlin@curbralan.com> writes:
The conversion to boost::any isn't a widening conversion in the usual sense, though, is it? Normally, a widening conversion preserves not only the information but the *interface* of the original data -- you wouldn't consider an implicit conversion from int to string a widening conversion, would you? Similar to boost::any, you *can* go through a set of gyrations to extract the integer -- I bet that if boost::any could preserve the interface of the boxed type, Vladimir's bug wouldn't have come up. -- Dave Abrahams Boost Consulting www.boost-consulting.com
participants (6)
-
Bjorn.Karlsson@readsoft.com
-
Daryle Walker
-
David Abrahams
-
Kevlin Henney
-
Peter Dimov
-
Vladimir Prus