Skip to content

Commit

Permalink
Make HasAbslStringify public.
Browse files Browse the repository at this point in the history
PiperOrigin-RevId: 565050503
Change-Id: I8f4c463be4ef513a2788745d1b454a7ede489152
  • Loading branch information
Abseil Team authored and Copybara-Service committed Sep 13, 2023
1 parent 6c6b273 commit 9e1789f
Show file tree
Hide file tree
Showing 10 changed files with 153 additions and 42 deletions.
1 change: 1 addition & 0 deletions CMake/AbseilDll.cmake
Original file line number Diff line number Diff line change
Expand Up @@ -306,6 +306,7 @@ set(ABSL_INTERNAL_DLL_FILES
"strings/internal/stringify_sink.h"
"strings/internal/stringify_sink.cc"
"strings/internal/has_absl_stringify.h"
"strings/has_absl_stringify.h"
"strings/match.cc"
"strings/match.h"
"strings/numbers.cc"
Expand Down
14 changes: 5 additions & 9 deletions absl/log/internal/log_message.h
Original file line number Diff line number Diff line change
Expand Up @@ -40,7 +40,7 @@
#include "absl/log/internal/nullguard.h"
#include "absl/log/log_entry.h"
#include "absl/log/log_sink.h"
#include "absl/strings/internal/has_absl_stringify.h"
#include "absl/strings/has_absl_stringify.h"
#include "absl/strings/string_view.h"
#include "absl/time/time.h"

Expand Down Expand Up @@ -170,15 +170,13 @@ class LogMessage {

// Types that support `AbslStringify()` are serialized that way.
template <typename T,
typename std::enable_if<
strings_internal::HasAbslStringify<T>::value, int>::type = 0>
typename std::enable_if<HasAbslStringify<T>::value, int>::type = 0>
LogMessage& operator<<(const T& v) ABSL_ATTRIBUTE_NOINLINE;

// Types that don't support `AbslStringify()` but do support streaming into a
// `std::ostream&` are serialized that way.
template <typename T,
typename std::enable_if<
!strings_internal::HasAbslStringify<T>::value, int>::type = 0>
typename std::enable_if<!HasAbslStringify<T>::value, int>::type = 0>
LogMessage& operator<<(const T& v) ABSL_ATTRIBUTE_NOINLINE;

// Note: We explicitly do not support `operator<<` for non-const references
Expand Down Expand Up @@ -283,8 +281,7 @@ class StringifySink final {

// Note: the following is declared `ABSL_ATTRIBUTE_NOINLINE`
template <typename T,
typename std::enable_if<strings_internal::HasAbslStringify<T>::value,
int>::type>
typename std::enable_if<HasAbslStringify<T>::value, int>::type>
LogMessage& LogMessage::operator<<(const T& v) {
StringifySink sink(*this);
// Replace with public API.
Expand All @@ -294,8 +291,7 @@ LogMessage& LogMessage::operator<<(const T& v) {

// Note: the following is declared `ABSL_ATTRIBUTE_NOINLINE`
template <typename T,
typename std::enable_if<!strings_internal::HasAbslStringify<T>::value,
int>::type>
typename std::enable_if<!HasAbslStringify<T>::value, int>::type>
LogMessage& LogMessage::operator<<(const T& v) {
OstreamView view(*data_);
view.stream() << log_internal::NullGuard<T>().Guard(v);
Expand Down
13 changes: 13 additions & 0 deletions absl/strings/BUILD.bazel
Original file line number Diff line number Diff line change
Expand Up @@ -70,6 +70,7 @@ cc_library(
"ascii.h",
"charconv.h",
"escaping.h",
"has_absl_stringify.h",
"internal/damerau_levenshtein_distance.h",
"internal/has_absl_stringify.h",
"internal/string_constant.h",
Expand Down Expand Up @@ -179,6 +180,18 @@ cc_test(
],
)

cc_test(
name = "has_absl_stringify_test",
size = "small",
srcs = ["has_absl_stringify_test.cc"],
copts = ABSL_TEST_COPTS,
visibility = ["//visibility:private"],
deps = [
":strings",
"@com_google_googletest//:gtest_main",
],
)

cc_test(
name = "ascii_test",
size = "small",
Expand Down
13 changes: 13 additions & 0 deletions absl/strings/CMakeLists.txt
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ absl_cc_library(
"ascii.h"
"charconv.h"
"escaping.h"
"has_absl_stringify.h"
"internal/damerau_levenshtein_distance.h"
"internal/string_constant.h"
"internal/has_absl_stringify.h"
Expand Down Expand Up @@ -154,6 +155,18 @@ absl_cc_test(
absl::check
)

absl_cc_test(
NAME
has_absl_stringify_test
SRCS
"has_absl_stringify_test.cc"
COPTS
${ABSL_TEST_COPTS}
DEPS
absl::strings
GTest::gmock_main
)

absl_cc_test(
NAME
ascii_test
Expand Down
63 changes: 63 additions & 0 deletions absl/strings/has_absl_stringify.h
Original file line number Diff line number Diff line change
@@ -0,0 +1,63 @@
// Copyright 2022 The Abseil Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#ifndef ABSL_STRINGS_HAS_ABSL_STRINGIFY_H_
#define ABSL_STRINGS_HAS_ABSL_STRINGIFY_H_

#include <type_traits>
#include <utility>

#include "absl/strings/string_view.h"

namespace absl {
ABSL_NAMESPACE_BEGIN

namespace strings_internal {

// This is an empty class not intended to be used. It exists so that
// `HasAbslStringify` can reference a universal class rather than needing to be
// copied for each new sink.
class UnimplementedSink {
public:
void Append(size_t count, char ch);

void Append(string_view v);

// Support `absl::Format(&sink, format, args...)`.
friend void AbslFormatFlush(UnimplementedSink* sink, absl::string_view v);
};

} // namespace strings_internal

// `HasAbslStringify<T>` detects if type `T` supports the `AbslStringify()`
// customization point (see
// https://abseil.io/docs/cpp/guides/format#abslstringify for the
// documentation).
//
// Note that there are types that can be `StrCat`-ed that do not use the
// `AbslStringify` customization point (for example, `int`).

template <typename T, typename = void>
struct HasAbslStringify : std::false_type {};

template <typename T>
struct HasAbslStringify<
T, std::enable_if_t<std::is_void<decltype(AbslStringify(
std::declval<strings_internal::UnimplementedSink&>(),
std::declval<const T&>()))>::value>> : std::true_type {};

ABSL_NAMESPACE_END
} // namespace absl

#endif // ABSL_STRINGS_HAS_ABSL_STRINGIFY_H_
37 changes: 37 additions & 0 deletions absl/strings/has_absl_stringify_test.cc
Original file line number Diff line number Diff line change
@@ -0,0 +1,37 @@
// Copyright 2023 The Abseil Authors
//
// Licensed under the Apache License, Version 2.0 (the "License");
// you may not use this file except in compliance with the License.
// You may obtain a copy of the License at
//
// https://www.apache.org/licenses/LICENSE-2.0
//
// Unless required by applicable law or agreed to in writing, software
// distributed under the License is distributed on an "AS IS" BASIS,
// WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied.
// See the License for the specific language governing permissions and
// limitations under the License.

#include "absl/strings/has_absl_stringify.h"

#include <string>

#include "gtest/gtest.h"

namespace {

struct TypeWithoutAbslStringify {};

struct TypeWithAbslStringify {
template <typename Sink>
friend void AbslStringify(Sink&, const TypeWithAbslStringify&) {}
};

TEST(HasAbslStringifyTest, Works) {
EXPECT_FALSE(absl::HasAbslStringify<int>::value);
EXPECT_FALSE(absl::HasAbslStringify<std::string>::value);
EXPECT_FALSE(absl::HasAbslStringify<TypeWithoutAbslStringify>::value);
EXPECT_TRUE(absl::HasAbslStringify<TypeWithAbslStringify>::value);
}

} // namespace
21 changes: 5 additions & 16 deletions absl/strings/internal/has_absl_stringify.h
Original file line number Diff line number Diff line change
Expand Up @@ -18,31 +18,20 @@
#include <type_traits>
#include <utility>

#include "absl/strings/string_view.h"
#include "absl/base/attributes.h"
#include "absl/strings/has_absl_stringify.h"

namespace absl {
ABSL_NAMESPACE_BEGIN

namespace strings_internal {

// This is an empty class not intended to be used. It exists so that
// `HasAbslStringify` can reference a universal class rather than needing to be
// copied for each new sink.
class UnimplementedSink {
public:
void Append(size_t count, char ch);

void Append(string_view v);

// Support `absl::Format(&sink, format, args...)`.
friend void AbslFormatFlush(UnimplementedSink* sink, absl::string_view v);
};

template <typename T, typename = void>
struct HasAbslStringify : std::false_type {};
struct ABSL_DEPRECATED("Use absl::HasAbslStringify") HasAbslStringify
: std::false_type {};

template <typename T>
struct HasAbslStringify<
struct ABSL_DEPRECATED("Use absl::HasAbslStringify") HasAbslStringify<
T, std::enable_if_t<std::is_void<decltype(AbslStringify(
std::declval<strings_internal::UnimplementedSink&>(),
std::declval<const T&>()))>::value>> : std::true_type {};
Expand Down
15 changes: 7 additions & 8 deletions absl/strings/internal/str_format/arg.h
Original file line number Diff line number Diff line change
Expand Up @@ -31,7 +31,7 @@
#include "absl/base/port.h"
#include "absl/meta/type_traits.h"
#include "absl/numeric/int128.h"
#include "absl/strings/internal/has_absl_stringify.h"
#include "absl/strings/has_absl_stringify.h"
#include "absl/strings/internal/str_format/extension.h"
#include "absl/strings/string_view.h"

Expand Down Expand Up @@ -333,7 +333,7 @@ IntegralConvertResult FormatConvertImpl(T v, FormatConversionSpecImpl conv,
template <typename T>
typename std::enable_if<std::is_enum<T>::value &&
!HasUserDefinedConvert<T>::value &&
!strings_internal::HasAbslStringify<T>::value,
!HasAbslStringify<T>::value,
IntegralConvertResult>::type
FormatConvertImpl(T v, FormatConversionSpecImpl conv, FormatSinkImpl* sink);

Expand Down Expand Up @@ -447,7 +447,7 @@ class FormatArgImpl {
struct DecayType {
static constexpr bool kHasUserDefined =
str_format_internal::HasUserDefinedConvert<T>::value ||
strings_internal::HasAbslStringify<T>::value;
HasAbslStringify<T>::value;
using type = typename std::conditional<
!kHasUserDefined && std::is_convertible<T, const char*>::value,
const char*,
Expand All @@ -456,11 +456,10 @@ class FormatArgImpl {
VoidPtr, const T&>::type>::type;
};
template <typename T>
struct DecayType<T,
typename std::enable_if<
!str_format_internal::HasUserDefinedConvert<T>::value &&
!strings_internal::HasAbslStringify<T>::value &&
std::is_enum<T>::value>::type> {
struct DecayType<
T, typename std::enable_if<
!str_format_internal::HasUserDefinedConvert<T>::value &&
!HasAbslStringify<T>::value && std::is_enum<T>::value>::type> {
using type = decltype(+typename std::underlying_type<T>::type());
};

Expand Down
14 changes: 7 additions & 7 deletions absl/strings/str_cat.h
Original file line number Diff line number Diff line change
Expand Up @@ -102,7 +102,7 @@
#include "absl/base/attributes.h"
#include "absl/base/port.h"
#include "absl/meta/type_traits.h"
#include "absl/strings/internal/has_absl_stringify.h"
#include "absl/strings/has_absl_stringify.h"
#include "absl/strings/internal/resize_uninitialized.h"
#include "absl/strings/internal/stringify_sink.h"
#include "absl/strings/numbers.h"
Expand Down Expand Up @@ -357,7 +357,7 @@ class AlphaNum {
: piece_(pc) {}

template <typename T, typename = typename std::enable_if<
strings_internal::HasAbslStringify<T>::value>::type>
HasAbslStringify<T>::value>::type>
AlphaNum( // NOLINT(runtime/explicit)
const T& v ABSL_ATTRIBUTE_LIFETIME_BOUND,
strings_internal::StringifySink&& sink ABSL_ATTRIBUTE_LIFETIME_BOUND = {})
Expand All @@ -384,17 +384,17 @@ class AlphaNum {
template <typename T,
typename = typename std::enable_if<
std::is_enum<T>{} && std::is_convertible<T, int>{} &&
!strings_internal::HasAbslStringify<T>::value>::type>
!HasAbslStringify<T>::value>::type>
AlphaNum(T e) // NOLINT(runtime/explicit)
: AlphaNum(+e) {}

// This overload matches scoped enums. We must explicitly cast to the
// underlying type, but use integral promotion for the same reason as above.
template <typename T,
typename std::enable_if<
std::is_enum<T>{} && !std::is_convertible<T, int>{} &&
!strings_internal::HasAbslStringify<T>::value,
char*>::type = nullptr>
typename std::enable_if<std::is_enum<T>{} &&
!std::is_convertible<T, int>{} &&
!HasAbslStringify<T>::value,
char*>::type = nullptr>
AlphaNum(T e) // NOLINT(runtime/explicit)
: AlphaNum(+static_cast<typename std::underlying_type<T>::type>(e)) {}

Expand Down
4 changes: 2 additions & 2 deletions absl/strings/substitute.h
Original file line number Diff line number Diff line change
Expand Up @@ -176,7 +176,7 @@ class Arg {
: piece_(value ? "true" : "false") {}

template <typename T, typename = typename std::enable_if<
strings_internal::HasAbslStringify<T>::value>::type>
HasAbslStringify<T>::value>::type>
Arg( // NOLINT(google-explicit-constructor)
const T& v, strings_internal::StringifySink&& sink = {})
: piece_(strings_internal::ExtractStringification(sink, v)) {}
Expand Down Expand Up @@ -204,7 +204,7 @@ class Arg {
template <typename T,
typename = typename std::enable_if<
std::is_enum<T>{} && !std::is_convertible<T, int>{} &&
!strings_internal::HasAbslStringify<T>::value>::type>
!HasAbslStringify<T>::value>::type>
Arg(T value) // NOLINT(google-explicit-constructor)
: Arg(static_cast<typename std::underlying_type<T>::type>(value)) {}

Expand Down

0 comments on commit 9e1789f

Please sign in to comment.