3

When trying to get the value of a boost::optional object, BOOST_ASSERT is used to make sure the object is indeed initialized.

But what I would like when dereferencing an uninitialized optional is for an exception to be thrown - is there any way to get this behaviour in a release build? If not, is there any other similar library which has this behaviour?

I would hate to use the is_initialized method each time before dereferencing the object, and I'd also like to avoid wrapping the optional class in my own class to get this behaviour.

alfC
  • 14,261
  • 4
  • 67
  • 118
Danra
  • 9,546
  • 5
  • 59
  • 117
  • This is something I miss from the boost::optional also. It would be nice to have a get_or_throw(exception) and also get_or_else(alternative). I prefer to wrap it, it communicates its purpose well I think. – daramarak Dec 06 '11 at 13:58
  • @daramarak There is a `safe_get` code here: http://boost.2283326.n4.nabble.com/optional-How-to-make-boost-optional-throw-if-trying-to-access-uninitialized-value-tp2591333p2591334.html . Also see my answer. – alfC Jul 04 '15 at 08:21

2 Answers2

3

Unfortunately optional doesn't give such an option. The whole point of optional is to be able to check if the value is present by using the overloaded bool operator.

Optional was designed to allow NOT to throw exceptions in functions, but return a success/failure with the value instead.

Maybe you should return a value always instead, and throw inside the function if it fails?

Kornel Kisielewicz
  • 55,802
  • 15
  • 111
  • 149
  • 1
    That would kind of defeat the purpose of allowing optional return values, which is what I am looking for... As seen in the examples: http://www.boost.org/doc/libs/1_41_0/libs/optional/doc/html/boost_optional/examples.html#boost_optional.examples.optional_return_values – Danra Jan 27 '10 at 10:59
  • 2
    @Danra -- you explicitly stated that you want an exception if the value is not present -- hence why not throw it in the function, where it's more logical to do it, than wait for it to be dereferenced? – Kornel Kisielewicz Jan 27 '10 at 11:15
  • 2
    It works for returning optional values very well. You ask it if it returned a value, and then act accordingly. Exceptions should never be a tool for normal program flow. – Kornel Kisielewicz Jan 27 '10 at 11:16
3

You can define boost::assertion_failed(...) and BOOST_ENABLE_ASSERT_HANDLER to throw an exception from boost::optional.

Code:

#include<boost/exception/to_string.hpp>

namespace boost{
void assertion_failed(char const* expr, char const* function, char const* file, long line){
    throw std::runtime_error(std::string()
        + expr + 
        " from " + function +
        " at " + file + ":" + boost::to_string(line)
    );
}
}

#define BOOST_ENABLE_ASSERT_HANDLER
#include <boost/optional.hpp>
#undef BOOST_ENABLE_ASSERT_HANDLER

int main(){
    double d = *boost::optional<double>{}; // throws! (width fairly useful msg)
    (void)d;
}

The error message (if exception is not catched) will read something like:

terminate called after throwing an instance of 'std::runtime_error'
  what():  this->is_initialized() from reference_type boost::optional<double>::get() [T = double] at /usr/include/boost/optional/optional.hpp:992

Other reference: http://boost.2283326.n4.nabble.com/optional-How-to-make-boost-optional-throw-if-trying-to-access-uninitialized-value-td2591333.html

Notes:

1) It may need a fine grained definition of assertion_failed to be useful in general. If you want to throw a different kind of exception I don't know other way than having a conditional in the assertion_failed function, (it is also too hacky for my taste):

namespace boost{
void assertion_failed(char const* expr, char const* function, char const* file, long line){
    if(std::string("this->is_initialized()") == expr) throw std::domain_error("optional is not intialized");
    throw std::runtime_error(std::string()
        + expr + 
        " from " + function +
        " at " + file + ":" + boost::to_string(line)
    );
}
}

2) I don't agree with the other answer, I think one should be able to choose the behavior. And being stuck with assert is not a good option. There are in my opinion uses of boost::optional in contexts not involving function returns.

3) There is now an std::experimental::optional version. Curiously enough they decided to be agnostic about this problem when taking the value with * (since an unchecked value is returned, this consistent with the raw pointer non-behavior) BUT the .value() member can throw an std::experimental::bad_optional_access exception. This is an interesting design choice (plus none of the two ways asserts! which I think is the right thing.).

alfC
  • 14,261
  • 4
  • 67
  • 118