blob: ca0e07a30c3a9ea02ca1f0b4e15313b4e28a145d [file] [log] [blame]
// Copyright 2026 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.
//
// -----------------------------------------------------------------------------
// File: stringify_stream.h
// -----------------------------------------------------------------------------
#ifndef ABSL_STRINGS_INTERNAL_STRINGIFY_STREAM_H_
#define ABSL_STRINGS_INTERNAL_STRINGIFY_STREAM_H_
// StringifyStream is an adaptor for any std::ostream, that provides a
// stream insertion (<<) operator with the following behavior when inserting
// some value of type T:
//
// - If there is a suitable overload of operator<< already defined for T, it
// will be used.
//
// - If there is no operator<< overload, but there is an AbslStringify defined
// for T, it will be used as a fallback.
//
// - Otherwise it is a compilation error.
//
// For reference, AbslStringify typically has the form:
//
// struct Foo {
// template <typename Sink>
// friend void AbslStringify(Sink& sink, const Foo& foo) { ... }
// };
//
// This permits the following usage, for example:
//
// StringifyStream(std::cout) << Foo();
//
#include <cstddef>
#include <ostream>
#include <string>
#include <type_traits>
#include <utility>
#include "absl/base/config.h"
#include "absl/strings/string_view.h"
namespace absl {
ABSL_NAMESPACE_BEGIN
namespace strings_internal {
class StringifyStream {
public:
// Constructor: adapts (but does not take ownership of) some underlying
// std::ostream instance.
explicit StringifyStream(std::ostream& os) : sink_{os} {}
// Stream insertion: delegate to an overload of operator<< if defined for type
// T, otherwise fall back to AbslStringify for T.
template <typename T>
friend StringifyStream& operator<<(StringifyStream& stream, const T& t) {
if constexpr (HasStreamInsertion<T>::value) {
stream.sink_.os << t;
} else {
AbslStringify(stream.sink_, t);
}
return stream;
}
// Rvalue-ref overload, required when the StringifyStream parameter hasn't
// been bound to a variable.
template <typename T>
friend StringifyStream& operator<<(StringifyStream&& stream, const T& t) {
return stream << t;
}
private:
// Abseil "stringify sink" concept (stringify_sink.h)
struct Sink {
std::ostream& os;
void Append(size_t count, char ch) { os << std::string(count, ch); }
void Append(absl::string_view v) { os << v; }
friend void AbslFormatFlush(Sink* sink, absl::string_view v) {
sink->Append(v);
}
} sink_;
// SFINAE helper to identify types with a defined operator<< overload.
template <typename T, typename = void>
struct HasStreamInsertion : std::false_type {};
template <typename T>
struct HasStreamInsertion<T,
std::void_t<decltype(std::declval<std::ostream&>()
<< std::declval<const T&>())>>
: std::true_type {};
};
} // namespace strings_internal
ABSL_NAMESPACE_END
} // namespace absl
#endif // ABSL_STRINGS_INTERNAL_STRINGIFY_STREAM_H_