Skip to content

Commit 707c9aa

Browse files
snarkmasterfacebook-github-bot
authored andcommitted
result_to_try & try_to_result
Summary: `result_to_try` & `try_to_result` to make it easier to `result`-ify existing codebases. Reviewed By: ispeters Differential Revision: D71589246 fbshipit-source-id: f03b9c4922fd62df0c08ea457af79f7dc8b3e235
1 parent 1a8c80c commit 707c9aa

File tree

2 files changed

+188
-0
lines changed

2 files changed

+188
-0
lines changed

folly/result/test/try_test.cpp

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#include <gtest/gtest.h>
18+
19+
#include <folly/result/try.h>
20+
21+
#if FOLLY_HAS_RESULT
22+
23+
namespace folly {
24+
25+
struct ThrowingMove {
26+
ThrowingMove(const ThrowingMove&) = default;
27+
ThrowingMove& operator=(const ThrowingMove&) = default;
28+
ThrowingMove(ThrowingMove&&) {}
29+
ThrowingMove& operator=(ThrowingMove&&) { return *this; }
30+
};
31+
struct NothrowMove {};
32+
33+
static_assert(noexcept(result_to_try(FOLLY_DECLVAL(result<int>))));
34+
static_assert(noexcept(result_to_try(FOLLY_DECLVAL(result<NothrowMove>))));
35+
static_assert(!noexcept(result_to_try(FOLLY_DECLVAL(result<ThrowingMove>))));
36+
37+
static_assert(noexcept(try_to_result(FOLLY_DECLVAL(Try<int>))));
38+
static_assert(noexcept(try_to_result(FOLLY_DECLVAL(Try<NothrowMove>))));
39+
static_assert(!noexcept(try_to_result(FOLLY_DECLVAL(Try<ThrowingMove>))));
40+
41+
TEST(ResultTry, result_to_try) {
42+
auto tInt = result_to_try(result<int>{5});
43+
static_assert(std::is_same_v<Try<int>, decltype(tInt)>);
44+
EXPECT_EQ(5, *tInt);
45+
46+
auto tErr = result_to_try(
47+
result<int>{make_exception_wrapper<std::runtime_error>("foo")});
48+
static_assert(std::is_same_v<Try<int>, decltype(tErr)>);
49+
EXPECT_STREQ("foo", tErr.tryGetExceptionObject<std::runtime_error>()->what());
50+
51+
// `void` -> `void` is a special case
52+
auto tVoid = result_to_try(result<void>{});
53+
static_assert(std::is_same_v<Try<void>, decltype(tVoid)>);
54+
EXPECT_TRUE(tVoid.hasValue());
55+
}
56+
57+
TEST(ResultTry, nonempty_try_to_result) {
58+
auto rInt = try_to_result(Try<int>{5});
59+
static_assert(std::is_same_v<result<int>, decltype(rInt)>);
60+
EXPECT_EQ(5, rInt.value_or_throw());
61+
62+
auto rErr = try_to_result(
63+
Try<int>{make_exception_wrapper<std::runtime_error>("foo")});
64+
static_assert(std::is_same_v<result<int>, decltype(rErr)>);
65+
EXPECT_STREQ("foo", get_exception<std::runtime_error>(rErr)->what());
66+
67+
// `void` -> `void` is a special case
68+
auto rVoid = try_to_result(Try<void>{});
69+
static_assert(std::is_same_v<result<void>, decltype(rVoid)>);
70+
EXPECT_TRUE(rVoid.has_value());
71+
}
72+
73+
TEST(ResultTry, empty_try_to_result_error) {
74+
auto rErr = try_to_result(Try<int>{});
75+
static_assert(std::is_same_v<result<int>, decltype(rErr)>);
76+
EXPECT_NE(nullptr, get_exception<UsingUninitializedTry>(rErr));
77+
}
78+
79+
TEST(ResultTry, empty_try_to_result_default) {
80+
auto rInt = try_to_result(Try<int>{}, empty_try_with{[]() { return 1337; }});
81+
static_assert(std::is_same_v<result<int>, decltype(rInt)>);
82+
EXPECT_EQ(1337, rInt.value_or_throw());
83+
84+
auto rErr = try_to_result(
85+
Try<int>{}, empty_try_with{[]() {
86+
return make_exception_wrapper<std::runtime_error>("baz");
87+
}});
88+
static_assert(std::is_same_v<result<int>, decltype(rErr)>);
89+
EXPECT_STREQ("baz", get_exception<std::runtime_error>(rErr)->what());
90+
}
91+
92+
} // namespace folly
93+
94+
#endif // FOLLY_HAS_RESULT

