[llvm] RFC: [llvm] specialize cl::opt_storage for std::string (PR #149172)

Andrew Rogers via llvm-commits llvm-commits at lists.llvm.org
Wed Jul 16 18:47:39 PDT 2025


https://github.com/andrurogerz updated https://github.com/llvm/llvm-project/pull/149172

>From 4ebd910fd88f58adab0f78ff0a05bfe64807af4c Mon Sep 17 00:00:00 2001
From: Andrew Rogers <andrurogerz at gmail.com>
Date: Wed, 16 Jul 2025 09:32:09 -0700
Subject: [PATCH] [llvm] specialize cl::opt_storage for std::string

---
 llvm/include/llvm/Support/CommandLine.h | 186 ++++++++++++++++++++++++
 llvm/include/llvm/TargetParser/Triple.h |   4 +
 2 files changed, 190 insertions(+)

diff --git a/llvm/include/llvm/Support/CommandLine.h b/llvm/include/llvm/Support/CommandLine.h
index adaa75cc6c348..823e31a80366e 100644
--- a/llvm/include/llvm/Support/CommandLine.h
+++ b/llvm/include/llvm/Support/CommandLine.h
@@ -1390,6 +1390,192 @@ class opt_storage<DataType, false, true> : public DataType {
   const OptionValue<DataType> &getDefault() const { return Default; }
 };
 
+// Define a fully-specialized version of opt_storage for std::string. This
+// implementation avoids inheriting from std::string while still being
+// compatible with it in most common use cases.
+//
+template <> class opt_storage<std::string, false, true> {
+  using Self = opt_storage<std::string, false, true>;
+
+public:
+  std::string Value;
+  OptionValue<std::string> Default;
+
+  // Make sure we initialize the value with the default constructor for the
+  // type.
+  opt_storage() : Value(std::string()), Default() {}
+
+  template <class T> void setValue(const T &V, bool initial = false) {
+    Value = static_cast<std::string>(V);
+    if (initial)
+      Default = static_cast<std::string>(V);
+  }
+
+  std::string &getValue() { return Value; }
+  std::string getValue() const { return Value; }
+
+  const OptionValue<std::string> &getDefault() const { return Default; }
+
+  // Support implicit conversions to std::string and LLVM string-like types.
+  //operator std::string() const { return Value; }
+  operator std::string&() { return Value; }
+  operator const std::string&() const { return Value; }
+  operator llvm::StringRef() const { return llvm::StringRef(Value); }
+  operator llvm::Twine() const { return llvm::Twine(llvm::StringRef(Value)); }
+
+  // If the datatype is a pointer, support -> on it.
+  std::string operator->() const { return Value; }
+
+  Self& operator=(const Self& rhs) = default;
+
+  Self& operator=(const std::string& rhs) {
+    Value = rhs;
+    return *this;
+  }
+
+  Self& operator=(const char* rhs) {
+    Value = rhs;
+    return *this;
+  }
+
+  // Implement a subset of std::string methods
+  size_t size() const noexcept { return Value.size(); };
+  size_t max_size() const noexcept { return Value.max_size(); };
+  size_t length() const noexcept { return Value.length(); };
+  bool empty() const noexcept { return Value.empty(); }
+  void clear() noexcept { Value.clear(); }
+
+  const char *c_str() const { return Value.c_str(); };
+  const char *data() const { return Value.data(); };
+
+  auto begin() noexcept { return Value.begin(); }
+  auto end() noexcept { return Value.end(); }
+  auto begin() const noexcept { return Value.begin(); }
+  auto end() const noexcept { return Value.end(); }
+  auto cbegin() const noexcept { return Value.cbegin(); }
+  auto cend() const noexcept { return Value.cend(); }
+
+  auto rbegin() noexcept { return Value.rbegin(); }
+  auto rend() noexcept { return Value.rend(); }
+  auto rbegin() const noexcept { return Value.rbegin(); }
+  auto rend() const noexcept { return Value.rend(); }
+  auto crbegin() const noexcept { return Value.rbegin(); }
+  auto crend() const noexcept { return Value.rend(); }
+
+  void erase(size_t pos = 0, size_t count = std::string::npos) {
+    Value.erase(pos, count);
+  }
+
+  size_t find(const std::string &str, size_t pos = 0) const {
+    return Value.find(str, pos);
+  }
+
+  size_t find(const char *s, size_t pos = 0) const {
+    return Value.find(s, pos);
+  }
+
+  size_t find(char c, size_t pos = 0) const { return Value.find(c, pos); }
+
+  std::string substr(size_t pos = 0, size_t count = std::string::npos) const {
+    return Value.substr(pos, count);
+  }
+
+  char &at(size_t pos) { return Value[pos]; }
+  const char &at(size_t pos) const { return Value.at(pos); }
+
+  char &front() { return Value.front(); }
+  const char &front() const { return Value.front(); }
+
+  char &back() { return Value.back(); }
+  const char &back() const { return Value.back(); }
+
+  char operator[](size_t pos) const { return Value.at(pos); }
+
+  Self& assign(const std::string& rhs) {
+    Value.assign(rhs);
+    return *this;
+  }
+
+  Self& assign(const char* rhs) {
+    Value.assign(rhs);
+    return *this;
+  }
+
+  Self& operator+=(const std::string& rhs) {
+    Value += rhs;
+    return *this;
+  }
+
+  Self& operator+=(const char* rhs) {
+    Value += rhs;
+    return *this;
+  }
+
+  friend std::string
+  operator+(Self const& lhs, std::string const& rhs) {
+    return lhs.Value + rhs;
+  }
+
+  friend std::string
+  operator+(std::string const& lhs, Self const& rhs) {
+    return lhs + rhs.Value;
+  }
+
+  friend std::string
+  operator+(Self const& lhs, char const* rhs) {
+    return lhs.Value + rhs;
+  }
+
+  friend std::string
+  operator+(char const* lhs, Self const& rhs) {
+    return lhs + rhs.Value;
+  }
+
+  friend bool operator==(const Self &lhs, const Self &rhs) {
+    return lhs.Value == rhs.Value;
+  }
+
+  friend bool operator==(const Self &lhs, const std::string &rhs) {
+    return lhs.Value == rhs;
+  }
+
+  friend bool operator==(const Self &lhs, const char *rhs) {
+    return lhs.Value == rhs;
+  }
+
+  friend bool operator==(const std::string &lhs, const Self &rhs) {
+    return lhs == rhs.Value;
+  }
+
+  friend bool operator==(const char *lhs, const Self &rhs) {
+    return lhs == rhs.Value;
+  }
+
+  friend bool operator!=(const Self &lhs, const Self &rhs) {
+    return lhs.Value != rhs.Value;
+  }
+
+  friend bool operator!=(const Self &lhs, const std::string &rhs) {
+    return lhs.Value != rhs;
+  }
+
+  friend bool operator!=(const Self &lhs, const char *rhs) {
+    return lhs.Value != rhs;
+  }
+
+  friend bool operator!=(const std::string &lhs, const Self &rhs) {
+    return lhs != rhs.Value;
+  }
+
+  friend bool operator!=(const char *lhs, const Self &rhs) {
+    return lhs != rhs.Value;
+  }
+
+  friend raw_ostream &operator<<(raw_ostream &os, const Self &str) {
+    return os << str.Value;
+  }
+};
+
 // Define a partial specialization to handle things we cannot inherit from.  In
 // this case, we store an instance through containment, and overload operators
 // to get at the value.
diff --git a/llvm/include/llvm/TargetParser/Triple.h b/llvm/include/llvm/TargetParser/Triple.h
index 57d771b80251a..57d4c02d339c1 100644
--- a/llvm/include/llvm/TargetParser/Triple.h
+++ b/llvm/include/llvm/TargetParser/Triple.h
@@ -356,6 +356,10 @@ class Triple {
   explicit Triple(const std::string &Str) : Triple(std::string(Str)) {}
   LLVM_ABI explicit Triple(const Twine &Str);
 
+  template <typename S, typename = std::enable_if_t<
+                            std::is_convertible<S, StringRef>::value>>
+  explicit Triple(const S &Str) : Triple(StringRef(Str)) {}
+
   LLVM_ABI Triple(const Twine &ArchStr, const Twine &VendorStr,
                   const Twine &OSStr);
   LLVM_ABI Triple(const Twine &ArchStr, const Twine &VendorStr,



More information about the llvm-commits mailing list