Commit 08e26148 authored by Eric Niebler's avatar Eric Niebler Committed by Facebook GitHub Bot

Avoid use of string functions in constexpr context when the compiler can't support that

Summary:
It's insufficient to infer support for `constexpr` `std::strlen` by merely testing that we're using libstdc++ on a compiler other than clang. Some other compilers besides gcc can be using libstdc++ that _don't_ have `constexpr` `std::strlen`.

Rather, use the compiler itself to detect support for either (a) the builtins, or (b) a `constexpr` implementation of the stdlb function, falling back to a custom `constexpr` implementation otherwise.

Also, handle MSVC, which supports `__builtin_strlen` but which doesn't support either `__has_feature` or `__has_builtin`.

Give `constexpr_strcmp` the same handling, but leave MSVC out of the fun because it doesn't provide `__builtin_strcmp`.

Also, remove the unnecessary include of `<type_traits>`.

Finally, make these functions `noexcept` for good measure.

Extra info: The overload sets in the `detail` namespace in this diff are a little magical. The first overload only compiles if (a) the string literal `"a"` can be `static_cast` to `const Char*` (that is, only if `Char` is `char`), and (b) if `FOLLY_DETAIL_STR???` yields a compile-time constant (so it can be used to initialize a non-type template parameter). If both of those things are true, that overload is preferred because the last argument requires no conversion. If either of those two conditions is false, that overload has a substitution failure and is removed from consideration. Then the second overload is selected.

Reviewed By: yfeldblum

Differential Revision: D23407997

fbshipit-source-id: f5838c578cb62b8fd777052223222882a6575429
parent 2875e290
...@@ -98,7 +98,7 @@ constexpr const Char (&checkNullTerminated(const Char (&a)[N]) noexcept)[N] { ...@@ -98,7 +98,7 @@ constexpr const Char (&checkNullTerminated(const Char (&a)[N]) noexcept)[N] {
return a[N - 1u] == Char(0) return a[N - 1u] == Char(0)
#ifndef NDEBUG #ifndef NDEBUG
// In Debug mode, guard against embedded nulls: // In Debug mode, guard against embedded nulls:
&& N - 1u == folly::detail::constexpr_strlen_fallback(a) && N - 1u == folly::constexpr_strlen(a)
#endif #endif
? decltype(a)(a) ? decltype(a)(a)
: (assertNotNullTerminated(), decltype(a)(a)); : (assertNotNullTerminated(), decltype(a)(a));
......
...@@ -20,68 +20,91 @@ ...@@ -20,68 +20,91 @@
#include <cstdint> #include <cstdint>
#include <cstring> #include <cstring>
#include <type_traits>
namespace folly { namespace folly {
namespace detail { namespace detail {
#if FOLLY_HAS_FEATURE(cxx_constexpr_string_builtins) || \
FOLLY_HAS_BUILTIN(__builtin_strlen) || defined(_MSC_VER)
#define FOLLY_DETAIL_STRLEN __builtin_strlen
#else
#define FOLLY_DETAIL_STRLEN ::std::strlen
#endif
#if FOLLY_HAS_FEATURE(cxx_constexpr_string_builtins) || \
FOLLY_HAS_BUILTIN(__builtin_strcmp)
#define FOLLY_DETAIL_STRCMP __builtin_strcmp
#else
#define FOLLY_DETAIL_STRCMP ::std::strcmp
#endif
// This overload is preferred if Char is char and if FOLLY_DETAIL_STRLEN
// yields a compile-time constant.
template <
typename Char,
size_t = FOLLY_DETAIL_STRLEN(static_cast<const Char*>(""))>
constexpr std::size_t constexpr_strlen_internal(const Char* s, int) noexcept {
return FOLLY_DETAIL_STRLEN(s);
}
template <typename Char> template <typename Char>
constexpr size_t constexpr_strlen_fallback(const Char* s) { constexpr std::size_t constexpr_strlen_internal(
size_t ret = 0; const Char* s,
std::size_t ret) noexcept {
while (*s++) { while (*s++) {
++ret; ++ret;
} }
return ret; return ret;
} }
template <typename Char>
constexpr size_t constexpr_strlen_fallback(const Char* s) noexcept {
return constexpr_strlen_internal(s, (std::size_t)0);
}
static_assert( static_assert(
constexpr_strlen_fallback("123456789") == 9, constexpr_strlen_fallback("123456789") == 9,
"Someone appears to have broken constexpr_strlen..."); "Someone appears to have broken constexpr_strlen...");
// This overload is preferred if Char is char and if FOLLY_DETAIL_STRCMP
// yields a compile-time constant.
template <
typename Char,
int = FOLLY_DETAIL_STRCMP(static_cast<const Char*>(""), "")>
constexpr int
constexpr_strcmp_internal(const Char* s1, const Char* s2, int) noexcept {
return FOLLY_DETAIL_STRCMP(s1, s2);
}
template <typename Char> template <typename Char>
constexpr int constexpr_strcmp_fallback(const Char* s1, const Char* s2) { constexpr int constexpr_strcmp_internal(
const Char* s1,
const Char* s2,
std::size_t) noexcept {
while (*s1 && *s1 == *s2) { while (*s1 && *s1 == *s2) {
++s1, ++s2; ++s1, ++s2;
} }
return int(*s2 < *s1) - int(*s1 < *s2); return int(*s2 < *s1) - int(*s1 < *s2);
} }
} // namespace detail
template <typename Char> template <typename Char>
constexpr size_t constexpr_strlen(const Char* s) { constexpr int constexpr_strcmp_fallback(
return detail::constexpr_strlen_fallback(s); const Char* s1,
const Char* s2) noexcept {
return constexpr_strcmp_internal(s1, s2, (std::size_t)0);
} }
template <> #undef FOLLY_DETAIL_STRCMP
constexpr size_t constexpr_strlen(const char* s) { #undef FOLLY_DETAIL_STRLEN
#if FOLLY_HAS_FEATURE(cxx_constexpr_string_builtins)
// clang provides a constexpr builtin } // namespace detail
return __builtin_strlen(s);
#elif defined(__GLIBCXX__) && !defined(__clang__)
// strlen() happens to already be constexpr under gcc
return std::strlen(s);
#else
return detail::constexpr_strlen_fallback(s);
#endif
}
template <typename Char> template <typename Char>
constexpr int constexpr_strcmp(const Char* s1, const Char* s2) { constexpr size_t constexpr_strlen(const Char* s) noexcept {
return detail::constexpr_strcmp_fallback(s1, s2); return ::folly::detail::constexpr_strlen_internal(s, 0);
} }
template <> template <typename Char>
constexpr int constexpr_strcmp(const char* s1, const char* s2) { constexpr int constexpr_strcmp(const Char* s1, const Char* s2) noexcept {
#if FOLLY_HAS_FEATURE(cxx_constexpr_string_builtins) return ::folly::detail::constexpr_strcmp_internal(s1, s2, 0);
// clang provides a constexpr builtin
return __builtin_strcmp(s1, s2);
#elif defined(__GLIBCXX__) && !defined(__clang__)
// strcmp() happens to already be constexpr under gcc
return std::strcmp(s1, s2);
#else
return detail::constexpr_strcmp_fallback(s1, s2);
#endif
} }
} // namespace folly } // namespace folly
Markdown is supported
0%
or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment