/* * Copyright (c) Facebook, Inc. and its affiliates. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. * You may obtain a copy of the License at * * http://www.apache.org/licenses/LICENSE-2.0 * * Unless required by applicable law or agreed to in writing, software * distributed under the License is distributed on an "AS IS" BASIS, * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. * See the License for the specific language governing permissions and * limitations under the License. */ #pragma once #include #include #include #include #include namespace folly { template Try::Try(Try&& t) noexcept(std::is_nothrow_move_constructible::value) : contains_(t.contains_) { if (contains_ == Contains::VALUE) { new (&value_) T(std::move(t.value_)); } else if (contains_ == Contains::EXCEPTION) { new (&e_) exception_wrapper(std::move(t.e_)); } } template template Try::Try(typename std::enable_if< std::is_same::value, Try const&>::type t) noexcept : contains_(Contains::NOTHING) { if (t.hasValue()) { contains_ = Contains::VALUE; new (&value_) T(); } else if (t.hasException()) { contains_ = Contains::EXCEPTION; new (&e_) exception_wrapper(t.exception()); } } template Try& Try::operator=(Try&& t) noexcept( std::is_nothrow_move_constructible::value) { if (this == &t) { return *this; } destroy(); if (t.contains_ == Contains::VALUE) { new (&value_) T(std::move(t.value_)); } else if (t.contains_ == Contains::EXCEPTION) { new (&e_) exception_wrapper(std::move(t.e_)); } contains_ = t.contains_; return *this; } template Try::Try(const Try& t) noexcept( std::is_nothrow_copy_constructible::value) { static_assert( std::is_copy_constructible::value, "T must be copyable for Try to be copyable"); contains_ = t.contains_; if (contains_ == Contains::VALUE) { new (&value_) T(t.value_); } else if (contains_ == Contains::EXCEPTION) { new (&e_) exception_wrapper(t.e_); } } template Try& Try::operator=(const Try& t) noexcept( std::is_nothrow_copy_constructible::value) { static_assert( std::is_copy_constructible::value, "T must be copyable for Try to be copyable"); if (this == &t) { return *this; } destroy(); if (t.contains_ == Contains::VALUE) { new (&value_) T(t.value_); } else if (t.contains_ == Contains::EXCEPTION) { new (&e_) exception_wrapper(t.e_); } contains_ = t.contains_; return *this; } template Try::~Try() { if (LIKELY(contains_ == Contains::VALUE)) { value_.~T(); } else if (UNLIKELY(contains_ == Contains::EXCEPTION)) { e_.~exception_wrapper(); } } template template T& Try::emplace(Args&&... args) noexcept( std::is_nothrow_constructible::value) { this->destroy(); new (&value_) T(static_cast(args)...); contains_ = Contains::VALUE; return value_; } template template exception_wrapper& Try::emplaceException(Args&&... args) noexcept( std::is_nothrow_constructible::value) { this->destroy(); new (&e_) exception_wrapper(static_cast(args)...); contains_ = Contains::EXCEPTION; return e_; } template T& Try::value() & { throwIfFailed(); return value_; } template T&& Try::value() && { throwIfFailed(); return std::move(value_); } template const T& Try::value() const& { throwIfFailed(); return value_; } template const T&& Try::value() const&& { throwIfFailed(); return std::move(value_); } template void Try::throwIfFailed() const { switch (contains_) { case Contains::VALUE: return; case Contains::EXCEPTION: e_.throw_exception(); case Contains::NOTHING: default: throw_exception(); } } template void Try::destroy() noexcept { auto oldContains = std::exchange(contains_, Contains::NOTHING); if (LIKELY(oldContains == Contains::VALUE)) { value_.~T(); } else if (UNLIKELY(oldContains == Contains::EXCEPTION)) { e_.~exception_wrapper(); } } Try& Try::operator=(const Try& t) noexcept { if (t.hasException()) { if (hasException()) { e_ = t.e_; } else { new (&e_) exception_wrapper(t.e_); hasValue_ = false; } } else { if (hasException()) { e_.~exception_wrapper(); hasValue_ = true; } } return *this; } template exception_wrapper& Try::emplaceException(Args&&... args) noexcept( std::is_nothrow_constructible::value) { if (hasException()) { e_.~exception_wrapper(); } new (&e_) exception_wrapper(static_cast(args)...); hasValue_ = false; return e_; } void Try::throwIfFailed() const { if (hasException()) { e_.throw_exception(); } } template typename std::enable_if< !std::is_same, void>::value, Try>>::type makeTryWithNoUnwrap(F&& f) { using ResultType = invoke_result_t; try { return Try(f()); } catch (std::exception& e) { return Try(exception_wrapper(std::current_exception(), e)); } catch (...) { return Try(exception_wrapper(std::current_exception())); } } template typename std:: enable_if, void>::value, Try>::type makeTryWithNoUnwrap(F&& f) { try { f(); return Try(); } catch (std::exception& e) { return Try(exception_wrapper(std::current_exception(), e)); } catch (...) { return Try(exception_wrapper(std::current_exception())); } } template typename std:: enable_if>::value, Try>>::type makeTryWith(F&& f) { return makeTryWithNoUnwrap(std::forward(f)); } template typename std::enable_if>::value, invoke_result_t>:: type makeTryWith(F&& f) { using ResultType = invoke_result_t; try { return f(); } catch (std::exception& e) { return ResultType(exception_wrapper(std::current_exception(), e)); } catch (...) { return ResultType(exception_wrapper(std::current_exception())); } } template T* tryEmplace(Try& t, Args&&... args) noexcept { try { return std::addressof(t.emplace(static_cast(args)...)); } catch (const std::exception& ex) { t.emplaceException(std::current_exception(), ex); return nullptr; } catch (...) { t.emplaceException(std::current_exception()); return nullptr; } } void tryEmplace(Try& t) noexcept { t.emplace(); } template T* tryEmplaceWith(Try& t, Func&& func) noexcept { static_assert( std::is_constructible>::value, "Unable to initialise a value of type T with the result of 'func'"); try { return std::addressof(t.emplace(static_cast(func)())); } catch (const std::exception& ex) { t.emplaceException(std::current_exception(), ex); return nullptr; } catch (...) { t.emplaceException(std::current_exception()); return nullptr; } } template bool tryEmplaceWith(Try& t, Func&& func) noexcept { static_assert( std::is_void>::value, "Func returns non-void. Cannot be used to emplace Try"); try { static_cast(func)(); t.emplace(); return true; } catch (const std::exception& ex) { t.emplaceException(std::current_exception(), ex); return false; } catch (...) { t.emplaceException(std::current_exception()); return false; } } namespace try_detail { /** * Trait that removes the layer of Try abstractions from the passed in type */ template struct RemoveTry; template