boost::optional For Pointers

Started by
38 comments, last by Hodgman 13 years, 7 months ago
Quote:Original post by Antheus
Quote:I also always prefer to have the code be the documentation (in addition to regular documentation) wherever possible.


X * foo();

This function may return null. There is no need to document it, it's an established fact. Since pointers can be null, the return value may be null.

but it does NOT document if it is expected to return null, or if it's a problem if it returns null.

optional makes this explicit.

in a lot of cases, returning a 0 pointer indicates an error. in some cases, it might mean "nothing found in the search that was never sure it finds something", so it's not an error, but expected behaviour.

this difference is massive, maybe not for you. but it is, theoretically.
If that's not the help you're after then you're going to have to explain the problem better than what you have. - joanusdmentia

My Page davepermen.net | My Music on Bandcamp and on Soundcloud

Advertisement
Quote:
but it does NOT document if it is expected to return null, or if it's a problem if it returns null.

One way to do this is instead of having optional<T*>, you could have mandatory<T> or notnull<T>. The operator-> overload also acts nicer with this. The constructor can assert (or even check at runtime) that the pointer is not-null.
Quote:Original post by davepermen

optional makes this explicit.

Don't really see how. Why was optional<> not set? Was it an error? Was it normal return?

As demonstrated above - optional<> too can be set, yet contain invalid value.

It carries precisely the same semantic information as regular pointer.

