From c31b528b862af14917131eeb9631dd1f5d4d4477 Mon Sep 17 00:00:00 2001 From: Marc Celani Date: Fri, 2 May 2014 18:17:32 -0700 Subject: [PATCH] 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 --- folly/ExceptionWrapper.h | 81 ++++++++++++++++++++++++++++- folly/test/ExceptionWrapperTest.cpp | 41 +++++++++++++++ 2 files changed, 121 insertions(+), 1 deletion(-) diff --git a/folly/ExceptionWrapper.h b/folly/ExceptionWrapper.h index 9174f6f7..23a9ac7a 100644 --- a/folly/ExceptionWrapper.h +++ b/folly/ExceptionWrapper.h @@ -130,7 +130,7 @@ class exception_wrapper { return std::exception_ptr(); } - private: + protected: std::shared_ptr 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([=]() { + * 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([=]() { + * if (badThingHappens()) { + * throw std::exception(); + * } + * }); + * + * // This will not catch the exception and it will be thrown. + * auto ew = folly::try_and_catch([=]() { + * if (badThingHappens()) { + * throw std::exception(); + * } + * }); + */ + +template +class try_and_catch; + +template +class try_and_catch : + public try_and_catch { + public: + template + explicit try_and_catch(F&& fn) : Base() { + call_fn(fn); + } + + protected: + typedef try_and_catch Base; + + try_and_catch() : Base() {} + + template + void call_fn(F&& fn) { + try { + Base::call_fn(std::move(fn)); + } catch (const LastException& e) { + this->item_ = std::make_shared(e); + this->throwfn_ = folly::detail::Thrower::doThrow; + } + } +}; + +template<> +class try_and_catch<> : public exception_wrapper { + public: + try_and_catch() {} + + protected: + template + void call_fn(F&& fn) { + fn(); + } +}; } #endif diff --git a/folly/test/ExceptionWrapperTest.cpp b/folly/test/ExceptionWrapperTest.cpp index c20535ee..27da94f9 100644 --- a/folly/test/ExceptionWrapperTest.cpp +++ b/folly/test/ExceptionWrapperTest.cpp @@ -43,3 +43,44 @@ TEST(ExceptionWrapper, boolean) { ew = make_exception_wrapper("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( + [=]() { + throw std::runtime_error(expected); + }); + EXPECT_TRUE(ew.get()); + EXPECT_EQ(ew.get()->what(), expected); + auto rep = dynamic_cast(ew.get()); + EXPECT_TRUE(rep); + + // Changing order is like catching in wrong order. Beware of this in your + // code. + auto ew2 = try_and_catch([=]() { + 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(ew2.get()); + EXPECT_FALSE(rep); + + // Catches even if not rightmost. + auto ew3 = try_and_catch([]() { + throw std::exception(); + }); + EXPECT_TRUE(ew3.get()); + EXPECT_NE(ew3.get()->what(), expected); + rep = dynamic_cast(ew3.get()); + EXPECT_FALSE(rep); + + // If does not catch, throws. + EXPECT_THROW( + try_and_catch([]() { + throw std::exception(); + }), + std::exception); +} -- 2.34.1