// 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 #include #include #include #include #include #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 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( [](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::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::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::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 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 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(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 class EnumLogger { public: explicit EnumLogger(E e) : e_(e) {} friend std::ostream &operator<<(std::ostream &out, const EnumLogger &v) { using I = typename std::underlying_type::type; return out << static_cast(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 detail::RangeLogger LogRange(const IteratorT &begin, const IteratorT &end, const PolicyT &policy) { return detail::RangeLogger(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 detail::RangeLogger 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 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 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 detail::EnumLogger LogEnum(E e) { static_assert(std::is_enum::value, "must be an enum"); return detail::EnumLogger(e); } } // namespace log_internal ABSL_NAMESPACE_END } // namespace absl #endif // ABSL_LOG_INTERNAL_CONTAINER_H_