How can I make sure a boost::optional<T> object is initialized in release-build?
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.
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?
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 assert
s! which I think is the right thing.).
精彩评论