1) deconstruction
typedef boost::optional<int> optional_int;
void deconstruct_boost_optional(optional_int& o){
o.~optional_int();
}
One would expect this to do nothing. Instead gcc 4.6.0 with O3 generates:
if(m_initialized){
// do nothing
m_initialized = false;
}
00000000 <deconstruct_boost_optional(boost::optional<int>&)>:
0: 8b 44 24 04 mov 0x4(%esp),%eax
4: 80 38 00 cmpb $0x0,(%eax)
7: 74 03 je c <deconstruct_boost_optional(boost::optional<int>&)+0xc>
9: c6 00 00 movb $0x0,(%eax)
c: f3 c3 repz ret
This one could be easily fixed by removing the bit that sets m_initialized to false, since we're deconstructing anyway.
2) assignment also generates these problems:
void assign_boost_optional(optional_int& o){
o=13;
}
Here there's a semantic issue: we have to decide to use the copy constructor or operator=. This is also wasteful for POD types or any type which has_trivial_copy<>.
3) Even more expensive is if we want to copy an optional<int>
void assign_boost_optional(optional_int& a,optional_int& b){
a=b;
}
00000000 <assign_boost_optional(boost::optional<int>&, boost::optional<int>&)>:
0: 8b 44 24 04 mov 0x4(%esp),%eax
4: 8b 54 24 08 mov 0x8(%esp),%edx
8: 80 38 00 cmpb $0x0,(%eax)
b: 74 0b je 18 <assign_boost_optional(boost::optional<int>&, boost::optional<int>&)+0x18>
d: 80 3a 00 cmpb $0x0,(%edx)
10: 75 16 jne 28 <assign_boost_optional(boost::optional<int>&, boost::optional<int>&)+0x28>
12: c6 00 00 movb $0x0,(%eax)
15: c3 ret
16: 66 90 xchg %ax,%ax
18: 80 3a 00 cmpb $0x0,(%edx)
1b: 74 09 je 26 <assign_boost_optional(boost::optional<int>&, boost::optional<int>&)+0x26>
1d: 8b 52 04 mov 0x4(%edx),%edx
20: c6 00 01 movb $0x1,(%eax)
23: 89 50 04 mov %edx,0x4(%eax)
26: f3 c3 repz ret
28: 8b 52 04 mov 0x4(%edx),%edx
2b: 89 50 04 mov %edx,0x4(%eax)
2e: c3 ret
Three possible branches! Theoretically single 64 bit copy do the job. I'm tempted to say: it would be best if for any T has_trivial_copy< optional<T> > iff has_trivial_copy<T>. It might make a sense to make an exception for huge T, where the copying an unused T is more expensive than the branching.
4) has_trivial_destructor<T> should impl has_trivial_destructor< optional<T> > , but this is hard to implement without specialization of optional.
Checking has_trivial_destructor might take care of the complexity of optional<T&> since has_trivial_destructor< T& >.
I'd be willing to fix #1. The other issues need some discussion.
Chris
_______________________________________________
Unsubscribe & other changes: http://lists.boost.org/mailman/listinfo.cgi/boost
I happen to have been looking at the source and generated code for boost::optional recently myself, so jumping in here with a few comments.
> 1) deconstruction
> typedef boost::optional<int> optional_int;
>
> void deconstruct_boost_optional(optional_int& o){
> o.~optional_int();
> }
>
> One would expect this to do nothing. Instead gcc 4.6.0 with O3 generates:
>
> if(m_initialized){
> // do nothing
> m_initialized = false;
> }
>
> 00000000 <deconstruct_boost_optional(boost::optional<int>&)>:
> 0: 8b 44 24 04 mov 0x4(%esp),%eax
> 4: 80 38 00 cmpb $0x0,(%eax)
> 7: 74 03 je c <deconstruct_boost_optional(boost::optional<int>&)+0xc>
> 9: c6 00 00 movb $0x0,(%eax)
> c: f3 c3 repz ret
>
>
> This one could be easily fixed by removing the bit that sets m_initialized to false, since we're deconstructing anyway.
This sounds right to me. Note that eliminating the assignment of m_initialized would (in this case of a trivial destructor for T) make the entire clause controlled by the conditional be empty after optimization, allowing the compiler to optimize away the conditional too.
What's going on here is that the destructor is calling the destroy() helper function, which does more work than the destructor actually needs, specifically setting m_initialized to false. Other callers of destroy() do need that assignment.
> 3) Even more expensive is if we want to copy an optional<int>
>
> void assign_boost_optional(optional_int& a,optional_int& b){
> a=b;
> }
>
> 00000000 <assign_boost_optional(boost::optional<int>&, boost::optional<int>&)>:
> 0: 8b 44 24 04 mov 0x4(%esp),%eax
> 4: 8b 54 24 08 mov 0x8(%esp),%edx
> 8: 80 38 00 cmpb $0x0,(%eax)
> b: 74 0b je 18 <assign_boost_optional(boost::optional<int>&, boost::optional<int>&)+0x18>
> d: 80 3a 00 cmpb $0x0,(%edx)
> 10: 75 16 jne 28 <assign_boost_optional(boost::optional<int>&, boost::optional<int>&)+0x28>
> 12: c6 00 00 movb $0x0,(%eax)
> 15: c3 ret
> 16: 66 90 xchg %ax,%ax
> 18: 80 3a 00 cmpb $0x0,(%edx)
> 1b: 74 09 je 26 <assign_boost_optional(boost::optional<int>&, boost::optional<int>&)+0x26>
> 1d: 8b 52 04 mov 0x4(%edx),%edx
> 20: c6 00 01 movb $0x1,(%eax)
> 23: 89 50 04 mov %edx,0x4(%eax)
> 26: f3 c3 repz ret
> 28: 8b 52 04 mov 0x4(%edx),%edx
> 2b: 89 50 04 mov %edx,0x4(%eax)
> 2e: c3 ret
>
> Three possible branches! Theoretically single 64 bit copy do the job. I'm tempted to say: it would be best if for any T has_trivial_copy< optional<T> > iff has_trivial_copy<T>. It might make a sense to make an exception for huge T, where the copying an unused T is more expensive than the branching.
I think the generated code gets somewhat simplified once issue (1) is addressed.
I think it would be a mistake to just blindly copy the value of b when b.m_initialized is false, if for no other reason than doing so will lead to endless user complaints about compiler and valgrind warnings. Also, invoking undefined behavior can result in the compiler doing very nasty and unexpected things, even in the absence of runtime issues from reading an "uninitialized" location. Consider the possibility that the compiler can prove that the optional being copied from is uninitialized, and so can conclude that the read of its value is undefined behavior. Probably the *best* one can hope for in such a situation is a compiler warning, and many far worse results are possible.
While I think this shouldn't be necessary from a theoretical standpoint, in a practical sense it might make the optimizer's job a little easier (and so increase the chances of getting the code you are looking for) to change the assign(optional) member functions that presently look something like
if (is_initialized())
if (rhs.is_initialized())
assign_value(…)
else destroy()
else if (rhs.is_initialized())
construct(…)
to instead be something like
if (rhs.is_initialized())
if (is_initialized())
assign_value(...)
else construct(...)
else destroy()
or
if ( ! rhs.is_initialized())
destroy()
else if (is_initialized())
assign_value(...)
else construct(…)
I find that it is quite easy to write safe C++ interfaces without using optional, so I see no reason why you can't design code that is both safe and fast without it.
I know the author of optional and you haven't convinced me that we should bother him.
Regards,
Luke
All of the offered suggestions require the caller to construct an initial object that can be passed (by reference / pointer) to the callee for replacement. That may be either inefficient (object is expensive to construct) or impossible (caller doesn't have access to an appropriate constructor).
> I think the generated code gets somewhat simplified once issue (1) is addressed.
It would help, but I think won't get rid of all the branches. Your refactoring might help more.
> I think it would be a mistake to just blindly copy the value of b when b.m_initialized is false,
> if for no other reason than doing so will lead to endless user complaints about compiler and
> valgrind warnings. Also, invoking undefined behavior can result in the compiler doing very
> nasty and unexpected things, even in the absence of runtime issues from reading an
> "uninitialized" location. Consider the possibility that the compiler can prove that the
> optional being copied from is uninitialized, and so can conclude that the read of its value
> is undefined behavior. Probably the *best* one can hope for in such a situation is a
> compiler warning, and many far worse results are possible.
Consider the completely legal code below:
struct cheap_optional_int{
cheap_optional_int() : m_initialized() {} // don't init m_data
bool m_initialized;
int m_data;
};
void assign_boost_cheap_optional_int(cheap_optional_int& a,cheap_optional_int& b){
a=b; // default impl
}
The compiler generates nothing but 32-bit moves from the source to the destination. This is completely fine for valgrind. It only complains if a branch based is taken based on uninitialized data.
00000000 <assign_boost_cheap_optional_int(cheap_optional_int&, cheap_optional_int&)>:
0: 53 push %ebx
1: 8b 44 24 0c mov 0xc(%esp),%eax
5: 8b 58 04 mov 0x4(%eax),%ebx
8: 8b 08 mov (%eax),%ecx
a: 8b 44 24 08 mov 0x8(%esp),%eax
e: 89 08 mov %ecx,(%eax)
10: 89 58 04 mov %ebx,0x4(%eax)
13: 5b pop %ebx
14: c3 ret
Sorry the assembler is so poorly formatted after it's mailed.
The cool thing is cheap_optional_int has_trivial_destructor and has_trivial_copy because we haven't overridden the defaults.
Unfotunately overriding the default ctor/dtor always breaks these, even if the code could be optimized out. It may not even be possible for a compiler to solve.
Chris
_____________________________________________
From: Hite, Christopher
Sent: Wednesday, January 25, 2012 6:29 PM
To: 'bo...@lists.boost.org'
Subject: [optional] generates unnessesary code for trivial types
You may be right, but you're talking about different use cases. I've got a protocol de/encoders so I want a friendly high level representation of messages that I want to hand off between modules. Imagine a struct with an optional substruct.
Valid alternatives: a pointer to the substruct. Even if I can put the second structure on the stack, this might mean less cache hits. The total extra size is also increased bool=>pointer.
Another option sometimes possible is a nullable value. FAST-FIX's nullable integer for example increments all non-negative values and uses 0 to represent a null.
Another option is to use a presence map at the top of a structure with one bit(or byte) per optional field. That might help with alignment.
> I find that it is quite easy to write safe C++ interfaces without using optional...
Yes I used optional because I knew it would do things correctly.
> you haven't convinced me
Just focus on #1 first. Not writing to m_initialized in the deconstructor would benifit all use cases of optional.
It can't be the solution to just not use boost everytime there's a performance issue.
the user can not always redesign an interface using optional<T> as he
could be not the owner (use of 3pp libraries).
I'm sure the author/maintainer of optional would adopt some patches if
it is probed a performance improvement for some specific cases.
Best,
Vicente
I see no reason why we can't have safe _and_ fast _and_ optional?
The rationale you gave is just typical premature pessimization apologetics that
also somehow assumes that C++ is "safe and slow" and that you have to go "bare
metal C" to have performance. Luckily that's just plain incorrect, to put it
mildly. Sadly, that rationale nonetheless also too often gives us such bloatware
as std::streams, lexical_cast or boost::filesystem...
When you design a such a generic library how can there be "premature optimization"?
--
"What Huxley teaches is that in the age of advanced technology, spiritual
devastation is more likely to come from an enemy with a smiling face than
from one whose countenance exudes suspicion and hate."
Neil Postman
Hi, I've recently created an improved internal version of boost::optional to
help workaround two issues:
- suboptimal codegen
- concurrent access.
You can now find this version under
https://svn.boost.org/svn/boost/sandbox/optional. So far the following has been
done:
a) the lifetime management bool was changed into a properly typed pointer (this
actually takes the same amount of space while it provides a no-op get_ptr()
member function as well as easier debugging as the contents of optional can
now clearly be seen through the pointer, as opposed to gibberish in an opaque
storage array)
b) added another conditional constructor that accepts an in-place factory
c) uses the safe bool idiom implementation from Boost.Range (which generates
better code on pre MSVC10 compilers)
d) skips redundant/dead stores of marking itself as uninitialised [including but
limited to, in its destructor (if it has one)]
e) streamlined internal assign paths to help the compiler avoid unnecessary
branching
f) added direct_create() and direct_destroy() member functions that allow the
user to bypass the internal lifetime management (they only assert correct
usage) in situations where the user's own external logic already implicitly
knows the state of the optional
g) optional now declares and defines a destructor only if the contained type has
a non-trivial destructor (this prevents the compiler from detecting false EH
states and thus generating bogus EH code)
h) optional marks itself as uninitialised _before_ calling the contained
object's destructor (this makes it a little more robust in race conditions;
it is of course not a complete solution for such scenarios, those require
external "help" and/or (m)-reference counting to be implemented)
i) extracted the "placeholder" functionality into a standalone class (basically
what would be left of optional<> if the lifetime management "bool" member and
logic was removed) so that it can be reused (e.g. for singleton like classes,
or when more complex custom lifetime management is required)
j) added compiler specific "aids" to workaround situations when the compiler is
unable to detect that placement new will never return a nullptr (and then
generates bogus branching) - IOW "optional<int> optional_number( 3 );" no
longer generates a branch before storing "3" (yes "LOL":)
k) the lifetime management pointer is now stored after the actual contained
object (this helps in avoiding more complex/offset addressing when accessing
optionals through pointers w/o checking whether they are initialised)
l) removed support for antediluvian compilers (MSVC6, BCB5)
todo:
m) lifetime management policy: bool, pointer, reference count (+ a more generic
abstraction/interop with smart_ptr)...
n) zero size overhead for optional references (requires (m))
o) avoid branching in assignment and copy construction of optionals that hold
PODs smaller than N * sizeof( void * ) where N is some small number
- temporarily renamed to optional2 to avoid collision with the original
optional
- passes all optional unit tests (after being renamed back to optional) with
MSVC10 SP1 and Apple Clang 3.0 (from Xcode 4.2.1)
Hope it helps ;)
ps. AFAICT the only real obstacle in having really nice codegen with
boost::optional<a_fundamental_type> is lack of proper ABI/compiler support for
passing and returning small structs in registers...
--
"What Huxley teaches is that in the age of advanced technology, spiritual
devastation is more likely to come from an enemy with a smiling face than
from one whose countenance exudes suspicion and hate."
Neil Postman
I'm actually glad to see you putting effort into making that happen. The effort required is the only reason. Performance wasn't the reason I don't use optional, but for those who do use it I'm sure it will be valuable.
>The rationale you gave is just typical premature pessimization apologetics that
>also somehow assumes that C++ is "safe and slow" and that you have to go "bare
>metal C" to have performance. Luckily that's just plain incorrect, to put it
>mildly. Sadly, that rationale nonetheless also too often gives us such bloatware
>as std::streams, lexical_cast or boost::filesystem...
I said safe and fast C++ without optional, which isn't the same thing as "bare metal C". Bare metal C wouldn't qualify as safe. I'm as annoyed by the "C is faster than C++, ergo I never learned C++" guys as you are. Optional was implemented to be safe and slow because it was targeting safe and slow use cases. For POD types and anything that has a default constructor a std::pair<bool, T> seems fine to me.
Regards,
Luke
I'm failing to see why optional should be slow. I use it extensively, POD
types included, and I don't consider pair<bool, T> as a valid replacement.
I'll be glad if it gets optimized for POD types, why not?
> Optional was implemented to be safe and slow because it was targeting safe and slow use cases.
You are saying it is *deliberately* slow??
> For POD types and anything that has a default constructor a std::pair<bool, T> seems fine to me.
I don't want to write a different style of code depending on whether
or not a type T is default constructible. I can't easily pass things
like this to templates because I have to write special cases all over
the place. Optional models my intent.
I want to take advantage of RVO.
Some things that are default constructible are still very expensive to
construct (such as std::deque under gcc).
When you initialize class members, do you use member intializer lists
for default constructible types or do you just throw a bunch of
assignments in the body of the constructor? If the former, why do you
do it, given that default construction followed by assignment "seems
fine" to you?
--
Nevin ":-)" Liber <mailto:ne...@eviloverlord.com> (847) 691-1404
> Hi, I've recently created an improved internal version of boost::optional to
> help workaround two issues:
> - suboptimal codegen
> - concurrent access.
Your changes sound interesting! (I'm not as sure about the
"concurrent access" stuff, but only because I haven't given it much
thought yet.)
Regards,
--
Nevin ":-)" Liber <mailto:ne...@eviloverlord.com> (847) 691-1404
_______________________________________________
AFAIK bool and pointer aren't the same size. How can it still take the
same amount of space?
Olaf
My guess would be that the compiler promotes the size of a bool to that of
the native word size of the machine because the ease and speed of aligned
memory access outweigh the 'size savings' (as typically your object is
going to need to occupy an entire register, word on the stack, etc --
except when in an array, but that's actually another reason you want to
have the size of the object promoted, as again, unaligned memory access is
slow).
Afaik though, in code it will typically still be treated as if it were e.g.
1 byte on x86 (using the AL register instead of EAX), and simply ignoring
the high portion of the register.
Note: Not a compiler/optimization/cpu/etc expert. This is just my amateur
'guess'. If you are really curious, just compile a test and disassemble it
with GDB/WinDbg/IDA/etc, testing the codegen for various scenarios and
optimization flags.
> I don't personally think that the style of programming that optional
> is intended for is suitable for high performance/performance critical
> situations in the first place.
Why not? It seems like a great candidate for common compiler
optimizations.
> Pass by reference and return a bool for a conditional return value.
> Pass the bool and the object separately for a conditional argument.
> Pass or return a pointer and check if it is null. Yes, my advice
> really is to not use optional if you want performance.
Why?
> Even if we did everything you can think of to make optional fast you
> are still better off designing your interfaces in such a way that you
> don't need it if your goal is performance.
Why do you say that?
--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com
on Fri Jan 27 2012, Domagoj Saric <domagoj.saric-AT-littleendian.com> wrote:
> a) the lifetime management bool was changed into a properly typed pointer (this
> actually takes the same amount of space while it provides a no-op get_ptr()
> member function as well as easier debugging as the contents of optional can
> now clearly be seen through the pointer, as opposed to gibberish in an opaque
> storage array)
Seems to me this potentially makes optional<char> much bigger. No?
> b) added another conditional constructor that accepts an in-place factory
> c) uses the safe bool idiom implementation from Boost.Range (which generates
> better code on pre MSVC10 compilers)
> d) skips redundant/dead stores of marking itself as uninitialised [including but
> limited to, in its destructor (if it has one)]
> e) streamlined internal assign paths to help the compiler avoid unnecessary
> branching
> f) added direct_create() and direct_destroy() member functions that allow the
> user to bypass the internal lifetime management (they only assert correct
> usage) in situations where the user's own external logic already implicitly
> knows the state of the optional
> g) optional now declares and defines a destructor only if the contained type has
> a non-trivial destructor (this prevents the compiler from detecting false EH
> states and thus generating bogus EH code)
> h) optional marks itself as uninitialised _before_ calling the contained
> object's destructor (this makes it a little more robust in race conditions;
I generally disagree with this sort of defensive programming. Won't it just
mask bugs?
Please tell me that at least *some* C++ compiler does that nowadays...?
--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com
All Linux compilers on x64 platforms follow the AMD64 ABI, possibly with minor variations/bugs. This ABI specifies that classes are passed in registers if
- they are trivially copyable and destructible (optional should be specialized for types that fulfill these criteria to ensure this),
- they have no virtual functions or bases,
- they are smaller than 2 qwords (4 qwords if all members are float, double, or SSE types), and
- they don't contain any weird stuff, like 80-bit long doubles or unaligned fields.
The Mac ABI for x64 is very close, though I don't know the differences.
The Win64 ABI is far less nice about registers. It passes the first four arguments in registers, and spills everything else onto the stack. It does not pack multiple values into a register. If a value is larger than 8 bytes, it is not split across registers. The ABI description says that "aggregates" can be passed in registers, but it doesn't elaborate on whether this refers to the C++ definition of aggregates (unlikely!) or whatever else the definition is. It sounds pretty useless.
I'm not aware of any x86-32 calling convention that passes classes of any kind in registers.
Sebastian
Correcting myself: the Common C++ ABI for x86-32 actually specifies that trivially copyable and destructible classes are treated just like simple values for parameter passing, so they can be passed and returned in registers. Of course, the far smaller register file of x86-32 makes that still not very useful.
>Why not? It seems like a great candidate for common compiler
>optimizations.
To some extent it depends what style of programming optional is intended for. What I had in mind was the highly object oriented defensive programming style that emphasizes safety often at the expense of performance in vogue around the time Java came out.
>> Pass by reference and return a bool for a conditional return value.
>> Pass the bool and the object separately for a conditional argument.
>> Pass or return a pointer and check if it is null. Yes, my advice
>> really is to not use optional if you want performance.
>Why?
I like pass by reference and return a bool over returning an optional for performance because we allocate memory for the result of the function outside of the function call and there is no transfer of ownership of the result. Even with move semantics, you have just changed an unnecessary copy into cheaper unnecessary move.
>> Even if we did everything you can think of to make optional fast you
>> are still better off designing your interfaces in such a way that you
>> don't need it if your goal is performance.
>Why do you say that?
I don't trust the compiler to always inline what I want it to if it is busy inlining the optional function calls. The compiler heuristics for inlining can get overloaded and confused as the number of nested inline functions grows. There are no inline function calls to check the bool return value of a function or use the reference passed to the function. I believe that getting the ownership of the data at the right place in the code for performance is preferable to transferring ownership, even with move. It also helps the compiler optimize to be given less code that looks more like what you want the compiler to produce at the end so that it has less opportunity to fail to give you what you wanted. We can imagine an arbitrarily good compiler that always does what we intend, but a compiler that generates a branch for "if(m_initialize) m_initialize = false" is clearly not the ideal compiler we imagine.
I did come around to supporting optimization of optional, it might as well be as good of a trade off between safety and performance as we can make it. I don't use optional myself because I prefer alternative syntax for simplicity reasons, convenience, fewer dependencies, etc, and not even performance reasons.
Regards,
Luke
> From: Dave Abrahams
> >> I don't personally think that the style of programming that optional
> >> is intended for is suitable for high performance/performance critical
> >> situations in the first place.
>
> >Why not? It seems like a great candidate for common compiler
> >optimizations.
>
> To some extent it depends what style of programming optional is intended
> for. What I had in mind was the highly object oriented defensive
> programming style that emphasizes safety often at the expense of
> performance in vogue around the time Java came out.
>
>
But if we can maintain the same level of safety, while at the same time
increasing efficiency, doesn't that benefit everyone?
Personally, I like returning values rather than modifying arguments. But more importantly, the caller might not even be able to construct that object to be passed by reference, due to lack of access to an appropriate combination of constructor and initialization arguments, such as when the class has no default constructor.
> Even with move semantics, you have just changed an unnecessary copy into cheaper unnecessary move.
If one cares about performance and one's compiler is not capable of doing RVO for optionals, perhaps one should be looking for a better compiler, and not just for better handling of optionals.
Does that also apply to functions that aren't exported? I'd assume the
compiler is free to do whatever it wants in that case.
Olaf
> On Jan 30, 2012, at 3:49 PM, Simonson, Lucanus J wrote:
>> I like pass by reference and return a bool over returning an
>> optional for performance because we allocate memory for the result
>> of the function outside of the function call and there is no
>> transfer of ownership of the result.
>
> Personally, I like returning values rather than modifying arguments.
> But more importantly, the caller might not even be able to construct
> that object to be passed by reference, due to lack of access to an
> appropriate combination of constructor and initialization arguments,
> such as when the class has no default constructor.
>
>> Even with move semantics, you have just changed an unnecessary copy into cheaper unnecessary move.
>
> If one cares about performance and one's compiler is not capable of
> doing RVO for optionals, perhaps one should be looking for a better
> compiler, and not just for better handling of optionals.
IIRC, RVO is now mandated where it's possible, so the whole move
argument is kina moot.
--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com
> a) the lifetime management bool was changed into a properly typed pointer (this
> actually takes the same amount of space while it provides a no-op get_ptr()
> member function as well as easier debugging as the contents of optional can
> now clearly be seen through the pointer, as opposed to gibberish in an opaque
> storage array)
I'd support this only if were configurable. It takes more space for small or
non-word-aligned data. It might be more expensive on some systems to calculate
the address and store it.
I did think that about defaulting to int_fast8_t for the bool if its alignment>=
alignment of T. On my x86 system it's still 1 byte though. On some system it
might help.
It would also break has_trivial_copy. If someone was naughty and memcopied them,
the new version would lead to a very hard to find bug.
As for the debugger the new C++ allows for a union to contain a class. So if
a placeholder implemention using such a union would show the data in debug.
> d) skips redundant/dead stores of marking itself as uninitialised [including but
> limited to, in its destructor (if it has one)]
> e) streamlined internal assign paths to help the compiler avoid unnecessary
> branching
Sounds like what I'm after.
> f) added direct_create() and direct_destroy() member functions that allow the
> user to bypass the internal lifetime management (they only assert correct
> usage) in situations where the user's own external logic already implicitly
> knows the state of the optional
Sounds good. I also wanted these.
> g) optional now declares and defines a destructor only if the contained type has
> a non-trivial destructor (this prevents the compiler from detecting false EH
> states and thus generating bogus EH code)
Yes, that's what I want.
> h) optional marks itself as uninitialised _before_ calling the contained
> object's destructor (this makes it a little more robust in race conditions;
> it is of course not a complete solution for such scenarios, those require
> external "help" and/or (m)-reference counting to be implemented)
Seems to contradict (g). I'd support something like that only if it can be
configured out. Maybe there's some case completely out of optional's scope
where you use atomic ops.
If you factor out the aligned storage you can build something else that does
ref-counting or a thread safe state machine or whatever.
> i) extracted the "placeholder" functionality into a standalone class (basically
> what would be left of optional<> if the lifetime management "bool" member and
> logic was removed) so that it can be reused (e.g. for singleton like classes,
> or when more complex custom lifetime management is required)
I 100% agree with this. I think there should be one placeholder implementation.
I think boost::function should use it as well. I think it may be useful to users.
> k) the lifetime management pointer is now stored after the actual contained
> object (this helps in avoiding more complex/offset addressing when accessing
> optionals through pointers w/o checking whether they are initialised)
Seems weird. If the front of T is more likely to be used (and old char buffer),
your pointer may wind up in a different cache line.
> o) avoid branching in assignment and copy construction of optionals that hold
> PODs smaller than N * sizeof( void * ) where N is some small number
Again it would be cool if the user had control over this.
I'm going to have to check out your code.
So the big thing I take away from all this it would be really nice if some things
were configurable. How do we do that without breaking code?
Changing the signature to optional<T,Properties=optional_traits<T> >, might
break code that uses boost::optional as a template template parameter.
You could just refer to optional_traits inside and force the user to specialize
it for his T, but that could create violations of the one definition rule.
Also is it OK for optional to depend on enable_if/SFINAE and type traits?
Chris
A "language lawyer" might be more precise but, for optional<T>:
- if the bool member is before the T member the compiler has to add
alignment_of<T>::value - sizeof( bool ) bytes of padding after the bool so that
the T member would be properly aligned
- if the bool member is after the T member the compiler has to add the same
amount of padding after the bool member to satisfy the requirement that there
are no "holes" between individual (properly aligned) instances of optional<T> in
arrays of optional<T>...
IOW, my statement in (a) does not for example hold for chars or shorts...
--
"What Huxley teaches is that in the age of advanced technology, spiritual
devastation is more likely to come from an enemy with a smiling face than
from one whose countenance exudes suspicion and hate."
Neil Postman
Quite possibly, you'll need to introduce a new type that provides the configurability you want, while hardcoding backward compatible choices for the existing optional.
_____
Rob Stewart robert....@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.
True, I was planning on automatically deciding between bool and pointer based on
sizeof( T ) after adding lifetime management policy support (m)...
> It might be more expensive on some systems to calculate
> the address and store it.
How? You have to fetch the address either way...
> It would also break has_trivial_copy. If someone was naughty and memcopied them,
> the new version would lead to a very hard to find bug.
True, didn't think about trivial copy until Sebastian outlined the
pass-POD-in-register requirements of the AMD x64 ABI. WRT to this it boils down
to whether you want a no-op get_ptr() or your platform and compiler actually
support passing PODs in registers _and_ most of the types you store in optionals
actually satisfy the compiler/ABI requirements for that _and_ you mostly pass
and return those optionals by value...
> As for the debugger the new C++ allows for a union to contain a class. So if
> a placeholder implemention using such a union would show the data in debug.
But the pointer approach would also work with "real world" compilers ;)
>> h) optional marks itself as uninitialised _before_ calling the contained
>> object's destructor (this makes it a little more robust in race conditions;
>> it is of course not a complete solution for such scenarios, those require
>> external "help" and/or (m)-reference counting to be implemented)
> Seems to contradict (g). I'd support something like that only if it can be
> configured out. Maybe there's some case completely out of optional's scope
> where you use atomic ops.
It doesn't (contradict (g)), this applies only to situations where you actually
have to mark the optional as empty (such as when reset() is called).
> If you factor out the aligned storage you can build something else that does
> ref-counting or a thread safe state machine or whatever.
With (m) I'd rather (in some distant future:) add a refcounting policy to
optional (or some future underlying more generic class) so that users don't have
to reimplement this...
>> k) the lifetime management pointer is now stored after the actual contained
>> object (this helps in avoiding more complex/offset addressing when accessing
>> optionals through pointers w/o checking whether they are initialised)
> Seems weird. If the front of T is more likely to be used (and old char buffer),
> your pointer may wind up in a different cache line.
Well yes, as I said this benefits only the cases where the pointer/bool is not
accessed (when an optional is accessed through a pointer/reference). IOW in
99.9% of real world cases the point is quite moot but it did make sense at a
particular stage of a project I'm working on (when you have dozens of hundreds
of template generated functions you can actually measure savings in code size
when you do even such micromanagement). It no longer matters for me but the
layout of optional2 is still like that (currently) purely because it turned out
like that (in the current stage of development) so I wrote point (k) nonetheless
just for the feedback ;)
> So the big thing I take away from all this it would be really nice if some things
> were configurable. How do we do that without breaking code?
>
> Changing the signature to optional<T,Properties=optional_traits<T> >, might
> break code that uses boost::optional as a template template parameter.
Judging for example from the rationale for the lack of smart_ptr configurability
or from the feedback I got for my improved boost::function proposal, it would be
very difficult for this type of configurability for optional to get accepted.
I was rather planing on making the best of optional with automatic/self
configuration based on properties of T and then later (in a galaxy far far
away:) propse an underlying library ("smart resource" or something like that),
that would separate the lifetime management and storage concerns in a maximally
configurable manner, on top of which traditional optional smart_ptr could be
built...
--
"What Huxley teaches is that in the age of advanced technology, spiritual
devastation is more likely to come from an enemy with a smiling face than
from one whose countenance exudes suspicion and hate."
Neil Postman
Or, optimal should not be optional (everything should be optimal :D).
>> a) the lifetime management bool was changed into a properly typed pointer (this
>> actually takes the same amount of space while it provides a no-op get_ptr()
>> member function as well as easier debugging as the contents of optional can
>> now clearly be seen through the pointer, as opposed to gibberish in an opaque
>> storage array)
>
> Seems to me this potentially makes optional<char> much bigger. No?
True (see my answer to Olaf and Christofer).
>> h) optional marks itself as uninitialised _before_ calling the contained
>> object's destructor (this makes it a little more robust in race conditions;
>
> I generally disagree with this sort of defensive programming. Won't it just
> mask bugs?
I generally disagree too but in cases where there is actual "defensive
programming" i.e. handling of invalid/buggy usage. The typical example is code
that asserts that a pointer is not null and then handles the case if it is.
There is none of that here. Imaging writing optional from scratch, at one point
you would have to decide the same thing, when to mark the optional as empty -
before or after calling the destructor. Either way you choose won't make a
difference (semantic or performance wise) for correct code. Incorrect code will
crash less. Isn't that a good thing (considering there is no actual handling of
incorrect code)?
Considering that "there is no bug free software" (one wonders about laser brain
surgery robots :), wouldn't it be better to "a priori crash less" and add
separate sanity checks for invalid concurrent access in order to catch bugs
(obviously this is more work and I don't know if any Boost component does
anything like this actually)?
Perhaps there is no "right" answer to this question and its more a matter of
preference so consider the above as "my 2 cents"...
ps. and yes, I forgot the buzz: (p) rvalue references support :)
--
"What Huxley teaches is that in the age of advanced technology, spiritual
devastation is more likely to come from an enemy with a smiling face than
from one whose countenance exudes suspicion and hate."
Neil Postman
Thanks for the summary (didn't know there was a separate OS X x64 ABI).
>> The Win64 ABI is far less nice about registers. It passes the first four arguments in registers, and spills everything else onto the stack. It does not pack multiple values into a register. If a value is larger than 8 bytes, it is not split across registers. The ABI description says that "aggregates" can be passed in registers, but it doesn't elaborate on whether this refers to the C++ definition of aggregates (unlikely!) or whatever else the definition is. It sounds pretty useless.
Right, the Windows/MSVC x64 ABI is a major !?wth!?...I just can't think of a
reason why they had to invest resources into making their own ABI that is so
complicated and so inferior to the AMD proposed one (e.g. you can't pass an SSE
vector through an XMM register??).
> Correcting myself: the Common C++ ABI for x86-32 actually specifies that trivially copyable and destructible classes are treated just like simple values for parameter passing, so they can be passed and returned in registers. Of course, the far smaller register file of x86-32 makes that still not very useful.
Unfortunately I have never seen MSVC pass or return any struct through registers
even though it has interprocedural optimizations and link time code generation
capabilities so it can "invent" (as the documentation claims) its own calling
conventions for non exported functions. Don't know whether any other x86
compiler is able to do so...
In any case, the problem is that there is no nearly
portable/standard/wide-spread way (pragma, decl specifier...) to tell the
compiler to return small PODs in registers, especially not just for a particular
function and/or POD type. GCC has -freg-struct-return but that seems nearly
useless because it applies to the whole binary and so it requires the OS to be
built with that option.
--
"What Huxley teaches is that in the age of advanced technology, spiritual
devastation is more likely to come from an enemy with a smiling face than
from one whose countenance exudes suspicion and hate."
Neil Postman
Isn't this about the destructor of optional? Marking it as empty seems
unneeded there.
Olaf
I did some experimenting in my own codebase with a "array_vector" which acts
like vector constructs things when they're added, but like boost::array uses
a fixed size array.
I tested the techniques I would use to improve optional. So I think I can
deliver this very small set of goals cleanly:
1) ~optional doesn't set m_initialized.
2) has_trivial_destructor<T> implies has_trivial_destructor<optional<T> >
3) has_has_trivial_copy<T> and has_trivial_assign<T> implies them optional
unless sizeof(T) exceeds some constant max_trivial_copy_Size, which
can also be overridden.
4) I'll define a optional_traits<T> with defaults and an
optional_with_traits<T,Traits=optional_traits<T> >
which can be used to make optionals which override features and from which
optional<T> will derive. That's the best compromise if I can't change
the signature of optional (Is Robert Stewart right?). I think we should use
the traits technique for any new libraries.
Thanks Sebastian Redl and Domagoj Saric for pointing out that (2) and (3)
will may help some compilers put cheap optionals in registers.
Shall I continue? Should I make branch or do it in trunk?
Do I understand it correctly that optional_with_traits is an advanced
replacement for optional? If so, will the good old optional be optimized? I
think, it is possible to optimize the current optional without changing its
signature if we specialize optional_detail::optional_base on the types or
traits we're interested in.
BTW, I would really like to see optional< T& > optimized to store T*
internally.
> Shall I continue? Should I make branch or do it in trunk?
I think, a branch or sandbox is a good start.
That might in rare cases it could break user code like:
mpl::quote1<optional>
Personaly I doubt that this is such an issue, but have the best of both
worlds I can define a temporary "optional_with_traits" which when boost
goes to 2.0 we could deprecate and add the parameter to optional.
template<typename T, typename Traits=optional_traits<T> >
class optional_with_traits;
template<typename T>
class optional : public optional_with_traits<T> {...};
Do we gurantee boost users that templates will never add default parameters?
> BTW, I would really like to see optional< T& > optimized to store T*
> internally.
I'm going to say something provacative here. I agree with Lucanus. I see no
reason for optional<T&>. As far I can tell you could use a T*. The only
justification I can think of is on system without memory protection you can
build checks into operator*().
Maybe if you're mixing code with old libraries where T* might imply ownership
you might use optional<T&> to imply no ownership and some temporary validity.
Perhaps we should define a new "smart pointer" called dumb_ptr<T> which can't
be assigned into auto_ptr,unique_ptr,shared_ptr, or any pointer type which
implies ownership.
Maybe I'm missing something, but I don't see the justification.
Chris
This would be a breaking change, so yes, such a change should be avoided if
possible. Personnally, I didn't grasp the benefit of using these traits to
justify the breakage. What will be traits used for, what will it provide?
> > BTW, I would really like to see optional< T& > optimized to store T*
> > internally.
>
> I'm going to say something provacative here. I agree with Lucanus. I see no
> reason for optional<T&>. As far I can tell you could use a T*. The only
> justification I can think of is on system without memory protection you can
> build checks into operator*().
>
> Maybe I'm missing something, but I don't see the justification.
optional< T& > is a useful thing when you want to apply operators (such as
relation operators or streaming) to the referred value. In generic code you
don't have to specialize for pointers to do the right thing. I'm going to use
this property in my Boost.Log library.
+1 here. Please keep the interfaces the same, unless you have a
*very* compelling reason not to.
(Unless, of course, you are one of those who *likes* vector<bool>... :-))
--
Nevin ":-)" Liber <mailto:ne...@eviloverlord.com> (847) 691-1404
_______________________________________________
+1.
optional<T*> cannot optimize the bool away, because it can be null. So
optional<T&> is both more efficeint and more handy.
-Thorsten
Andrey, would you mind giving us a short example of how you want to use an
optional reference? I am in the middle of designing the new optional
interface for TR2, and came to the conclusion that in order to avoid
counter-intuitive semantics for optional reference assignment, I had better
remove it at all; that is, optional references are to be limited: they
should provide no assignment. You could still use optional values and
optional references in generic code but with reduced interface:
template <typename T> // T is a ref or a value
void use( std::tr2::optional<T> opt, T nval )
{
if (opt) {
std::cout << *out; // fine
*opt = nval; // fine, assigning T's not optionals
opt = nval; // invalid if T is a ref
opt = opt; // invalid if T is a ref
}
if (needToRebindAReference()) {
opt.emplace(nval); // valid - always rebinds
}
};
Would such a limited interface as I described above be enough for your
generic usage of optional?
Regards,
&rzej
My code is not yet set in stone, and actually, it doesn't use optional
references yet. But it looks like it should be enough for my needs,
assuming optional<T&> is still going to support the same relation
operators as optional<T> with the same semantics. The general idea is
that I build a Boost.Phoenix expression (usually, a predicate or a
streaming expression) where some of the terminals may result in
optional references (references allow to avoid expensive copying).
When the expression is executed, it should operate on the referred
values, if present, or execute a fallback logic otherwise. In my
context, the required behavior is almost exactly what optional<T&>
provides.
I have a question though. Why prohibit opt = opt assignment? It looks
quite safe and has a fairly obvious behavior. If I have an optional
reference as a member of my class, the lack of assignment in optional
would force me to define a custom assignment operator for my class.
This seems to be an unnecessary requirement. Also, in the source code
I dealt with I often saw people writing something like opt =
optional<T>() to clear the value. This would break with references for
no apparent reason.
The optional in sandbox (that passes regression tests) already does 1 and 2
(among many other things) so doing it from scratch again would be reinventing
the wheel.
ad 3) I would agree to such a compromise: that a bool be used for small PODs (so
that they get trivial copy and assign) and a pointer for everything else (so
that these get a no-op get_ptr() and nice debugging)...
[In my version PODs always/implicitly get "nice debugging" regardless of the
lifetime management implementation (bool/pointer/...).]
ad 4) As said before, even though my personal prima facie stance is always "the
more configurability the better", it is highly unlikely (from reasons previously
given) that changing optional's signature would pass.
Given that, the best workaround IMO for such "ancient"/"written in stone"
constructs that suffer from the "Joe Sixpack" approach, i.e. they are good
enough for 90% use cases, is to:
- create a separate configurable construct and use it as an implementation
detail of the original construct that maximally auto-configures based on T
(improving the "good enough percentage" to "98%")
- provide global configuration (that overrides auto-configuration) for the
original construct (improving the "good enough percentage" to "99.8%")
...and the remaining "0.2%" can use the new construct directly...
So far this corresponds to your optional_with_traits approach except that I
don't think that providing global configuration by overriding/specializing the
default traits is the correct approach. As you noted, this can violate the ODR
and AFAIK users are not used that changing a _type_ can violate the ODR and
change the behaviour of another type. I'd rather use macros for that (e.g.
#define BOOST_OPTIONAL_MAX_BRANCHLESS_COPY_SIZE 4 * sizeof( void * )) because
programmers are already used/"trained" to be careful with macros WRT to the ODR
_and_ because there already exist tools/compilers which can detect macro ODR
violations at link time (e.g. MSVC10)...
--
"What Huxley teaches is that in the age of advanced technology, spiritual
devastation is more likely to come from an enemy with a smiling face than
from one whose countenance exudes suspicion and hate."
Neil Postman
IMO it seems that, yes, you are making the same mistake as Lucanus, thinking
about "The Universe" only as/through your POV of your personal problem domain:
a) (optional models optionally holding an object) + (objects can be held by
value and by reference) = optional<T&> perfectly logical
b) creating special cases (e.g. for T&) creates special problems in generic code
--
"What Huxley teaches is that in the age of advanced technology, spiritual
devastation is more likely to come from an enemy with a smiling face than
from one whose countenance exudes suspicion and hate."
Neil Postman
Actually I forgot a, personally, much more important reason why placing the
contained object at the beginning/same address as optional itself was more
desirable.
My optional use cases generally fall into two categories, optionals of
fundamental types (bools, ints and floats) and small PODs and optionals of
nontrivial GUI objects. The latter case usually looks like this (a compile-time
generated Model-View-Controller design where the "controller" is "short
circuited" for simplicity and efficiency):
template <typename T>
class Model
{
optional<View<Model>> optionalGUI_;
};
Without a "controller", View<Model> needs to access its Model instance and
instead of storing a Model pointer it can simply deduce its address from its own
address (knowing that Views only ever exist as members of Models). When View is
inside an optional it first needs to calculate the address of
optional<View<Model>> from its own address and then the address of the Model
parent from the optional address. And the crux of the problem is: to calculate
the address of the optional it needs to know the layout of optional...
(Incidentally the current/original optional allowed for an ugly way to calculate
the offset of the contained object by using a helper class that derives from
optional_base...)
First, let me show why opt = nval; is controversial. Current semantics for
boost::optional<T&> is to rebind on assignment, which means that in the
following code:
int i = 1;
int j = 2;
optional<int&> oi = i;
oi = j;
The effect of this program is that i remains 1, j remains 2 and oi holds a
reference to j. This is surprising to those that think of optional as
delayed initialization. I lean towards disabling opt = opt because it is
very similar to opt = nval;
int i = 1;
int j = 2;
optional<int&> oi = i;
optional<int&> oj = j;
i = j;
The effect here is that i remains 1, j remains 2 and oi and oj both hold a
reference to j. You may find it less surprising but if you think of
optional reference as a regular reference that is initialized a bit later,
the behavior is not what you would expect.
Note that if you store a normal reference to int as class member, you
already have to write your assignment yourself. changing a normal reference
to an optional reference should not come as something irregular.
I understand and agree that opt = nval may be ambiguous and even dangerous.
> I lean towards disabling opt = opt because it is
> very similar to opt = nval;
>
> int i = 1;
> int j = 2;
> optional<int&> oi = i;
> optional<int&> oj = j;
> i = j;
>
> The effect here is that i remains 1, j remains 2 and oi and oj both hold a
> reference to j. You may find it less surprising but if you think of
> optional reference as a regular reference that is initialized a bit later,
> the behavior is not what you would expect.
I see your point but, IMHO, optional<T&> is too different from normal
references to attempt to draw this association. After all,
optional<T&> is an object in the sense it has internal state and you
can take its address while T& is not (one may call it an alias of an
object). It is therefore logical that optional<T&>::operator= operates
on the object (i.e. on the optional contents) and hypothetical
T&::operator= operates on the referred object.
> Note that if you store a normal reference to int as class member, you
> already have to write your assignment yourself. changing a normal reference
> to an optional reference should not come as something irregular.
Again, I tend to see optional<T&> as an object, with no apparent
reason why it cannot be assigned to.
I understand (I think) your point of view. Let me clarify one thing. I am
thinking of disabling the assignment not because I think it does not belong
to references, but because there are two ways of implementing it, and
implementing it either way would surprise a different group of programmers.
And this would be a "run-time surprise". Instead my choice (not necessarily
the best one) is to provide a "compile-time surprise".
With your view of optional reference (if I got it right it is a pointer
with a somewhat different syntax) your expectation of rebinding assignment
comes as natural. With a different model of optional reference a
non-rebinding assignment comes as more natural. I believe that
optional<reference_wrapper<T>> would serve your purpose best. Or would it
also introduce the lack of uniformity?
Actually, you could just take the optional_traits as the first parameter. So you define
optional<T> or optional<optional_traits<my_traits<T> > >. Then optional would be
specialized for optional_traits that will get the user-defined traits.
> I believe that
> optional<reference_wrapper<T>> would serve your purpose best. Or would it
> also introduce the lack of uniformity?
Interesting. I'm not sure it's going to work because reference_wrapper won't
have T's operators. The compiler may find the operators via ADL, but calling
them will require an implicit cast from reference_wrapper to T&, which may
mess up overload resolution. I think, optional<T&> is closer to my needs.
One additional note on this. I would like optional<T&> to be perfectly
implementable as a wrapper around T*. optional<reference_wrapper<T>> does not
allow this, at least not with its generic interface, since optional<T>::get()
returns T&, which effectively forces optional<reference_wrapper<T>> to store
reference_wrapper internally (along with the value presence flag).
Specializing optional on std::reference_wrapper does not solve the problem
entirely because there is also boost::reference_wrapper. It would be odd if
optional worked differently with different reference_wrappers.
Or I might just be babbling :) That's what traits are for (when per type as
opposed to per instantiation configuration is enough/desired)...
(possibly a bit of work to still get the special trivial destructor and
assignment functionality in the specialization, but) Clever ;)
--
"What Huxley teaches is that in the age of advanced technology, spiritual
devastation is more likely to come from an enemy with a smiling face than
from one whose countenance exudes suspicion and hate."
Neil Postman
_______________________________________________
> From: Domagoj Saric <dsa...@gmail.com>
> To: bo...@lists.boost.org
> Cc:
> Sent: Thursday, February 9, 2012 2:09 PM
> Subject: Re: [boost] [optional] generates unnessesary code for trivial types
>
>& quot;paul Fultz" je napisao u poruci interesnoj
> grupi:1328802527.475...@web112602.mail.gq1.yahoo.com...
>> Actually, you could just take the optional_traits as the first parameter.
> So you define
>> optional<T> or optional<optional_traits<my_traits<T> >
>> . Then optional would be
>> specialized for optional_traits that will get the user-defined traits.
>
> (possibly a bit of work to still get the special trivial destructor and
> assignment functionality in the specialization, but) Clever ;)
Actually, you could use an optional_impl class, that always uses traits. And then
when the user is not passing in their own traits you would pass in default_traits.
Something like this:
template<class T>
class optional : public optional_impl<default_traits<T> >
{
//Foward constructors, and operators
};
template<class Trait>
class optional<optional_traits<Trait> : public optional_impl<Trait >
{
//Foward constructors, and operators
};
Then the assign operator would forward to an assign method in the base class.
Of course, this would mean that if T is trivially assignable, optional<T> would not
be trivially assignable. Was that one of your goals of the original design?
Huh? How does oi come to hold a reference to j?
Regards,
Nate
> > The effect here is that i remains 1, j remains 2 and oi and oj both
hold a
> > reference to j. You may find it less surprising but if you think of
> > optional reference as a regular reference that is initialized a bit
later,
> > the behavior is not what you would expect.
>
> Huh? How does oi come to hold a reference to j?
Apologies, I meant to say
int i = 1;
int j = 2;
optional<int&> oi = i;
optional<int&> oj = j;
oi = oj;
Boost.Optional documentation explains it better:
http://www.boost.org/doc/libs/1_48_0/libs/optional/doc/html/boost_optional/rebinding_semantics_for_assignment_of_optional_references.html
Regards,
&rzej
That new optional might break old code:
template<typename T>
void print(const optional<T>& o){
if(o){
T copy=*o;
std::cout<<copy;
}
}
That copy is going to be a optional_traits<>. My method would also force you to
add traits to the signature, but at least you wouldn't need to write two specializations:
template<typename T,typename Traits>
void print(const optional_with_traits<T, Traits >& o);
Se have to ask ourselves if configuration is worth it. I think sometimes you don't
find that out until it's too late and someone wants a option.
Things I can think of making configurable:
* bool type - a word might be faster on risk machines
* use_trivial_destruction - there might be a case for overridding this
* use_trivial_copy - cann't see a case for overriding this
* bool_first
* alignment - maybe 4 byte alignment is legal but 8 byte is faster
* enable_assertions
Chris
> IMO it seems that, yes, you are making the same mistake as Lucanus, thinking
> about "The Universe" only as/through your POV of your personal problem domain:
> a) (optional models optionally holding an object) + (objects can be held by
> value and by reference) = optional<T&> perfectly logical
I'm really not. Give me a use case. Do they make sense being passed as
parameters/returns? Do they make sense being stored?
I just see some corner case of optional being turned into a replacement for regular
pointer.
Why not just make dumb_ptr<T>? If you want you can make it streamable and
give it clean construction.
What is it you really want from optional<T&>? Does anyone use it a lot?
Is it like std::vector<bool>?
> b) creating special cases (e.g. for T&) creates special problems in generic code
My point exactly. I'd make it work for completeness but not bother to optimize.
While my main concern is the interface, here are the reasons I'd like
to see it optimized:
1. If the implementation needs to special case references anyway.
2. The optimization is really easy; it probably takes less time to
implement and test it than any one of us has spent writing any of the
emails in this chain to discuss it.
3. The question on this particular optimization shows up on the Boost
mailing lists like clockwork. The only thing that will stop it is by
implementing the optimization, and that in itself is doing Boost
community at large a great service.
--
Nevin ":-)" Liber <mailto:ne...@eviloverlord.com> (847) 691-1404
_______________________________________________
Hi People,
As the author of Boost.Optional I wanted to let you know that I am following the
entire thread (as all other threads about the library) even if I don't join the
debate.
I concurr with Nevin that at least some of the optimizations presented are
embarrasingly trivial to even worth any discussion.
OTOH, this is not the only update that the library needs, so be patient.
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.
Best
--
Fernando Cacciola
SciSoft Consulting, Founder
http://www.scisoft-consulting.com
I'm very glad to hear it!
> Actually, you could just take the optional_traits as the first parameter. So you define
> optional<T> or optional<optional_traits<my_traits<T> > >. Then optional would be
> specialized for optional_traits that will get the user-defined traits.
Be aware that tricks like this start to break down in generic contexts,
as optional_traits<T> specializations are treated differently by
optional from all other types.
--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com
> I am
> thinking of disabling the assignment not because I think it does not belong
> to references, but because there are two ways of implementing it, and
> implementing it either way would surprise a different group of
> programmers.
> And this would be a "run-time surprise". Instead my choice (not necessarily
> the best one) is to provide a "compile-time surprise".
If value semantics are achievable for this kind of optional, I think
it's better to supply that and let the people who don't expect value
semantics be surprised at runtime.
IMO-ly y'rs,
--
Dave Abrahams
BoostPro Computing
http://www.boostpro.com
> 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.
Side question: is streamablity going to be in your proposal?
Chris
> 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
It is very difficult to provide a non-surprising/non-controversial
behavior. While I am not happy with my solution, I would also have concerns
about "run-time surprise" because:
First, there are two ways of implementing optional reference assignment
(rebinding or not rebinding) and it seams (from the past discussions in
this group) that either is equally counter-intuitive, and it is not clear
which one to choose. It would have to be an absolutely arbitrary choice.
People are strongly convinced about their views and it is not possible to
come up with a reasonable compromise. In fact it is the optional reference
assignment that prevented the addition of Optional to ISO C++ the first
time (http://lists.boost.org/Archives/boost/2005/10/95120.php).
Second, it is difficult to say about value semantics here. References
themselves are not really value semantic (at least according to my
understanding of the term): they have shallow copy constructor and deep
copy assignment. Even if it is disputable for native references, it is more
so for optional references, which removes the "special" aspects of
references, like extending the life-time of a temporary, reference
semantics, or no-storage requirement. We would be building value semantics
on a non-value-semantic type. Or in other words, I am concerned that we
would only be providing "value-semantics syntax" but not "value-semantics
semantics". But we would violate the axioms of value semantic types (like
the "separation of the value").
> But the pointer approach would also work with "real world" compilers
> ;)
This isn't for science fiction. Gcc says they support n2544 since 4.6.
http://gcc.gnu.org/projects/cxx0x.html
http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2008/n2544.pdf
I just tried it out:
template<typename T>
union placeholder{
T v;
placeholder() {}
~placeholder (){}
};
...
placeholder<std::string> ps;
std::string s("hi");
new(&ps.v) std::string(s);
< Uninited happens to contain null. >
(gdb) print ps
$2 = {v = {static npos = 18446744073709551615,
_M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, _M_p = 0x0}}}
< Initied: >
(gdb) print ps
$3 = {v = {static npos = 18446744073709551615,
_M_dataplus = {<std::allocator<char>> = {<__gnu_cxx::new_allocator<char>> = {<No data fields>}, <No data fields>}, _M_p = 0x602028 "hi"}}}
I don't know what platform you're using, but it seems completely reasonable to #ifdef a version of the placeholder in that does this so people can debug easily.
Does that remove the main reason you wanted a pointer?
BTW I did look at your code some. The deconstructor mixin, it's the same technique I used! For the ctor I just define a intermediate class with either default copy and operator= or impled ones.
Chris
I'd do only << and forget about >>
--
Olaf
But even that appears like a non-obvious thing to me. Would other people
here in the list, and also developers in general agree that it is ok that
you have output operation but no input operation? I think that in the
standard library they always go in pairs. (I am not sure, though).
Also, how would you implement it? what should an uninitialized optional
print? Nothing? but how is having printed an uninitialized optional<int>
different that not having printed anything? A question mark? but how would
the following two be different:
optional<string> o1; // uninitialized
optional<string> o2 = "?";
cout << o1;
cout << o2;
If you propose to provide only output operation, then it looks like you
want this for some sort of logging. But perhaps it is better to have some
overloaded function toString() that converts all the types to strings.
Usually the string format that works for one program does not work for the
other. And there appears to be no natural/intuitive way of representing any
type (like optional) as text.
Regards,
&rzej
In my own code I often find myself defining only output operators. For things
more complex than enums or numerics I usually handle input with specialized
solutions, with tools line Boost.Spirit. As for the standard library, I'm not
sure but dos thread::id have an input operator?
> Also, how would you implement it? what should an uninitialized optional
> print? Nothing? but how is having printed an uninitialized optional<int>
> different that not having printed anything? A question mark? but how would
> the following two be different:
FWIW, boost::optional provides both input and output.
> If you propose to provide only output operation, then it looks like you
> want this for some sort of logging. But perhaps it is better to have some
> overloaded function toString() that converts all the types to strings.
> Usually the string format that works for one program does not work for the
> other. And there appears to be no natural/intuitive way of representing any
> type (like optional) as text.
I usually dislike toString and alike methods. I think, manipulators are more
flexible and modularized approach. Do you think it would be possible to
provide different manipulators to fulfill different IO requirements?
if (o1) os << *o1;
> what should an uninitialized optional
> print? Nothing? but how is having printed an uninitialized optional<int>
> different that not having printed anything?
> A question mark? but how would
> the following two be different:
They won't.
> If you propose to provide only output operation, then it looks like you
> want this for some sort of logging. But perhaps it is better to have some
> overloaded function toString() that converts all the types to strings.
Isn't that what << is for? :p
> Usually the string format that works for one program does not work for the
> other. And there appears to be no natural/intuitive way of representing any
> type (like optional) as text.
Right and that's not limited to optional.
Defining no IO seems ok too.
--
Olaf
Why? The poster child is database access, where every field can be
set or unset, and every set field has a value. Why would anyone want
to special case access to bool to use a different type?
> We could optimize optional<bool> to store ~0 to indicate unset.
Could you elaborate? I don't see how, at least not without changing
the interface, as a "dereferenced" optional<bool> has to return a
reference to bool whose address cannot change.
Please look at vector<bool> to see the trouble caused when people
changed the generic interface for a specific type.
> 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.
If there are debates about about the meaning of reassignment, then it
really is NOT equivalent to T*.
> 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.
Do you honestly think it is more work than was put into writing your
email message (let alone all the future threads that will inevitably
keep coming up)?
--
Nevin ":-)" Liber <mailto:ne...@eviloverlord.com> (847) 691-1404
_______________________________________________
> But even that appears like a non-obvious thing to me. Would other people
> here in the list, and also developers in general agree that it is ok that
> you have output operation but no input operation? I think that in the
> standard library they always go in pairs. (I am not sure, though).
I haven't followed this closely, but don't see a problem with
not being symmetrical here. The C++ Middleware Writer (CMW)
has asymmetric support for Boost Range types:
@out (boost::sub_range<std::vector<int32_t> >)
If you omit the @out or write @in on that line, the CMW
gives you an error message that ranges don't support
receive (input) functionality. It only made sense to me
to output via a range so that's all that's supported for now.
--
Brian Wood
Ebenezer Enterprises
http://webEbenezer.net
I'm happy Adele won all those Grammys. She deserves them
them I think.
I have checked the standard and indeed thread::id provides operator<<
without the matching operator>>, so this asymmetry would not be a precedent
in the standard. Although the situation with thread::id is speciffic. ID's
are assigned by the OS and trying to assign them by other means would
definitely be an error.
I also checked boost::optional and it provides both (I learned two new
things today). Uninitialized optional of any type renders "--" (two
dashes); whereas initialize optional always prepends a space. I am not sure
if this does not look like hack; but people already said this would not be
much of a problem.
Certainly, there ways to make the output of optional configurable. I am not
sure manipulators would be my preference. An alternative would be the
approach that Boost.Date_time has adopted: you set your preference per
stream (plus one global setting) by means of locale mechanism. However,
when it comes to proposing stuff in front of the ISO committee, there is
one other factor, which I failed to mention yet. It is the "fragility" of
the process. I am concerned that if anything turns out to be controversial,
the whole Optional would be at risk of being rejected. This might be an
exaggeration (I have never gone through the process of proposing something
and then pushing it through), but Optional has already been proposed by
Fernando (http://www.open-std.org/jtc1/sc22/wg21/docs/papers/2005/n1878.htm)
and rejected; primarily for two things: operators -> and * and the
semantics for optional reference assignment (see
http://lists.boost.org/Archives/boost/2005/10/95079.php). I would much like
to avoid the situation where Optional (which I believe is very useful to
wide variety of programmers) would be rejected because of one additional,
however tiny, controversy of streaming operator.
Regards,
&rzej
I think the safe option is to not include it. Same for references.
What's the problem with the ptr interface? Seems fine with me.
--
Olaf
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
> 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").
Nathan, going back to the same question I asked Andrey (
http://groups.google.com/group/boost-developers-archive/msg/704971a1eb63b3d2),
it looks like your use case would still work if we disabled any sort of
assignment for optional references (even the assignment from boost::none
for resetting). Am I correct?
Regards,
&rzej
>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
You are correct. I was providing a use case for optional references in general,
not one for assignment to optional references. (That's not to say I think use
cases for assignment to optional references don't exist - I just don't have one
in mind right now).
Regards,
Nate
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
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
> Why? The poster child is database access, where every field can be set or unset, and every set field has a value. Why would anyone want to special case access to bool to use a different type?
I get that use case. You're writing a DB wrapper and you've got optional strings,doubles,ints, and bool should work with the exact same semantics. Sure then use optional<bool>.
> > We could optimize optional<bool> to store ~0 to indicate unset.
> Could you elaborate? I don't see how, at least not without changing the interface, as a "dereferenced" optional<bool> has to return a reference to bool whose address cannot change.
Sure, you could implement it with 1 byte (or whatever sizeof(bool) is) and store ~0 to indicate unset.
operator*() can return the address of the byte. This works because you should never call this if it is empty.
optional<bool> ob=true;
bool& b =*ob; //fine
b=false; // legal
ob=none; //b is no longer a valid ref = same as any other T
You should never set b to ~0, but that is bad form anyway and most compilers give warnings when assigning ints to bools lke that. Consider this:
int i=256;
bool b=i; //is b 0?
Still I don't recommend doing strange things like this. However if someone came and said "my DB app stores tons of optional<bool> and it's burning twice the storage it needs", well I guess it couldn't hurt right?
> If there are debates about about the meaning of reassignment, then it really is NOT equivalent to T*.
You can convert a T* to and from optional<T&>. So in that sense they are equivalent. Syntactically they are very close except when being assigned.
Chris
I agree with being conservative to ensure its inclusion, but there may be a way to get both.
> I think the safe option is to not include it. Same for
> references. What's the problem with the ptr interface? Seems
> fine with me.
One proposal can be for the no I/O, no reference version. Another proposal could extend that with I/O. Yet another could extend it with reference support. That would permit the first proposal to be accepted, even if the others are rejected. I think it is also reasonable to make I/O and reference support optional additions to the core of a single proposal.
Obviously, I/O can be defaulted and made customizable. Deciding on widely useful defaults may not be easy, but customization does permit choosing alternatives. The easiest customization is, of course, a traits class.
Reference support, with some assignment policy, can be added by a wrapper type. "optional_with_references", say, can do everything the normal optional class does plus add support for references. That's not as pretty, perhaps, but there would, then, be no question as to the behavior in a given context. Indeed, you could have two variants of the wrapper to give both assignment protocols, thereby removing all confusion.
_____
Rob Stewart robert....@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.
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.
Why not?
get_value_or() for pointers seems trivial.
Olaf
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
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
>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
> 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
----- Original Message -----
> From: Olaf van der Spek <m...@vdspek.org>
> To: bo...@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 <pfu...@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().
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....@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.
_______________________________________________
???
> 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
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 (boost/std)::(shared_ptr/unique_ptr) does compile
and should be prefered to raw pointers.
Julien
Why?
If the pointers are non-owning, shared/unique is wrong.
--
Olaf
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
>> 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
>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