[llvm] [Support] Introduce formatv variant of createStringError (PR #80493)

Alex Langford via llvm-commits llvm-commits at lists.llvm.org
Fri Feb 2 12:54:46 PST 2024


https://github.com/bulbazord created https://github.com/llvm/llvm-project/pull/80493

Many times I have found myself wanting to create a StringError with the ability to interpolate a StringRef into the error string. This can be achieved with:

StringRef Foo("...");
auto Err = createStringError(..., "Something went wrong: %s", Foo.str().c_str());

However, this requires us to construct a temporary std::string (which may perform a memory allocation if large enough).

I propose a new variant of `createStringError` called `createStringErrorv` which uses `formatv` under the hood. This allows the above example to become:

StringRef Foo("...");
auto Err = createStringError(..., "Something went wrong: {0}", Foo);

>From 97b88162c1b213a7017fa114fbddd7bf20709358 Mon Sep 17 00:00:00 2001
From: Alex Langford <alangford at apple.com>
Date: Fri, 2 Feb 2024 12:38:08 -0800
Subject: [PATCH] [Support] Introduce formatv variant of createStringError

Many times I have found myself wanting to create a StringError with the
ability to interpolate a StringRef into the error string. This can be
achieved with:

StringRef Foo("...");
auto Err = createStringError(..., "Something went wrong: %s", Foo.str().c_str());

However, this requires us to construct a temporary std::string (which
may perform memory allocation cost if large enough).

I propose a new variant of `createStringError` called
`createStringErrorv` which uses `formatv` under the hood. This allows
the above example to become:

StringRef Foo("...");
auto Err = createStringError(..., "Something went wrong: {0}", Foo);
---
 llvm/include/llvm/Support/Error.h    | 10 ++++++++++
 llvm/unittests/Support/ErrorTest.cpp | 17 +++++++++++++++++
 2 files changed, 27 insertions(+)

diff --git a/llvm/include/llvm/Support/Error.h b/llvm/include/llvm/Support/Error.h
index bb4f38f7ec355..e13543d1681c0 100644
--- a/llvm/include/llvm/Support/Error.h
+++ b/llvm/include/llvm/Support/Error.h
@@ -22,6 +22,7 @@
 #include "llvm/Support/ErrorHandling.h"
 #include "llvm/Support/ErrorOr.h"
 #include "llvm/Support/Format.h"
+#include "llvm/Support/FormatVariadic.h"
 #include "llvm/Support/raw_ostream.h"
 #include <cassert>
 #include <cstdint>
@@ -1261,6 +1262,15 @@ inline Error createStringError(std::errc EC, char const *Fmt,
   return createStringError(std::make_error_code(EC), Fmt, Vals...);
 }
 
+template <typename... Ts>
+inline Error createStringErrorv(std::error_code EC, const char *Fmt,
+                                const Ts &...Vals) {
+  std::string Buffer;
+  raw_string_ostream Stream(Buffer);
+  Stream << formatv(Fmt, Vals...);
+  return make_error<StringError>(Stream.str(), EC);
+}
+
 /// This class wraps a filename and another Error.
 ///
 /// In some cases, an error needs to live along a 'source' name, in order to
diff --git a/llvm/unittests/Support/ErrorTest.cpp b/llvm/unittests/Support/ErrorTest.cpp
index 11f93203597bf..17de7aaf4c9c1 100644
--- a/llvm/unittests/Support/ErrorTest.cpp
+++ b/llvm/unittests/Support/ErrorTest.cpp
@@ -472,6 +472,23 @@ TEST(Error, createStringError) {
     << "Failed to convert createStringError() result to error_code.";
 }
 
+TEST(Error, createStringErrorv) {
+  static llvm::StringRef Bar("bar");
+  static const std::error_code EC = errc::invalid_argument;
+  std::string Msg;
+  raw_string_ostream S(Msg);
+  logAllUnhandledErrors(createStringErrorv(EC, "foo{0}{1}{2:x}", Bar, 1, 0xff),
+                        S);
+  EXPECT_EQ(S.str(), "foobar10xff\n")
+      << "Unexpected createStringErrorv() log result";
+
+  S.flush();
+  Msg.clear();
+  auto Res = errorToErrorCode(createStringErrorv(EC, "foo{0}", Bar));
+  EXPECT_EQ(Res, EC)
+      << "Failed to convert createStringErrorv() result to error_code.";
+}
+
 // Test that the ExitOnError utility works as expected.
 TEST(ErrorDeathTest, ExitOnError) {
   ExitOnError ExitOnErr;



More information about the llvm-commits mailing list