folly/result/try.h

Lines changed: 94 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,94 @@
1+
/*
2+
* Copyright (c) Meta Platforms, Inc. and affiliates.
3+
*
4+
* Licensed under the Apache License, Version 2.0 (the "License");
5+
* you may not use this file except in compliance with the License.
6+
* You may obtain a copy of the License at
7+
*
8+
* http://www.apache.org/licenses/LICENSE-2.0
9+
*
10+
* Unless required by applicable law or agreed to in writing, software
11+
* distributed under the License is distributed on an "AS IS" BASIS,
12+
* WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
13+
* See the License for the specific language governing permissions and
14+
* limitations under the License.
15+
*/
16+
17+
#pragma once
18+
19+
#include <folly/Try.h>
20+
#include <folly/result/result.h>
21+
22+
/// `result<T>` <-> `Try<T>` conversions to aid in migrating legacy `Try` code.
23+
///
24+
/// Perfect interconversion is not always possible:
25+
/// - `Try` does not support reference types, but `result` does.
26+
/// - `Try` has 3 states. Value & error interconvert transparently. For the
27+
/// empty state, `try_to_result` has a 2nd arg to set the policy.
28+
29+
#if FOLLY_HAS_RESULT
30+
31+
namespace folly {
32+
33+
// NB: If `T` is a reference type, this will fail with a `Try` static assert.
34+
template <typename T>
35+
Try<T> result_to_try(result<T> r) noexcept(
36+
std::is_nothrow_move_constructible_v<T>) {
37+
if (r.has_value()) {
38+
if constexpr (std::is_void_v<T>) {
39+
return Try<void>{};
40+
} else {
41+
return Try<T>{std::move(r).value_or_throw()};
42+
}
43+
} else {
44+
return Try<T>{std::move(r).non_value().get_legacy_error_or_cancellation()};
45+
}
46+
}
47+
48+
inline constexpr struct empty_try_as_error_t {
49+
template <typename T>
50+
result<T> on_empty_try() const {
51+
return {make_exception_wrapper<UsingUninitializedTry>()};
52+
}
53+
} empty_try_as_error;
54+
55+
template <typename Fn>
56+
class empty_try_with {
57+
private:
58+
Fn fn_;
59+
60+
public:
61+
explicit empty_try_with(Fn fn) : fn_(std::move(fn)) {}
62+
template <typename T>
63+
result<T> on_empty_try() && {
64+
return {std::move(fn_)()};
65+
}
66+
};
67+
68+
/// If `t` is empty, defaults to returning a `UsingUninitializedTry` result.
69+
/// Pick your own default via `empty_try_with{[]() { return result<T>{...}; }`.
70+
template <typename T, typename IfEmpty>
71+
result<T> try_to_result(Try<T> t, IfEmpty if_empty) noexcept(
72+
std::is_nothrow_move_constructible_v<T>) {
73+
if (t.hasValue()) {
74+
if constexpr (std::is_void_v<T>) {
75+
return result<void>{};
76+
} else {
77+
return {std::move(t).value()};
78+
}
79+
} else if (t.hasException()) {
80+
return {non_value_result::make_legacy_error_or_cancellation(
81+
std::move(t).exception())};
82+
} else {
83+
return std::move(if_empty).template on_empty_try<T>();
84+
}
85+
}
86+
template <typename T>
87+
result<T> try_to_result(Try<T> t) noexcept(
88+
std::is_nothrow_move_constructible_v<T>) {
89+
return try_to_result(std::move(t), empty_try_as_error);
90+
}
91+
92+
} // namespace folly
93+
94+
#endif // FOLLY_HAS_RESULT

0 commit comments

Comments
 (0)