Re: [boost] [optional] generates unnessesary code for trivial types

FYI I'm currently working closely with Andrzej Krzemienski on a std proposal which, among other things, involves the long awaited update to the Boost implementation.
Could you give a use case for optional<T&>. When reading the doc about I completely understand the other use cases: * return types * parameters * locals * members * expensive unnecessary default construction
We could be having the same conversation about optional<bool>. Here the documentation is clear: it works as expected but is the user probably should be using tri-state.
We could optimize optional<bool> to store ~0 to indicate unset. I personally don't think it's worth doing that, since use case is questionable.
As far as I can tell optional<T&> is a type which can be easily confused with optional<T>, but is really equivalent to T*. It also allows for debates about the meaning of reassignment.
I'm not dead set against this optimization. I'm just wondering if anyone will really benefit and if it's worth adding a specialization + tests.
Speaking for myself, I do not have a need for optional reference. I have been using Boost.Optional for many years, I find it extremely useful; and I never had a use case for optional reference. On the other hand, others seem to be in need of optional references.Andrey said in this thread that that he wants to use it. The only two motivating use cases that triggered the addition of optional references (that I am aware of) is the interface of Boost.Variant (see http://lists.boost.org/Archives/boost/2003/02/44585.php) and the use case for Boost.Spirit (see http://lists.boost.org/Archives/boost/2003/10/55584.php). I would be reluctant to just discard these use cases. On the other hand, if they are allowed, the controversy of assignment arises. One thing to note is the requirement for optional references is not motivated by optimization opportunities, but additional behavior.
Side question: is streamablity going to be in your proposal? It is not currently. The reason for that is that there is no clear way how it should be implemented (a similar issue applies to pairs, tuples, variant, any, etc...) See http://lists.boost.org/Archives/boost/2005/03/81619.php for discussion. I believe that the problem is with the streaming itself. For instance, should streaming out and then streaming the result back in produce the same value? Some people expect that, but even std::string could not provide this behavior.
Regards, &rzej

On Mon, Feb 13, 2012 at 2:28 PM, Andrzej Krzemienski <akrzemi1@gmail.com> wrote:
Side question: is streamablity going to be in your proposal? It is not currently. The reason for that is that there is no clear way how it should be implemented (a similar issue applies to pairs, tuples, variant, any, etc...) See http://lists.boost.org/Archives/boost/2005/03/81619.php for discussion. I believe that the problem is with the streaming itself. For instance, should streaming out and then streaming the result back in produce the same value? Some people expect that, but even std::string could not provide this behavior.
I'd do only << and forget about >> -- Olaf

From: akrzemi1@gmail.com
Speaking for myself, I do not have a need for optional reference. I have been using Boost.Optional for many years, I find it extremely useful; and I never had a use case for optional reference. On the other hand, others seem to be in need of optional references.Andrey said in this thread that that he wants to use it. The only two motivating use cases that triggered the addition of optional references (that I am aware of) is the interface of Boost.Variant (see http://lists.boost.org/Archives/boost/2003/02/44585.php) and the use case for Boost.Spirit (see http://lists.boost.org/Archives/boost/2003/10/55584.php). I would be reluctant to just discard these use cases. On the other hand, if they are allowed, the controversy of assignment arises.
I use optional references in my code, in cases like the following: I have an operation, which processes some elements, and some of the elements constitute special cases. The caller of the operation may or may not want to know about the special cases that arose, so they can optionally pass in a container which will be populated with the special cases: void some_operation(inputs, optional<vector<case>&> special_cases = none) { for (...) { ... if (special_case) { ... if (special_cases) special_cases->push_back(current_case); } } } Before I discovered optional, I used a plain reference, but that was annoying because I had to create a dummy vector to be used as the default argument. (The other alternative would have been to use a pointer, but then the caller has to use the uglier syntax of passing in "&special_cases" rather than "special_cases"). Regards, Nate

Nathan Ridge wrote:
I use optional references in my code, in cases like the following: I have an operation, which processes some elements, and some of the elements constitute special cases. The caller of the operation may or may not want to know about the special cases that arose, so they can optionally pass >in a container which will be populated with the special cases:
void some_operation(inputs, optional<vector<case>&> special_cases = none) ;
That's an interesting use case. Default arguments are equivalent to overloading. You could write it as two functions: void some_operation(inputs, vector<case>& special_cases, bool optimize_out_special_cases = false) ; void some_operation(inputs) { vector<case> dummy; some_operation(inputs, dummy, true); } I think it would be more convenient for the users to use the overloaded functions over wrapping their vector in an optional. Regards, Luke

From: lucanus.j.simonson@intel.com
Nathan Ridge wrote:
I use optional references in my code, in cases like the following: I have an operation, which processes some elements, and some of the elements constitute special cases. The caller of the operation may or may not want to know about the special cases that arose, so they can optionally pass >in a container which will be populated with the special cases:
void some_operation(inputs, optional<vector<case>&> special_cases = none) ;
That's an interesting use case.
Default arguments are equivalent to overloading. You could write it as two functions:
void some_operation(inputs, vector<case>& special_cases, bool optimize_out_special_cases = false) ;
void some_operation(inputs) { vector<case> dummy; some_operation(inputs, dummy, true); }
I think it would be more convenient for the users to use the overloaded functions over wrapping their vector in an optional.
But they don't need to wrap their vector in an optional! That's the beauty of it. They can just do: vector<case> special_cases; some_operation(inputs, special_cases); if they care about the special cases, or: some_operation(inputs); if they do not. And given that it's all the same to the users, it's more convenient for *me* to just *write* one function. Regards, Nate

Before I discovered optional, I used a plain reference, but that was annoying because I had to create a dummy vector to be used as the default argument. (The other alternative would have been to use a pointer, but then the caller has to use the uglier syntax of passing in "&special_cases" rather than "special_cases").
Personally I'd simplify one line like this: //void some_operation(inputs, optional<vector<case>&> special_cases = none) { void some_operation(inputs, vector<case>* special_cases = 0) { for (...) { ... if (special_case) { ... if (special_cases) special_cases->push_back(current_case); } } } That's what the language construct pointer is for. Basic C++: Use a ref when you it can't be null; use a pointer when it can: http://www.parashift.com/c++-faq-lite/references.html#faq-8.6 Sorry to pick on you Nate, but your example is just what I'd like to avoid. People redefining concepts that the language already has. Any C++ programmer reading your code should understand what T* means. He may not be familiar with boost::optional<T&>. Chris

One disadvantage of using a pointer instead of optional<T&> is when I write a functions to serach for an item in a map like this: template<class T, class Map> optional<T&> search_for(Map& m, T& key); Optional lets me call get_value_or() so I can return a value if it cant find the key, but using a pointer I can't do that so easily. However, another class such as an optional_ref<T> class, could be created that would act like a pointer in this case, but still give the get_value_or() capability. Ultimately, I think optional should be a range(as well as the possible optional_ref<T>). I think it integrates better with C++. Instead of a get_value_or() function there would be a first_or() or a front_or() function that retrieves the first value from the range or returns something else when its empty. Also, checking for the value and getting the value could be done in one for loop. Now perhaps, its too late in the game for these things to be changed. Either way, optional<T> should be able to be made a range not intrusively. ----- Original Message -----
From: "Hite, Christopher" <Christopher.Hite@partner.commerzbank.com> To: Boost Developers Mailing List <boost@lists.boost.org> Cc: Sent: Tuesday, February 14, 2012 4:43 AM Subject: Re: [boost] [optional] generates unnessesary code for trivial types
Before I discovered optional, I used a plain reference, but that was annoying because I had to create a dummy vector to be used as the default argument. (The other alternative would have been to use a pointer, but then the caller has to use the uglier syntax of passing in "&special_cases" rather than "special_cases").
Personally I'd simplify one line like this:
//void some_operation(inputs, optional<vector<case>&> special_cases = none) { void some_operation(inputs, vector<case>* special_cases = 0) { for (...) { ... if (special_case) { ... if (special_cases) special_cases->push_back(current_case); } } }
That's what the language construct pointer is for. Basic C++: Use a ref when you it can't be null; use a pointer when it can: http://www.parashift.com/c++-faq-lite/references.html#faq-8.6
Sorry to pick on you Nate, but your example is just what I'd like to avoid. People redefining concepts that the language already has.
Any C++ programmer reading your code should understand what T* means. He may not be familiar with boost::optional<T&>.
Chris
_______________________________________________ Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost

On Tue, Feb 14, 2012 at 5:07 PM, paul Fultz <pfultz2@yahoo.com> wrote:
One disadvantage of using a pointer instead of optional<T&> is when I write a functions to serach for an item in a map like this:
template<class T, class Map> optional<T&> search_for(Map& m, T& key);
Optional lets me call get_value_or() so I can return a value if it cant find the key,
but using a pointer I can't do that so easily. However, another class such as
Why not? get_value_or() for pointers seems trivial. Olaf

----- Original Message -----
From: Olaf van der Spek <ml@vdspek.org> To: boost@lists.boost.org Cc: Sent: Tuesday, February 14, 2012 11:32 AM Subject: Re: [boost] [optional] generates unnessesary code for trivial types
On Tue, Feb 14, 2012 at 5:07 PM, paul Fultz <pfultz2@yahoo.com> wrote:
One disadvantage of using a pointer instead of optional<T&> is when I write a functions to serach for an item in a map like this:
template<class T, class Map> optional<T&> search_for(Map& m, T& key);
Optional lets me call get_value_or() so I can return a value if it cant find the key,
but using a pointer I can't do that so easily. However, another class such as
Why not? get_value_or() for pointers seems trivial.
Except, if T* is going to be used like that, than get_value_or() should be provided by the library. Still, I think optional<T> as a range is a much more generic solution to the problem. Its much easier to extend with my own optional types. If optional<T&> does not give me the semantics I want, I can write my own optional_ref<T> class, and get_value_or()(or maybe front_or()) will work as long as I provide begin() and end().

Den 14-02-2012 10:43, Hite, Christopher skrev:
Before I discovered optional, I used a plain reference, but that was annoying because I had to create a dummy vector to be used as the default argument. (The other alternative would have been to use a pointer, but then the caller has to use the uglier syntax of passing in "&special_cases" rather than "special_cases").
Personally I'd simplify one line like this:
//void some_operation(inputs, optional<vector<case>&> special_cases = none) { void some_operation(inputs, vector<case>* special_cases = 0) { for (...) { ... if (special_case) { ... if (special_cases) special_cases->push_back(current_case); } } }
I have a similar use-case, where an optional<std::exception&> is used as an argument, and its important to preserve reference semantics.
That's what the language construct pointer is for. Basic C++: Use a ref when you it can't be null; use a pointer when it can: http://www.parashift.com/c++-faq-lite/references.html#faq-8.6
Sorry to pick on you Nate, but your example is just what I'd like to avoid. People redefining concepts that the language already has.
Any C++ programmer reading your code should understand what T* means. He may not be familiar with boost::optional<T&>.
The main benefit is that optional<T&> is /way/ more clear than T*; people use T* even when it can't be null. In such a code base, T* is ambiguous to read, whereas optional<T&> is not. And no, we can't just clean up hundreds of k lines of source code to make the interpretation unambiguous. Anyway, it should be trivial to make a new optional<T&> as efficient as T*. -Thorsten

+1 for optional<T&> with the current assignment semantics and optimization. In my opinion, optional<T&> is better then T*, because: - it is clear, that the object isn't owned (owning T* pointers should be replaced by smart pointers, but that's another story); - the default constructor of optional<T&> initializes it to none; - it is clear, that the object actually is optional, whereas with T*, it needs to be documented "can be null" or something; - I don't have to write my own get_value_or function, no matter how trivial it would be for pointers ;-) ; - access is asserted, so logic errors are caught early (right?). Also, I prefer optional<T&> to some dumb_ptr, because optional<T&> says exactly what i mean, and dumb_ptr - not really. A note about assignment semantics: int i = 1, j = 2; optional<int&> oi = i, oj = j; oi = oj; If many people find it confusing, that oi now contains a reference to j, while i remains unchanged, I could live with this kind of assignment disabled at compile-time, and replaced by some member function. But personally I prefer to leave those semantics unchanged. A note about optimization: as it has been said, it seems easier to just provide the optimization, than discuss it over and over again. Regards Kris

On 2/14/2012 3:48 PM, Krzysztof Czainski wrote:
A note about assignment semantics: int i = 1, j = 2; optional<int&> oi = i, oj = j; oi = oj; If many people find it confusing, that oi now contains a reference to j, while i remains unchanged, I could live with this kind of assignment disabled at compile-time, and replaced by some member function.
Altough, as you would expect, I am not confused about the current rebinding semantics (and in fact I believe is more approriate than the alternative), in the std proposal that Andrzej and I are preparing, assignment is disabled for optional lvalue references. If you do need to rebind the reference, you can use the new emplace() method, which in fact behaves consistently among all sorts of optionals, references included and which rebinds the reference as you would expect. OTOH, if you need to assign to the contained/referenced object instead, you can use: (*opt) = value which also behaves consistently among all optionals. Finally, if you need to "assign" in a generic context where T might or might not be a reference, but without rebinding semantics, you can do: if ( opt ) *opt = value ; else opt.emplace(value) Which also works consistently among all sorts of optional, always assign to the contained/referenced object if it exists, or initializes the optional with a new object/reference otherwise. Best -- --- Fernando Cacciola SciSoft Consulting, Founder http://www.scisoft-consulting.com

Hite, Christopher
Any C++ programmer reading your code should understand what T* means. He may not be familiar with boost::optional<T&>.
Assuming a future std::optional, any future C++ programmer perhaps should know. The real question is, is optional<T&> safer than T*? If someone accesses the object inside optional without checking if it is valid that is equivalent to dereferencing a pointer without checking if it is null. However, safety is about behavior, and people tend to dereference pointers without checking them very frequently because there are plenty of cases where it is safe to assume a pointer is not null. In code review people ask me "why didn't you check if that pointer is null?" and my answer is usually, "because throwing an exception if it is null is the behavior I want". You could say I should have used a reference instead, but it might not have been my pointer to start with. Specifically, polymorphic data types are often passed around by pointer to base class instead of reference to base class by most C++ programmers. Optional, however, is only used when the value could be invalid under normal circumstances and would normally always be checked. Regards, Luke

Simonson, Lucanus J wrote:
Hite, Christopher
Any C++ programmer reading your code should understand what T* means. He may not be familiar with boost::optional<T&>.
The real question is, is optional<T&> safer than T*? If someone accesses the object inside optional without checking if it is valid that is equivalent to dereferencing a pointer without checking if it is null. However, safety is about behavior, and people tend to dereference pointers without checking them very frequently because there are plenty of cases where it is safe to assume a pointer is not null.
I don't know to what "people" you're referring, but I check for null pointers before dereferencing them. If you want to pass the buck, always use smart pointers that validate dereferences. However, I prefer not to pay the overhead of checking for null on every dereference, so I convert to a reference once I've verified a non-null pointer, even with smart pointers, provided there's to be more than one dereference.
In code review people ask me "why didn't you check if that pointer is null?" and my answer is usually, "because throwing an exception if it is null is the behavior I want". You
In that case, you're using a smart pointer.
could say I should have used a reference instead, but it might not have been my pointer to start with.
What has that to do with it? Once I get a pointer into my code, I check for null, dereference it, and pass it by reference thereafter so no other code needs to test for null.
Specifically, polymorphic data types are often passed around by pointer to base class instead of reference to base class by most C++ programmers.
Really? I'm certainly not "most C++ programmers" then. If something might not exist, I use optional or a pointer, depending. If it is required to exist, then I use a reference. I write functions taking references, not pointers, when I don't want to deal with the possibility of a null pointer. The caller must handle that for me. _____ Rob Stewart robert.stewart@sig.com Software Engineer using std::disclaimer; Dev Tools & Components Susquehanna International Group, LLP http://www.sig.com ________________________________ IMPORTANT: The information contained in this email and/or its attachments is confidential. If you are not the intended recipient, please notify the sender immediately by reply and immediately delete this message and all its attachments. Any review, use, reproduction, disclosure or dissemination of this message or any attachment by an unintended recipient is strictly prohibited. Neither this message nor any attachment is intended as or should be construed as an offer, solicitation or recommendation to buy or sell any security or other financial instrument. Neither the sender, his or her employer nor any of their respective affiliates makes any warranties as to the completeness or accuracy of any of the information contained herein or that this message or any of its attachments is free of viruses.

Den 14-02-2012 20:53, Stewart, Robert skrev:
Specifically, polymorphic data types are often passed around by pointer to base class instead of reference to base class by most C++ programmers.
Really? I'm certainly not "most C++ programmers" then. If something might not exist, I use optional or a pointer, depending. If it is required to exist, then I use a reference. I write functions taking references, not pointers, when I don't want to deal with the possibility of a null pointer. The caller must handle that for me.
Yes, really. Look at Boost.PtrContainers, for instance. They also allow you to create containers that are validated to contain no nulls. But we are very often required to use the pointers. How the object is passed around out-side the container is another matter. -Thorsten

Simonson, Lucanus J wrote:
You could say I should have used a reference instead, but it might not have been my pointer to start with.
???
Specifically, polymorphic data types are often passed around by pointer to base class instead of reference to base class by most C++ programmers.
This doesn't convince me. If the pointer can't be zero (and if somebody else owns it), there is no good reason not to use a reference. Well, if the pointer is a class member, using a reference might be impossible. Regards, Thomas

Thomas Klimpel wrote: Simonson, Lucanus J wrote:
You could say I should have used a reference instead, but it might not have been my pointer to start with.
???
It might not have been my code to start with.
Specifically, polymorphic data types are often passed around by pointer to base class instead of reference to base class by most C++ programmers.
This doesn't convince me. If the pointer can't be zero (and if somebody else owns it), there is no good reason not to use a reference. Well, if the pointer is a class member, using a reference might be impossible.
An std container of references does not compile, a std container of pointers does. In code with dynamic polymorphic type systems pointers get used a lot, and often more than they should. Regards, Luke

An std container of references does not compile, a std container of pointers does. In code with dynamic polymorphic type systems pointers get used a lot, and often more than they should.
An std container of (boost/std)::(shared_ptr/unique_ptr) does compile and should be prefered to raw pointers. Julien

On Wed, Feb 15, 2012 at 3:30 AM, Julien Nitard <julien.nitard@m4tp.org> wrote:
An std container of references does not compile, a std container of pointers does. In code with dynamic polymorphic type systems pointers get used a lot, and often more than they should.
An std container of (boost/std)::(shared_ptr/unique_ptr) does compile and should be prefered to raw pointers.
Why? If the pointers are non-owning, shared/unique is wrong. -- Olaf

On Wed, Feb 15, 2012 at 5:40 PM, Olaf van der Spek <ml@vdspek.org> wrote:
On Wed, Feb 15, 2012 at 3:30 AM, Julien Nitard <julien.nitard@m4tp.org> wrote:
An std container of references does not compile, a std container of pointers does. In code with dynamic polymorphic type systems pointers get used a lot, and often more than they should.
An std container of (boost/std)::(shared_ptr/unique_ptr) does compile and should be prefered to raw pointers.
Why? If the pointers are non-owning, shared/unique is wrong.
I may just say that you could add weak_ptr to the list to make it look better, but I guess you're right. I just thought (wrongly) that the pointers were owning. Sorry. In the context of the question, though, I was trying to support the fact that raw pointers are "used a lot, and often more than they should". I forgot that we're talking on boost and that your usual metrics and practices won't always hold. Julien

Luke wrote:
This doesn't convince me. If the pointer can't be zero (and if somebody else owns it), there is no good reason not to use a reference. Well, if the pointer is a class member, using a reference might be impossible.
An std container of references does not compile, a std container of pointers does. In code with dynamic polymorphic type systems pointers get used a lot, and often more than they should.
Yeah, those containers want your type to be default constructable which ref is not. If you use one of those containers you should use a pointer and from the point of view of that container it really can be null. If you know it won't be convert back to ref, but be careful: std::map<int,entry_type*> m; m[3]; // constructs a null pointer Changing the code to: std::map<int,optional<entry_type&> > m; won't make it any safer or easier to read. If fact the ref syntax on assignment might make it worse: m[3]=x; //quitely takes address of x and stores it Normal refs have to be assigned were they're declared or in initializer lists. This kind of a ref container seems sneaky to me. The intrusive conainers work by ref though and it makes sense since the elements can not be null. http://www.boost.org/doc/libs/1_48_0/doc/html/boost/intrusive/set.html Note unlink_leftmost_without_rebalance() returns pointer, because it could be null. Chris
participants (12)
-
Andrzej Krzemienski
-
Fernando Cacciola
-
Hite, Christopher
-
Julien Nitard
-
Krzysztof Czainski
-
Nathan Ridge
-
Olaf van der Spek
-
paul Fultz
-
Simonson, Lucanus J
-
Stewart, Robert
-
Thomas Klimpel
-
Thorsten Ottosen