optional<>       |        T*-----------------+---------------------------not_set          |   NULLset + value      |   some pointerset + corrupt    |   invalid pointerset + NULL       |   ???                 |if (p) {         |   if (p) {}   // what if NULL)}
Quote:Original post by rip-off
One way to do this is instead of having optional<T*>, you could have mandatory<T> or notnull<T>. The operator-> overload also acts nicer with this. The constructor can assert (or even check at runtime) that the pointer is not-null.

I'm currently planning on specializing optional for pointer types to get rid of a few annoyances. First off, I've imported optional into my own namespace so it's clear that this is not fully boost's implementation. The specialization will handle the operator-> more elegantly, and will also treat null construction identically with default construction (i.e. optional<T>(0) == optional<T>(), and bool(<either one>) == false).

Quote:Original post by Antheus
Don't really see how. Why was optional<> not set? Was it an error? Was it normal return?

As demonstrated above - optional<> too can be set, yet contain invalid value.

Except that it won't. I am developing the library that returns an optional, therefore I am in full control of whether optional will ever be set yet contain invalid data. In the case of my intended use, optional will always contain a valid non-null pointer if it is set, by design. This is simply a contract guaranteed by the library.
Quote:Original post by Shinkage
In the case of my intended use, optional will always contain a valid non-null pointer if it is set, by design. This is simply a contract guaranteed by the library.


I still don't get it. It sounds like you just want to give a different name to "checking for null". Instead of checking for null, you're going to make a boolean and check the boolean for false. In addition to now increasing the size of a pointer by 50-100%, it seems like you're going to a lot of trouble here to make one of the simplest tasks in C++ very complicated.
Well, I don't know that there's anything more to explain about it without repeating myself. It acts as a semantic clue and a minor syntactic barrier against using certain pointer values in an unsafe manner, and does so in a way that's consistent with non-pointer values. It also could be described as making explicit certain guarantees (or contracts, if you will) that could otherwise only be ascertained by reading and memorizing the documentation of relevant functions.

There's definitely a point. Whether it actually accomplishes that point is something that I think will only become apparent after observing its "use in the wild," assuming it gets to that point. As for myself, I've found it's already helped me avoid some mistakes, just from using it in my own project so far.

That said, this thread has definitely given me some ideas for how to streamline the implementation.
Quote:Original post by Antheus
Quote:Original post by davepermen

optional makes this explicit.

Don't really see how. Why was optional<> not set? Was it an error? Was it normal return?

As demonstrated above - optional<> too can be set, yet contain invalid value.

It carries precisely the same semantic information as regular pointer.

optional<>       |        T*-----------------+---------------------------not_set          |   NULLset + value      |   some pointerset + corrupt    |   invalid pointerset + NULL       |   ???                 |if (p) {         |   if (p) {}   // what if NULL)}


the trick is to NOT EVER USE optional in case of an erroneous state. you just use it where it's expected to consider both cases as valid. and ONLY then.

null pointer should never happen, it should always be error.

that is the distinction to make, and it's an important one. important enough for me to varrant different code constructs to represent them. just as i use exception handling for the error handling, and state-returns for actual application logic.

but i gave enough examples (think of "GetNearestHitPoint (or in my case RayTrace)". that is a typical function made for optional. optional<Hit>). and enough where you don't want optional.

the distinction should be obvious to anyone, but apparently isn't. well, to those that is, they see the point of optional. the others still hack around in their lowlevel "you just have to know it". i prefer to use the language to
the explain it. esp when writing a library / class that others will use.



oh, and, just in case, optional is a standard language feature in c# and works wonderfully. and pointers aren't (for safe code), and that works wonderfully, too. (but optional on gc types doesn't, sadly, so the consistency is a bit lost, there :( )
If that's not the help you're after then you're going to have to explain the problem better than what you have. - joanusdmentia

My Page davepermen.net | My Music on Bandcamp and on Soundcloud

Quote:Original post by davepermen
Quote:Original post by Antheus
Quote:Original post by davepermen

optional makes this explicit.

Don't really see how. Why was optional<> not set? Was it an error? Was it normal return?

As demonstrated above - optional<> too can be set, yet contain invalid value.

It carries precisely the same semantic information as regular pointer.

optional<>       |        T*-----------------+---------------------------not_set          |   NULLset + value      |   some pointerset + corrupt    |   invalid pointerset + NULL       |   ???                 |if (p) {         |   if (p) {}   // what if NULL)}


the trick is to NOT EVER USE optional in case of an erroneous state. you just use it where it's expected to consider both cases as valid. and ONLY then.

null pointer should never happen, it should always be error.

that is the distinction to make, and it's an important one. important enough for me to varrant different code constructs to represent them. just as i use exception handling for the error handling, and state-returns for actual application logic.

but i gave enough examples (think of "GetNearestHitPoint (or in my case RayTrace)". that is a typical function made for optional. optional<Hit>). and enough where you don't want optional.

the distinction should be obvious to anyone, but apparently isn't. well, to those that is, they see the point of optional. the others still hack around in their lowlevel "you just have to know it". i prefer to use the language to
the explain it. esp when writing a library / class that others will use.



oh, and, just in case, optional is a standard language feature in c# and works wonderfully. and pointers aren't (for safe code), and that works wonderfully, too. (but optional on gc types doesn't, sadly, so the consistency is a bit lost, there :( )


I totally get the point of optional<T>. What I don't get is the point of optional<T*>. Increased pointer size by 4 bytes and decreased compile times, for a non-runtime benefit would not even be within the realm of discussion for any commercial game, or many other real-world software projects for that matter.

If NULL pointer should never happen, and it should always be an error, then it sounds like what you need is an optional<T>. Or maybe a reference.

Or as a last resort, which I still think is abusive but would not cringe quite so much at, your own customized smart pointer class called non_null_ptr<T>.
Quote:Original post by davepermen
the distinction should be obvious to anyone, but apparently isn't. well, to those that is, they see the point of optional. the others still hack around in their lowlevel "you just have to know it". i prefer to use the language to
the explain it. esp when writing a library / class that others will use.

Ah, the "you don't get it" argument.

Quote:oh, and, just in case, optional is a standard language feature in c# and works wonderfully. and pointers aren't (for safe code), and that works wonderfully, too. (but optional on gc types doesn't, sadly, so the consistency is a bit lost, there :( )

Nullable adds null to value types.
It cannot be used with "pointers".


As per title of this thread: "boost::optional for pointers" - optional for pointers doesn't make sense. NULL is the optional return value.

Optional is used to add nullability to value types. This solves the problem of magic values which would need to be used instead.
Quote:Original post by davepermen
Quote:Original post by Antheus
X * foo();
This function may return null. There is no need to document it, it's an established fact. Since pointers can be null, the return value may be null.
but it does NOT document if it is expected to return null, or if it's a problem if it returns null. optional makes this explicit.
If something can return NULL, then NULL is expected (even if it's a problem).
If it can return NULL but you're not expecting it to, then you've got a bug...

"X*" documents that it can return NULL, and thus binds you into a contract of dealing with that situation. Whether or not that situation is a "problematic" one or an "expected" one should be fairly obvious by whatever "foo" means.

How far can you take this before it's too much, anyway?
optional<X*> foo();error_if_missing<X*> foo();never_null<X*> foo();unexpectedly_optional<X*> foo();expected_not_to_be_optional_but_might_on_tuesday<X*> foo();...
Besides the fact that it's inconsistent with return-by-value functions that may optionally return some kind of NULL type, what's wrong with the C++ approach of:
T* findByName( const std::string& );//can fail to give you a valid T pointerT& getCriticalSingleton();//will always return a valid pointer

This topic is closed to new replies.

Advertisement