diff --git a/src/tint/CMakeLists.txt b/src/tint/CMakeLists.txt index 1eef3a13fd..7378b1462e 100644 --- a/src/tint/CMakeLists.txt +++ b/src/tint/CMakeLists.txt @@ -836,6 +836,7 @@ if(TINT_BUILD_TESTS) utils/io/tmpfile_test.cc utils/map_test.cc utils/math_test.cc + utils/result_test.cc utils/reverse_test.cc utils/scoped_assignment_test.cc utils/string_test.cc diff --git a/src/tint/utils/result.h b/src/tint/utils/result.h new file mode 100644 index 0000000000..b2a69d56fc --- /dev/null +++ b/src/tint/utils/result.h @@ -0,0 +1,103 @@ +// Copyright 2022 The Tint Authors. +// +// 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. + +#ifndef SRC_TINT_UTILS_RESULT_H_ +#define SRC_TINT_UTILS_RESULT_H_ + +#include +// TODO(https://crbug.com/dawn/1379) Update cpplint and remove NOLINT +#include // NOLINT(build/include_order) + +namespace tint::utils { + +/// Empty structure used as the default FAILURE_TYPE for a Result. +struct FailureType {}; + +static constexpr const FailureType Failure; + +/// Result is a helper for functions that need to return a value, or an failure value. +/// Result can be constructed with either a 'success' or 'failure' value. +/// @tparam SUCCESS_TYPE the 'success' value type. +/// @tparam FAILURE_TYPE the 'failure' value type. Defaults to FailureType which provides no +/// information about the failure, except that something failed. Must not be the same type +/// as SUCCESS_TYPE. +template +struct Result { + static_assert(!std::is_same_v, + "Result must not have the same type for SUCCESS_TYPE and FAILURE_TYPE"); + + /// Constructor + /// @param success the success result + Result(const SUCCESS_TYPE& success) // NOLINT(runtime/explicit): + : value{success} {} + + /// Constructor + /// @param failure the failure result + Result(const FAILURE_TYPE& failure) // NOLINT(runtime/explicit): + : value{failure} {} + + /// @returns true if the result was a success + operator bool() const { return std::holds_alternative(value); } + + /// @returns true if the result was a failure + bool operator!() const { return std::holds_alternative(value); } + + /// @returns the success value + /// @warning attempting to call this when the Result holds an failure will result in UB. + const SUCCESS_TYPE* operator->() const { return &std::get(value); } + + /// @returns the success value + /// @warning attempting to call this when the Result holds an failure value will result in UB. + const SUCCESS_TYPE& Get() const { return std::get(value); } + + /// @returns the failure value + /// @warning attempting to call this when the Result holds a success value will result in UB. + const FAILURE_TYPE& Failure() const { return std::get(value); } + + /// Equality operator + /// @param val the value to compare this Result to + /// @returns true if this result holds a success value equal to `value` + bool operator==(SUCCESS_TYPE val) const { + if (auto* v = std::get_if(&value)) { + return *v == val; + } + return false; + } + + /// Equality operator + /// @param val the value to compare this Result to + /// @returns true if this result holds a failure value equal to `value` + bool operator==(FAILURE_TYPE val) const { + if (auto* v = std::get_if(&value)) { + return *v == val; + } + return false; + } + + /// The result. Either a success of failure value. + std::variant value; +}; + +/// Writes the result to the ostream. +/// @param out the std::ostream to write to +/// @param res the result +/// @return the std::ostream so calls can be chained +template +inline std::ostream& operator<<(std::ostream& out, Result res) { + return res ? (out << "success: " << res.Get()) : (out << "failure: " << res.Failure()); +} + +} // namespace tint::utils + +#endif // SRC_TINT_UTILS_RESULT_H_ diff --git a/src/tint/utils/result_test.cc b/src/tint/utils/result_test.cc new file mode 100644 index 0000000000..ce125f452b --- /dev/null +++ b/src/tint/utils/result_test.cc @@ -0,0 +1,55 @@ +// Copyright 2022 The Tint Authors. +// +// 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. + +#include "src/tint/utils/result.h" + +#include + +#include "gmock/gmock.h" + +namespace tint::utils { +namespace { + +TEST(ResultTest, SuccessInt) { + auto r = Result(123); + EXPECT_TRUE(r); + EXPECT_FALSE(!r); + EXPECT_EQ(r.Get(), 123); +} + +TEST(ResultTest, SuccessStruct) { + struct S { + int value; + }; + auto r = Result({123}); + EXPECT_TRUE(r); + EXPECT_FALSE(!r); + EXPECT_EQ(r->value, 123); +} + +TEST(ResultTest, Failure) { + auto r = Result(Failure); + EXPECT_FALSE(r); + EXPECT_TRUE(!r); +} + +TEST(ResultTest, CustomFailure) { + auto r = Result("oh noes!"); + EXPECT_FALSE(r); + EXPECT_TRUE(!r); + EXPECT_EQ(r.Failure(), "oh noes!"); +} + +} // namespace +} // namespace tint::utils diff --git a/test/tint/BUILD.gn b/test/tint/BUILD.gn index 4f1619f1ee..551c674212 100644 --- a/test/tint/BUILD.gn +++ b/test/tint/BUILD.gn @@ -375,6 +375,7 @@ tint_unittests_source_set("tint_unittests_utils_src") { "../../src/tint/utils/io/tmpfile_test.cc", "../../src/tint/utils/map_test.cc", "../../src/tint/utils/math_test.cc", + "../../src/tint/utils/result_test.cc", "../../src/tint/utils/reverse_test.cc", "../../src/tint/utils/scoped_assignment_test.cc", "../../src/tint/utils/string_test.cc",