Allow efficient construction of `absl::Status` from `std::string&&`. PiperOrigin-RevId: 944002601 Change-Id: Iadb5434511a7c00ed5ffd8d3313ca1c9cd27d206
diff --git a/absl/status/internal/status_internal.h b/absl/status/internal/status_internal.h index 2428069..d2892c5 100644 --- a/absl/status/internal/status_internal.h +++ b/absl/status/internal/status_internal.h
@@ -21,6 +21,7 @@ #include <memory> #include <optional> #include <string> +#include <type_traits> #include <utility> #include <vector> @@ -86,6 +87,15 @@ message_(message_arg), payloads_(std::move(payloads_arg)) {} + template <typename String, + typename = std::enable_if_t<std::is_same_v<String, std::string>>> + StatusRep(absl::StatusCode code_arg, String&& message_arg, + std::unique_ptr<status_internal::Payloads> payloads_arg) + : ref_(int32_t{1}), + code_(code_arg), + message_(std::forward<String>(message_arg)), + payloads_(std::move(payloads_arg)) {} + absl::StatusCode code() const { return code_; } const std::string& message() const { return message_; }
diff --git a/absl/status/status.cc b/absl/status/status.cc index 3ef4a27..18adf51 100644 --- a/absl/status/status.cc +++ b/absl/status/status.cc
@@ -22,6 +22,8 @@ #include <memory> #include <ostream> #include <string> +#include <type_traits> +#include <utility> #include "absl/base/attributes.h" #include "absl/base/config.h" @@ -112,20 +114,36 @@ absl::StatusCode::kOk, message, nullptr))); } -uintptr_t Status::MakeRep(uintptr_t inlined_rep, absl::string_view msg, - absl::SourceLocation loc) { - bool ok = inlined_rep == CodeToInlinedRep(absl::StatusCode::kOk); +template <typename StringOrView> +uintptr_t MakeStatusRepImpl(uintptr_t inlined_rep, StringOrView msg, + absl::SourceLocation loc) { + static_assert(std::is_same_v<StringOrView, absl::string_view> || + std::is_same_v<StringOrView, std::string&&>); + bool ok = inlined_rep == Status::CodeToInlinedRep(absl::StatusCode::kOk); if (ok) return inlined_rep; if (msg.empty() ) { return inlined_rep; } - auto* rep = new status_internal::StatusRep(InlinedRepToCode(inlined_rep), msg, - nullptr); + auto* rep = + new status_internal::StatusRep(Status::InlinedRepToCode(inlined_rep), + std::forward<StringOrView>(msg), nullptr); if (loc.file_name()[0] != '\0') { rep->AddSourceLocation(loc); } - return PointerToRep(rep); + return Status::PointerToRep(rep); +} + +uintptr_t Status::MakeRepFromStringView(uintptr_t inlined_rep, + absl::string_view msg, + absl::SourceLocation loc) { + return MakeStatusRepImpl<absl::string_view>(inlined_rep, msg, loc); +} + +uintptr_t Status::MakeRepFromStringRvalue(uintptr_t inlined_rep, + std::string&& msg, + absl::SourceLocation loc) { + return MakeStatusRepImpl<std::string&&>(inlined_rep, std::move(msg), loc); } uintptr_t Status::AddSourceLocationImpl(uintptr_t rep,
diff --git a/absl/status/status.h b/absl/status/status.h index 55802cd..faed05e 100644 --- a/absl/status/status.h +++ b/absl/status/status.h
@@ -56,6 +56,7 @@ #include <optional> #include <ostream> #include <string> +#include <type_traits> #include <utility> #include "absl/base/attributes.h" @@ -459,6 +460,14 @@ Status(absl::StatusCode code, absl::string_view msg, absl::SourceLocation loc = SourceLocation::current()); + // Same as above but for rvalue string. + // Note: using a template to disambiguate the case of matching string_view and + // string&& (e.g. char*) as a template lowers the priority of the overload. + template <typename String, + typename = std::enable_if_t<std::is_same_v<String, std::string>>> + Status(absl::StatusCode code, String&& msg, + absl::SourceLocation loc = SourceLocation::current()); + // Create a status from a `base_status` and a `loc`. The `loc` will be // appended to the location chain of the new status, iff the `base_status` is // not ok and has non-empty msg. @@ -707,8 +716,18 @@ // Delegate factory in header that ensures CodeToInlinedRep is inlined // where possible. - static uintptr_t MakeRep(uintptr_t inlined_rep, absl::string_view msg, - absl::SourceLocation loc); + static uintptr_t MakeRepFromStringView(uintptr_t inlined_rep, + absl::string_view msg, + absl::SourceLocation loc); + + // Same as above but for rvalue string. + static uintptr_t MakeRepFromStringRvalue(uintptr_t inlined_rep, + std::string&& msg, + absl::SourceLocation loc); + + template <typename StringOrView> + friend uintptr_t MakeStatusRepImpl(uintptr_t inlined_rep, StringOrView msg, + absl::SourceLocation loc); // Underlying constructor for status from a rep_. explicit Status(uintptr_t rep) : rep_(rep) {} @@ -897,7 +916,13 @@ inline Status::Status(absl::StatusCode code, absl::string_view msg, absl::SourceLocation loc) - : Status(MakeRep(CodeToInlinedRep(code), msg, loc)) {} + : Status(MakeRepFromStringView(CodeToInlinedRep(code), msg, loc)) {} + +template <typename String, typename> +inline Status::Status(absl::StatusCode code, String&& msg, + absl::SourceLocation loc) + : Status(MakeRepFromStringRvalue(CodeToInlinedRep(code), + std::forward<String>(msg), loc)) {} inline Status::Status(const Status& x) : Status(x.rep_) { Ref(rep_); }
diff --git a/absl/status/status_benchmark.cc b/absl/status/status_benchmark.cc index 539b783..0404db1 100644 --- a/absl/status/status_benchmark.cc +++ b/absl/status/status_benchmark.cc
@@ -12,6 +12,7 @@ // See the License for the specific language governing permissions and // limitations under the License. +#include <string> #include <utility> #include "absl/status/status.h" @@ -46,4 +47,14 @@ } BENCHMARK(BM_AppendSourceLocation); +void BM_LongMessageRValue(benchmark::State& state) { + for (auto _ : state) { + std::string msg(100, 'X'); + benchmark::DoNotOptimize(msg); + absl::Status s(absl::StatusCode::kInvalidArgument, std::move(msg)); + benchmark::DoNotOptimize(s); + } +} +BENCHMARK(BM_LongMessageRValue); + } // namespace