Commit c31b528b authored by Marc Celani's avatar Marc Celani Committed by Dave Watson

try_and_catch

Summary: A helper function to do try/catch on multiple exception types and store the ressult in an exception_wrapper. The purpose of this function is to best effort mimic std::current_exception(). Unlike std::current_exception(), we need to specify the types that we expect to see. Rather than writing macros or several lines per exception type to capture the exception into an exception_wrapper, this function makes writing try/catch blocks for this purpose very easy.

Test Plan: unit test

Reviewed By: mhorowitz@fb.com

FB internal diff: D1308511

@override-unit-failures
parent 708b3d77
......@@ -130,7 +130,7 @@ class exception_wrapper {
return std::exception_ptr();
}
private:
protected:
std::shared_ptr<std::exception> item_;
void (*throwfn_)(std::exception*);
......@@ -146,5 +146,84 @@ exception_wrapper make_exception_wrapper(Args&&... args) {
return ew;
}
/*
* try_and_catch is a simple replacement for try {} catch(){} that allows you to
* specify which derived exceptions you would like to catch and store in an
* exception_wrapper.
*
* Because we cannot build an equivalent of std::current_exception(), we need
* to catch every derived exception that we are interested in catching.
*
* Exceptions should be listed in the reverse order that you would write your
* catch statements (that is, std::exception& should be first).
*
* NOTE: Although implemented as a derived class (for syntactic delight), don't
* be confused - you should not pass around try_and_catch objects!
*
* Example Usage:
*
* // This catches my runtime_error and if I call throwException() on ew, it
* // will throw a runtime_error
* auto ew = folly::try_and_catch<std::exception, std::runtime_error>([=]() {
* if (badThingHappens()) {
* throw std::runtime_error("ZOMG!");
* }
* });
*
* // This will catch the exception and if I call throwException() on ew, it
* // will throw a std::exception
* auto ew = folly::try_and_catch<std::exception, std::runtime_error>([=]() {
* if (badThingHappens()) {
* throw std::exception();
* }
* });
*
* // This will not catch the exception and it will be thrown.
* auto ew = folly::try_and_catch<std::runtime_error>([=]() {
* if (badThingHappens()) {
* throw std::exception();
* }
* });
*/
template <typename... Exceptions>
class try_and_catch;
template <typename LastException, typename... Exceptions>
class try_and_catch<LastException, Exceptions...> :
public try_and_catch<Exceptions...> {
public:
template <typename F>
explicit try_and_catch(F&& fn) : Base() {
call_fn(fn);
}
protected:
typedef try_and_catch<Exceptions...> Base;
try_and_catch() : Base() {}
template <typename F>
void call_fn(F&& fn) {
try {
Base::call_fn(std::move(fn));
} catch (const LastException& e) {
this->item_ = std::make_shared<LastException>(e);
this->throwfn_ = folly::detail::Thrower<LastException>::doThrow;
}
}
};
template<>
class try_and_catch<> : public exception_wrapper {
public:
try_and_catch() {}
protected:
template <typename F>
void call_fn(F&& fn) {
fn();
}
};
}
#endif
......@@ -43,3 +43,44 @@ TEST(ExceptionWrapper, boolean) {
ew = make_exception_wrapper<std::runtime_error>("payload");
EXPECT_TRUE(bool(ew));
}
TEST(ExceptionWrapper, try_and_catch_test) {
std::string expected = "payload";
// Catch rightmost matching exception type
exception_wrapper ew = try_and_catch<std::exception, std::runtime_error>(
[=]() {
throw std::runtime_error(expected);
});
EXPECT_TRUE(ew.get());
EXPECT_EQ(ew.get()->what(), expected);
auto rep = dynamic_cast<std::runtime_error*>(ew.get());
EXPECT_TRUE(rep);
// Changing order is like catching in wrong order. Beware of this in your
// code.
auto ew2 = try_and_catch<std::runtime_error, std::exception>([=]() {
throw std::runtime_error(expected);
});
EXPECT_TRUE(ew2.get());
// We are catching a std::exception, not std::runtime_error.
EXPECT_NE(ew2.get()->what(), expected);
rep = dynamic_cast<std::runtime_error*>(ew2.get());
EXPECT_FALSE(rep);
// Catches even if not rightmost.
auto ew3 = try_and_catch<std::exception, std::runtime_error>([]() {
throw std::exception();
});
EXPECT_TRUE(ew3.get());
EXPECT_NE(ew3.get()->what(), expected);
rep = dynamic_cast<std::runtime_error*>(ew3.get());
EXPECT_FALSE(rep);
// If does not catch, throws.
EXPECT_THROW(
try_and_catch<std::runtime_error>([]() {
throw std::exception();
}),
std::exception);
}
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment