[libcxx-commits] [libcxx] 7217346 - [libc++] Refactor char_traits

Nikolas Klauser via libcxx-commits libcxx-commits at lists.llvm.org
Fri Dec 9 06:45:09 PST 2022


Author: Nikolas Klauser
Date: 2022-12-09T15:45:02+01:00
New Revision: 72173469dd020fd1eb81f607aa4782ec5ec7425c

URL: https://github.com/llvm/llvm-project/commit/72173469dd020fd1eb81f607aa4782ec5ec7425c
DIFF: https://github.com/llvm/llvm-project/commit/72173469dd020fd1eb81f607aa4782ec5ec7425c.diff

LOG: [libc++] Refactor char_traits

This allows us to reuse workarounds for compilers that don't provide the builtins or constexpr support.

Reviewed By: ldionne, Mordante, #libc

Spies: libcxx-commits

Differential Revision: https://reviews.llvm.org/D139555

Added: 
    libcxx/test/libcxx/strings/c.strings/constexpr.cstring.compile.pass.cpp
    libcxx/test/libcxx/strings/c.strings/constexpr.cwchar.compile.pass.cpp

Modified: 
    libcxx/include/__string/char_traits.h
    libcxx/include/cstring
    libcxx/include/cwchar

Removed: 
    


################################################################################
diff  --git a/libcxx/include/__string/char_traits.h b/libcxx/include/__string/char_traits.h
index 617c6c156e961..98dcc2491547a 100644
--- a/libcxx/include/__string/char_traits.h
+++ b/libcxx/include/__string/char_traits.h
@@ -210,25 +210,22 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<char>
     static inline _LIBCPP_CONSTEXPR bool lt(char_type __c1, char_type __c2) _NOEXCEPT
         {return (unsigned char)__c1 < (unsigned char)__c2;}
 
-    static _LIBCPP_CONSTEXPR_SINCE_CXX17
-    int compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT;
+  static _LIBCPP_CONSTEXPR_SINCE_CXX17 int compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
+    if (__n == 0)
+      return 0;
+    return std::__constexpr_memcmp(__s1, __s2, __n);
+  }
 
-    static inline size_t _LIBCPP_CONSTEXPR_SINCE_CXX17 length(const char_type* __s)  _NOEXCEPT {
-      // GCC currently does not support __builtin_strlen during constant evaluation.
-      // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70816
-#ifdef _LIBCPP_COMPILER_GCC
-      if (__libcpp_is_constant_evaluated()) {
-        size_t __i = 0;
-        for (; __s[__i] != char_type('\0'); ++__i)
-            ;
-        return __i;
-      }
-#endif
-      return __builtin_strlen(__s);
-    }
+  static inline size_t _LIBCPP_CONSTEXPR_SINCE_CXX17 length(const char_type* __s)  _NOEXCEPT {
+    return std::__constexpr_strlen(__s);
+  }
 
-    static _LIBCPP_CONSTEXPR_SINCE_CXX17
-    const char_type* find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT;
+  static _LIBCPP_CONSTEXPR_SINCE_CXX17
+  const char_type* find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT {
+    if (__n == 0)
+        return nullptr;
+    return std::__constexpr_char_memchr(__s, static_cast<int>(__a), __n);
+  }
 
     static inline _LIBCPP_CONSTEXPR_SINCE_CXX20
     char_type* move(char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
@@ -261,49 +258,6 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<char>
         {return int_type(EOF);}
 };
 
-inline _LIBCPP_CONSTEXPR_SINCE_CXX17
-int
-char_traits<char>::compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT
-{
-    if (__n == 0)
-        return 0;
-#if __has_feature(cxx_constexpr_string_builtins)
-    return __builtin_memcmp(__s1, __s2, __n);
-#elif _LIBCPP_STD_VER <= 14
-    return _VSTD::memcmp(__s1, __s2, __n);
-#else
-    for (; __n; --__n, ++__s1, ++__s2)
-    {
-        if (lt(*__s1, *__s2))
-            return -1;
-        if (lt(*__s2, *__s1))
-            return 1;
-    }
-    return 0;
-#endif
-}
-
-inline _LIBCPP_CONSTEXPR_SINCE_CXX17
-const char*
-char_traits<char>::find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT
-{
-    if (__n == 0)
-        return nullptr;
-#if __has_feature(cxx_constexpr_string_builtins)
-    return __builtin_char_memchr(__s, to_int_type(__a), __n);
-#elif _LIBCPP_STD_VER <= 14
-    return (const char_type*) _VSTD::memchr(__s, to_int_type(__a), __n);
-#else
-    for (; __n; --__n)
-    {
-        if (eq(*__s, __a))
-            return __s;
-        ++__s;
-    }
-    return nullptr;
-#endif
-}
-
 // char_traits<wchar_t>
 
 #ifndef _LIBCPP_HAS_NO_WIDE_CHARACTERS
@@ -326,12 +280,22 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<wchar_t>
     static inline _LIBCPP_CONSTEXPR bool lt(char_type __c1, char_type __c2) _NOEXCEPT
         {return __c1 < __c2;}
 
-    static _LIBCPP_CONSTEXPR_SINCE_CXX17
-    int compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT;
-    static _LIBCPP_CONSTEXPR_SINCE_CXX17
-    size_t length(const char_type* __s) _NOEXCEPT;
-    static _LIBCPP_CONSTEXPR_SINCE_CXX17
-    const char_type* find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT;
+  static _LIBCPP_CONSTEXPR_SINCE_CXX17 int compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
+    if (__n == 0)
+        return 0;
+    return std::__constexpr_wmemcmp(__s1, __s2, __n);
+  }
+
+  static _LIBCPP_CONSTEXPR_SINCE_CXX17 size_t length(const char_type* __s) _NOEXCEPT {
+    return std::__constexpr_wcslen(__s);
+  }
+
+  static _LIBCPP_CONSTEXPR_SINCE_CXX17
+  const char_type* find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT {
+    if (__n == 0)
+        return nullptr;
+    return std::__constexpr_wmemchr(__s, __a, __n);
+  }
 
     static inline _LIBCPP_CONSTEXPR_SINCE_CXX20
     char_type* move(char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
@@ -363,65 +327,6 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<wchar_t>
     static inline _LIBCPP_CONSTEXPR int_type eof() _NOEXCEPT
         {return int_type(WEOF);}
 };
-
-inline _LIBCPP_CONSTEXPR_SINCE_CXX17
-int
-char_traits<wchar_t>::compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT
-{
-    if (__n == 0)
-        return 0;
-#if __has_feature(cxx_constexpr_string_builtins)
-    return __builtin_wmemcmp(__s1, __s2, __n);
-#elif _LIBCPP_STD_VER <= 14
-    return _VSTD::wmemcmp(__s1, __s2, __n);
-#else
-    for (; __n; --__n, ++__s1, ++__s2)
-    {
-        if (lt(*__s1, *__s2))
-            return -1;
-        if (lt(*__s2, *__s1))
-            return 1;
-    }
-    return 0;
-#endif
-}
-
-inline _LIBCPP_CONSTEXPR_SINCE_CXX17
-size_t
-char_traits<wchar_t>::length(const char_type* __s) _NOEXCEPT
-{
-#if __has_feature(cxx_constexpr_string_builtins)
-    return __builtin_wcslen(__s);
-#elif _LIBCPP_STD_VER <= 14
-    return _VSTD::wcslen(__s);
-#else
-    size_t __len = 0;
-    for (; !eq(*__s, char_type(0)); ++__s)
-        ++__len;
-    return __len;
-#endif
-}
-
-inline _LIBCPP_CONSTEXPR_SINCE_CXX17
-const wchar_t*
-char_traits<wchar_t>::find(const char_type* __s, size_t __n, const char_type& __a) _NOEXCEPT
-{
-    if (__n == 0)
-        return nullptr;
-#if __has_feature(cxx_constexpr_string_builtins)
-    return __builtin_wmemchr(__s, __a, __n);
-#elif _LIBCPP_STD_VER <= 14
-    return _VSTD::wmemchr(__s, __a, __n);
-#else
-    for (; __n; --__n)
-    {
-        if (eq(*__s, __a))
-            return __s;
-        ++__s;
-    }
-    return nullptr;
-#endif
-}
 #endif // _LIBCPP_HAS_NO_WIDE_CHARACTERS
 
 #ifndef _LIBCPP_HAS_NO_CHAR8_T
@@ -445,8 +350,10 @@ struct _LIBCPP_TEMPLATE_VIS char_traits<char8_t>
     static inline constexpr bool lt(char_type __c1, char_type __c2) noexcept
         {return __c1 < __c2;}
 
-    static constexpr
-    int              compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT;
+  static _LIBCPP_HIDE_FROM_ABI constexpr int
+  compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT {
+      return std::__constexpr_memcmp(__s1, __s2, __n);
+  }
 
     static constexpr
     size_t           length(const char_type* __s) _NOEXCEPT;
@@ -496,24 +403,6 @@ char_traits<char8_t>::length(const char_type* __s) _NOEXCEPT
     return __len;
 }
 
-inline constexpr
-int
-char_traits<char8_t>::compare(const char_type* __s1, const char_type* __s2, size_t __n) _NOEXCEPT
-{
-#if __has_feature(cxx_constexpr_string_builtins)
-    return __builtin_memcmp(__s1, __s2, __n);
-#else
-    for (; __n; --__n, ++__s1, ++__s2)
-    {
-        if (lt(*__s1, *__s2))
-            return -1;
-        if (lt(*__s2, *__s1))
-            return 1;
-    }
-    return 0;
-#endif
-}
-
 // TODO use '__builtin_char_memchr' if it ever supports char8_t ??
 inline constexpr
 const char8_t*

diff  --git a/libcxx/include/cstring b/libcxx/include/cstring
index 42bbe73924d3a..c88d97739f744 100644
--- a/libcxx/include/cstring
+++ b/libcxx/include/cstring
@@ -58,6 +58,7 @@ size_t strlen(const char* s);
 
 #include <__assert> // all public C++ headers provide the assertion handler
 #include <__config>
+#include <__type_traits/is_constant_evaluated.h>
 
 #include <string.h>
 
@@ -99,6 +100,53 @@ using ::memset _LIBCPP_USING_IF_EXISTS;
 using ::strerror _LIBCPP_USING_IF_EXISTS;
 using ::strlen _LIBCPP_USING_IF_EXISTS;
 
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 size_t __constexpr_strlen(const char* __str) {
+  // GCC currently doesn't support __builtin_strlen for heap-allocated memory during constant evaluation.
+  // https://gcc.gnu.org/bugzilla/show_bug.cgi?id=70816
+#ifdef _LIBCPP_COMPILER_GCC
+  if (__libcpp_is_constant_evaluated()) {
+    size_t __i = 0;
+    for (; __str[__i] != '\0'; ++__i)
+      ;
+    return __i;
+  }
+#endif
+  return __builtin_strlen(__str);
+}
+
+template <class _Tp>
+_LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int
+__constexpr_memcmp(const _Tp* __lhs, const _Tp* __rhs, size_t __count) {
+#ifdef _LIBCPP_COMPILER_GCC
+  if (__libcpp_is_constant_evaluated()) {
+    for (; __count; --__count, ++__lhs, ++__rhs) {
+      if (*__lhs < *__rhs)
+        return -1;
+      if (*__rhs < *__lhs)
+        return 1;
+    }
+    return 0;
+  }
+#endif
+  return __builtin_memcmp(__lhs, __rhs, __count);
+}
+
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 const char*
+__constexpr_char_memchr(const char* __str, int __char, size_t __count) {
+#if __has_builtin(__builtin_char_memchr)
+  return __builtin_char_memchr(__str, __char, __count);
+#else
+  if (!__libcpp_is_constant_evaluated())
+    return static_cast<const char*>(std::memchr(__str, __char, __count));
+  for (; __count; --__count) {
+    if (*__str == __char)
+      return __str;
+    ++__str;
+  }
+  return nullptr;
+#endif
+}
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_CSTRING

diff  --git a/libcxx/include/cwchar b/libcxx/include/cwchar
index 220c817072de9..fb7b92b760af9 100644
--- a/libcxx/include/cwchar
+++ b/libcxx/include/cwchar
@@ -104,6 +104,7 @@ size_t wcsrtombs(char* restrict dst, const wchar_t** restrict src, size_t len,
 
 #include <__assert> // all public C++ headers provide the assertion handler
 #include <__config>
+#include <__type_traits/is_constant_evaluated.h>
 #include <cwctype>
 
 #include <wchar.h>
@@ -189,6 +190,55 @@ using ::putwchar _LIBCPP_USING_IF_EXISTS;
 using ::vwprintf _LIBCPP_USING_IF_EXISTS;
 using ::wprintf _LIBCPP_USING_IF_EXISTS;
 
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 size_t __constexpr_wcslen(const wchar_t* __str) {
+#if __has_builtin(__builtin_wcslen)
+  return __builtin_wcslen(__str);
+#else
+  if (!__libcpp_is_constant_evaluated())
+    return std::wcslen(__str);
+
+  size_t __len = 0;
+  for (; *__str != L'\0'; ++__str)
+    ++__len;
+  return __len;
+#endif
+}
+
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 int
+__constexpr_wmemcmp(const wchar_t* __lhs, const wchar_t* __rhs, size_t __count) {
+#if __has_builtin(__builtin_wmemcmp)
+  return __builtin_wmemcmp(__lhs, __rhs, __count);
+#else
+  if (!__libcpp_is_constant_evaluated())
+    return std::wmemcmp(__lhs, __rhs, __count);
+
+  for (; __count; --__count, ++__lhs, ++__rhs) {
+    if (*__lhs < *__rhs)
+      return -1;
+    if (*__rhs < *__lhs)
+      return 1;
+  }
+  return 0;
+#endif
+}
+
+inline _LIBCPP_HIDE_FROM_ABI _LIBCPP_CONSTEXPR_SINCE_CXX14 const wchar_t*
+__constexpr_wmemchr(const wchar_t* __str, wchar_t __char, size_t __count) {
+#if __has_feature(cxx_constexpr_string_builtins)
+  return __builtin_wmemchr(__str, __char, __count);
+#else
+  if (!__libcpp_is_constant_evaluated())
+    return std::wmemchr(__str, __char, __count);
+
+  for (; __count; --__count) {
+    if (*__str == __char)
+      return __str;
+    ++__str;
+  }
+  return nullptr;
+#endif
+}
+
 _LIBCPP_END_NAMESPACE_STD
 
 #endif // _LIBCPP_CWCHAR

diff  --git a/libcxx/test/libcxx/strings/c.strings/constexpr.cstring.compile.pass.cpp b/libcxx/test/libcxx/strings/c.strings/constexpr.cstring.compile.pass.cpp
new file mode 100644
index 0000000000000..ad078c0a21db4
--- /dev/null
+++ b/libcxx/test/libcxx/strings/c.strings/constexpr.cstring.compile.pass.cpp
@@ -0,0 +1,24 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11
+
+// Check that __constexpr_* cstring functions are actually constexpr
+
+#include <cstring>
+
+static_assert(std::__constexpr_strlen("Banane") == 6, "");
+static_assert(std::__constexpr_memcmp("Banane", "Banand", 6) == 1, "");
+static_assert(std::__constexpr_memcmp("Banane", "Banane", 6) == 0, "");
+static_assert(std::__constexpr_memcmp("Banane", "Bananf", 6) == -1, "");
+
+constexpr bool test_constexpr_wmemchr() {
+  const char str[] = "Banane";
+  return std::__constexpr_char_memchr(str, 'n', 6) == str + 2;
+}
+static_assert(test_constexpr_wmemchr(), "");

diff  --git a/libcxx/test/libcxx/strings/c.strings/constexpr.cwchar.compile.pass.cpp b/libcxx/test/libcxx/strings/c.strings/constexpr.cwchar.compile.pass.cpp
new file mode 100644
index 0000000000000..02feed064eacc
--- /dev/null
+++ b/libcxx/test/libcxx/strings/c.strings/constexpr.cwchar.compile.pass.cpp
@@ -0,0 +1,26 @@
+//===----------------------------------------------------------------------===//
+//
+// Part of the LLVM Project, under the Apache License v2.0 with LLVM Exceptions.
+// See https://llvm.org/LICENSE.txt for license information.
+// SPDX-License-Identifier: Apache-2.0 WITH LLVM-exception
+//
+//===----------------------------------------------------------------------===//
+
+// UNSUPPORTED: c++03, c++11
+
+// UNSUPPORTED: no-wide-characters
+
+// Check that __constexpr_* cwchar functions are actually constexpr
+
+#include <cwchar>
+
+static_assert(std::__constexpr_wcslen(L"Banane") == 6, "");
+static_assert(std::__constexpr_wmemcmp(L"Banane", L"Banand", 6) == 1, "");
+static_assert(std::__constexpr_wmemcmp(L"Banane", L"Banane", 6) == 0, "");
+static_assert(std::__constexpr_wmemcmp(L"Banane", L"Bananf", 6) == -1, "");
+
+constexpr bool test_constexpr_wmemchr() {
+  const wchar_t str[] = L"Banane";
+  return std::__constexpr_wmemchr(str, 'n', 6) == str + 2;
+}
+static_assert(test_constexpr_wmemchr(), "");


        


More information about the libcxx-commits mailing list