| // Copyright 2025 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. | |
| // | |
| // The typical use looks like this: | |
| // | |
| // LOG(INFO) << LogContainer(container); | |
| // | |
| // By default, LogContainer() uses the LogShortUpTo100 policy: comma-space | |
| // separation, no newlines, and with limit of 100 items. | |
| // | |
| // Policies can be specified: | |
| // | |
| // LOG(INFO) << LogContainer(container, LogMultiline()); | |
| // | |
| // The above example will print the container using newlines between elements, | |
| // enclosed in [] braces. | |
| // | |
| // See below for further details on policies. | |
| #ifndef ABSL_LOG_INTERNAL_CONTAINER_H_ | |
| #define ABSL_LOG_INTERNAL_CONTAINER_H_ | |
| #include <cstdint> | |
| #include <limits> | |
| #include <ostream> | |
| #include <sstream> | |
| #include <string> | |
| #include <type_traits> | |
| #include "absl/base/config.h" | |
| #include "absl/meta/internal/requires.h" | |
| #include "absl/strings/str_cat.h" | |
| namespace absl { | |
| ABSL_NAMESPACE_BEGIN | |
| namespace log_internal { | |
| // Several policy classes below determine how LogRangeToStream will | |
| // format a range of items. A Policy class should have these methods: | |
| // | |
| // Called to print an individual container element. | |
| // void Log(ostream &out, const ElementT &element) const; | |
| // | |
| // Called before printing the set of elements: | |
| // void LogOpening(ostream &out) const; | |
| // | |
| // Called after printing the set of elements: | |
| // void LogClosing(ostream &out) const; | |
| // | |
| // Called before printing the first element: | |
| // void LogFirstSeparator(ostream &out) const; | |
| // | |
| // Called before printing the remaining elements: | |
| // void LogSeparator(ostream &out) const; | |
| // | |
| // Returns the maximum number of elements to print: | |
| // int64 MaxElements() const; | |
| // | |
| // Called to print an indication that MaximumElements() was reached: | |
| // void LogEllipsis(ostream &out) const; | |
| namespace internal { | |
| struct LogBase { | |
| template <typename ElementT> | |
| void Log(std::ostream &out, const ElementT &element) const { // NOLINT | |
| // Fallback to `AbslStringify` if the type does not have `operator<<`. | |
| if constexpr (meta_internal::Requires<std::ostream, ElementT>( | |
| [](auto&& x, auto&& y) -> decltype(x << y) {})) { | |
| out << element; | |
| } else { | |
| out << absl::StrCat(element); | |
| } | |
| } | |
| void LogEllipsis(std::ostream &out) const { // NOLINT | |
| out << "..."; | |
| } | |
| }; | |
| struct LogShortBase : public LogBase { | |
| void LogOpening(std::ostream &out) const { out << "["; } // NOLINT | |
| void LogClosing(std::ostream &out) const { out << "]"; } // NOLINT | |
| void LogFirstSeparator(std::ostream &out) const { out << ""; } // NOLINT | |
| void LogSeparator(std::ostream &out) const { out << ", "; } // NOLINT | |
| }; | |
| struct LogMultilineBase : public LogBase { | |
| void LogOpening(std::ostream &out) const { out << "["; } // NOLINT | |
| void LogClosing(std::ostream &out) const { out << "\n]"; } // NOLINT | |
| void LogFirstSeparator(std::ostream &out) const { out << "\n"; } // NOLINT | |
| void LogSeparator(std::ostream &out) const { out << "\n"; } // NOLINT | |
| }; | |
| struct LogLegacyBase : public LogBase { | |
| void LogOpening(std::ostream &out) const { out << ""; } // NOLINT | |
| void LogClosing(std::ostream &out) const { out << ""; } // NOLINT | |
| void LogFirstSeparator(std::ostream &out) const { out << ""; } // NOLINT | |
| void LogSeparator(std::ostream &out) const { out << " "; } // NOLINT | |
| }; | |
| } // namespace internal | |
| // LogShort uses [] braces and separates items with comma-spaces. For | |
| // example "[1, 2, 3]". | |
| struct LogShort : public internal::LogShortBase { | |
| int64_t MaxElements() const { return (std::numeric_limits<int64_t>::max)(); } | |
| }; | |
| // LogShortUpToN(max_elements) formats the same as LogShort but prints no more | |
| // than the max_elements elements. | |
| class LogShortUpToN : public internal::LogShortBase { | |
| public: | |
| explicit LogShortUpToN(int64_t max_elements) : max_elements_(max_elements) {} | |
| int64_t MaxElements() const { return max_elements_; } | |
| private: | |
| int64_t max_elements_; | |
| }; | |
| // LogShortUpTo100 formats the same as LogShort but prints no more | |
| // than 100 elements. | |
| struct LogShortUpTo100 : public LogShortUpToN { | |
| LogShortUpTo100() : LogShortUpToN(100) {} | |
| }; | |
| // LogMultiline uses [] braces and separates items with | |
| // newlines. For example "[ | |
| // 1 | |
| // 2 | |
| // 3 | |
| // ]". | |
| struct LogMultiline : public internal::LogMultilineBase { | |
| int64_t MaxElements() const { return (std::numeric_limits<int64_t>::max)(); } | |
| }; | |
| // LogMultilineUpToN(max_elements) formats the same as LogMultiline but | |
| // prints no more than max_elements elements. | |
| class LogMultilineUpToN : public internal::LogMultilineBase { | |
| public: | |
| explicit LogMultilineUpToN(int64_t max_elements) | |
| : max_elements_(max_elements) {} | |
| int64_t MaxElements() const { return max_elements_; } | |
| private: | |
| int64_t max_elements_; | |
| }; | |
| // LogMultilineUpTo100 formats the same as LogMultiline but | |
| // prints no more than 100 elements. | |
| struct LogMultilineUpTo100 : public LogMultilineUpToN { | |
| LogMultilineUpTo100() : LogMultilineUpToN(100) {} | |
| }; | |
| // The legacy behavior of LogSequence() does not use braces and | |
| // separates items with spaces. For example "1 2 3". | |
| struct LogLegacyUpTo100 : public internal::LogLegacyBase { | |
| int64_t MaxElements() const { return 100; } | |
| }; | |
| struct LogLegacy : public internal::LogLegacyBase { | |
| int64_t MaxElements() const { return (std::numeric_limits<int64_t>::max)(); } | |
| }; | |
| // The default policy for new code. | |
| typedef LogShortUpTo100 LogDefault; | |
| // LogRangeToStream should be used to define operator<< for | |
| // STL and STL-like containers. For example, see stl_logging.h. | |
| template <typename IteratorT, typename PolicyT> | |
| inline void LogRangeToStream(std::ostream &out, // NOLINT | |
| IteratorT begin, IteratorT end, | |
| const PolicyT &policy) { | |
| policy.LogOpening(out); | |
| for (int64_t i = 0; begin != end && i < policy.MaxElements(); ++i, ++begin) { | |
| if (i == 0) { | |
| policy.LogFirstSeparator(out); | |
| } else { | |
| policy.LogSeparator(out); | |
| } | |
| policy.Log(out, *begin); | |
| } | |
| if (begin != end) { | |
| policy.LogSeparator(out); | |
| policy.LogEllipsis(out); | |
| } | |
| policy.LogClosing(out); | |
| } | |
| namespace detail { | |
| // RangeLogger is a helper class for LogRange and LogContainer; do not use it | |
| // directly. This object captures iterators into the argument of the LogRange | |
| // and LogContainer functions, so its lifetime should be confined to a single | |
| // logging statement. Objects of this type should not be assigned to local | |
| // variables. | |
| template <typename IteratorT, typename PolicyT> | |
| class RangeLogger { | |
| public: | |
| RangeLogger(const IteratorT &begin, const IteratorT &end, | |
| const PolicyT &policy) | |
| : begin_(begin), end_(end), policy_(policy) {} | |
| friend std::ostream &operator<<(std::ostream &out, const RangeLogger &range) { | |
| LogRangeToStream<IteratorT, PolicyT>(out, range.begin_, range.end_, | |
| range.policy_); | |
| return out; | |
| } | |
| // operator<< above is generally recommended. However, some situations may | |
| // require a string, so a convenience str() method is provided as well. | |
| std::string str() const { | |
| std::stringstream ss; | |
| ss << *this; | |
| return ss.str(); | |
| } | |
| private: | |
| IteratorT begin_; | |
| IteratorT end_; | |
| PolicyT policy_; | |
| }; | |
| template <typename E> | |
| class EnumLogger { | |
| public: | |
| explicit EnumLogger(E e) : e_(e) {} | |
| friend std::ostream &operator<<(std::ostream &out, const EnumLogger &v) { | |
| using I = std::underlying_type_t<E>; | |
| return out << static_cast<I>(v.e_); | |
| } | |
| private: | |
| E e_; | |
| }; | |
| } // namespace detail | |
| // Log a range using "policy". For example: | |
| // | |
| // LOG(INFO) << LogRange(start_pos, end_pos, LogMultiline()); | |
| // | |
| // The above example will print the range using newlines between | |
| // elements, enclosed in [] braces. | |
| template <typename IteratorT, typename PolicyT> | |
| detail::RangeLogger<IteratorT, PolicyT> LogRange(const IteratorT &begin, | |
| const IteratorT &end, | |
| const PolicyT &policy) { | |
| return detail::RangeLogger<IteratorT, PolicyT>(begin, end, policy); | |
| } | |
| // Log a range. For example: | |
| // | |
| // LOG(INFO) << LogRange(start_pos, end_pos); | |
| // | |
| // By default, Range() uses the LogShortUpTo100 policy: comma-space | |
| // separation, no newlines, and with limit of 100 items. | |
| template <typename IteratorT> | |
| detail::RangeLogger<IteratorT, LogDefault> LogRange(const IteratorT &begin, | |
| const IteratorT &end) { | |
| return LogRange(begin, end, LogDefault()); | |
| } | |
| // Log a container using "policy". For example: | |
| // | |
| // LOG(INFO) << LogContainer(container, LogMultiline()); | |
| // | |
| // The above example will print the container using newlines between | |
| // elements, enclosed in [] braces. | |
| template <typename ContainerT, typename PolicyT> | |
| auto LogContainer(const ContainerT& container, const PolicyT& policy) | |
| -> decltype(LogRange(container.begin(), container.end(), policy)) { | |
| return LogRange(container.begin(), container.end(), policy); | |
| } | |
| // Log a container. For example: | |
| // | |
| // LOG(INFO) << LogContainer(container); | |
| // | |
| // By default, Container() uses the LogShortUpTo100 policy: comma-space | |
| // separation, no newlines, and with limit of 100 items. | |
| template <typename ContainerT> | |
| auto LogContainer(const ContainerT& container) | |
| -> decltype(LogContainer(container, LogDefault())) { | |
| return LogContainer(container, LogDefault()); | |
| } | |
| // Log a (possibly scoped) enum. For example: | |
| // | |
| // enum class Color { kRed, kGreen, kBlue }; | |
| // LOG(INFO) << LogEnum(kRed); | |
| template <typename E> | |
| detail::EnumLogger<E> LogEnum(E e) { | |
| static_assert(std::is_enum_v<E>, "must be an enum"); | |
| return detail::EnumLogger<E>(e); | |
| } | |
| } // namespace log_internal | |
| ABSL_NAMESPACE_END | |
| } // namespace absl | |
| #endif // ABSL_LOG_INTERNAL_CONTAINER_H_ |