/* * Copyright (C) 1996-2023 The Squid Software Foundation and contributors * * Squid software is distributed under GPLv2+ license and includes * contributions from numerous individuals and organizations. * Please see the COPYING and CONTRIBUTORS files for details. */ #ifndef SQUID_SRC_BASE_IOMANIP_H #define SQUID_SRC_BASE_IOMANIP_H #include "debug/Stream.h" #include #include #include /// \section Custom manipulator tuning methods /// /// Our convenience manipulator/wrapper classes often have methods that tune /// their "printing" effects (e.g., AsHex::minDigits()). STL streams also have /// manipulators that tune how subsequent operator "<<" parameters are printed /// (e.g., std::setw()). The calling code can also print various decorations /// (i.e. prefixes and suffixes). The following principles are useful when /// deciding what manipulator methods to add and how to implement them: /// /// \li Add a manipulator method if callers would otherwise have to restore /// stream format after calling the manipulator. For example, AsHex::toUpper() /// frees callers from doing `std::uppercase << asHex(n) << std::nouppercase`. /// /// \li Add a manipulator method if callers would otherwise have to use /// conditionals to get the same effect. For example, AsList::prefixedBy() frees /// callers from doing `(c.empty() ? "" : "/") << asList(c)`. /// /// \li Add a manipulator method if callers would otherwise have to repeat a /// combination of actions to get the right effect. For example, /// AsList::minDigits() prevents duplication of the following caller code: /// `std::setfill('0') << std::setw(8) << asHex(n)`. /// /// \li Avoid adding a manipulator method that can be _fully_ replaced with a /// _single_ caller item. For example, do not add AsX::foo() if callers can do /// `bar << asX(y)` or `asX(y) << bar` and get exactly the same effect. /// /// \li Manipulators should honor existing stream formatting to the extent /// possible (e.g., AsHex honors std::uppercase by default). /// Safely prints an object pointed to by the given pointer: [label] /// Prints nothing at all if the pointer is nil. template class RawPointerT { public: RawPointerT(const char *aLabel, const Pointer &aPtr): label(aLabel), ptr(aPtr) {} /// Report the pointed-to-object on a dedicated Debug::Extra line. RawPointerT &asExtra() { onExtraLine = true; return *this; } /// enable and, optionally, customize reporting of nil pointers RawPointerT &orNil(const char *nilTextToUse = "[nil]") { nilText = nilTextToUse; return *this; } const char *label; /// the name or description of the being-debugged object /// whether and how to report a nil pointer; use orNil() to enable const char *nilText = nullptr; const Pointer &ptr; /// a possibly nil pointer to the being-debugged object bool onExtraLine = false; }; /// convenience wrapper for creating RawPointerT<> objects template inline RawPointerT RawPointer(const char *label, const Pointer &ptr) { return RawPointerT(label, ptr); } /// convenience wrapper for creating RawPointerT<> objects without a label template inline RawPointerT RawPointer(const Pointer &ptr) { return RawPointerT(nullptr, ptr); } /// prints RawPointerT<>, dereferencing the io_manip pointer if possible template inline std::ostream & operator <<(std::ostream &os, const RawPointerT &pd) { if (!pd.ptr) { if (pd.nilText) os << pd.nilText; return os; } if (pd.onExtraLine) os << Debug::Extra; if (pd.label) os << pd.label; os << *pd.ptr; return os; } /// std::ostream manipulator to print integers and alike as hex numbers. /// Normally used through the asHex() convenience function. template class AsHex { public: // Without this assertion, asHex(pointer) and AsHex(3.14) compile, but their // caller is likely confused about the actual argument type and expects // different output. Enum values are not integral types but arguably do not // cause similar problems. static_assert(std::is_integral::value || std::is_enum::value); explicit AsHex(const Integer n): io_manip(n) {} /// Sets the minimum number of digits to print. If the integer has fewer /// digits than the given width, then we also print leading zero(s). /// Otherwise, this method has no effect. auto &minDigits(const size_t w) { forcePadding = w; return *this; } /// Print hex digits in upper (or, with a false parameter value, lower) case. auto &upperCase(const bool u = true) { forceCase = u; return *this; } Integer io_manip; ///< the integer to print /// \copydoc minDigits() /// The default is to use stream's field width and stream's fill character. std::optional forcePadding; /// \copydoc upperCase() /// The default is to use stream's std::uppercase flag. std::optional forceCase; }; template inline std::ostream & operator <<(std::ostream &os, const AsHex number) { const auto oldFlags = os.flags(); const auto oldFill = os.fill(); if (number.forceCase) os << (*number.forceCase ? std::uppercase : std::nouppercase); if (number.forcePadding) { os.width(*number.forcePadding); os.fill('0'); } // When Integer is smaller than int, the unary plus converts the stored // value into an equivalent integer because C++ "arithmetic operators do not // accept types smaller than int as arguments, and integral promotions are // automatically applied". For larger integer types, plus is a no-op. os << std::hex << +number.io_manip; os.fill(oldFill); os.flags(oldFlags); return os; } /// a helper to ease AsHex object creation template inline AsHex asHex(const Integer n) { return AsHex(n); } /// Prints the first n data bytes using hex notation. Does nothing if n is 0. void PrintHex(std::ostream &, const char *data, size_t n); /// std::ostream manipulator to print containers as flat lists template class AsList { public: explicit AsList(const Container &c): container(c) {} /// a c-string to print before the first item (if any). Caller must ensure lifetime. auto &prefixedBy(const char * const p) { prefix = p; return *this; } /// a c-string to print after the last item (if any). Caller must ensure lifetime. auto &suffixedBy(const char * const p) { suffix = p; return *this; } /// a c-string to print between consecutive items (if any). Caller must ensure lifetime. auto &delimitedBy(const char * const d) { delimiter = d; return *this; } /// c-string to print before and after each item. Caller must ensure lifetime. auto "ed(const char * const q = "\"") { preQuote = postQuote = q; return *this; } /// c-strings to print before and after each item. Caller must ensure lifetime. auto "ed(const char * const preQ, const char * const postQ) { preQuote = preQ; postQuote = postQ; return *this; } /// writes the container to the given stream void print(std::ostream &) const; public: const Container &container; ///< zero or more items to print const char *prefix = nullptr; ///< \copydoc prefixedBy() const char *suffix = nullptr; ///< \copydoc suffixedBy() const char *delimiter = nullptr; ///< \copydoc delimitedBy() const char *preQuote = nullptr; ///< optional c-string to print before each item; \sa quoted() const char *postQuote = nullptr; ///< optional c-string to print after each item; \sa quoted() }; template void AsList::print(std::ostream &os) const { bool opened = false; for (const auto &item: container) { if (!opened) { if (prefix) os << prefix; opened = true; } else { if (delimiter) os << delimiter; } if (preQuote) os << preQuote; os << item; if (postQuote) os << postQuote; } if (opened && suffix) os << suffix; } template inline std::ostream & operator <<(std::ostream &os, const AsList &manipulator) { manipulator.print(os); return os; } /// a helper to ease AsList object creation template inline auto asList(const Container &c) { return AsList(c); } /// Helps print T object at most once per AtMostOnce object lifetime. /// T objects are printed to std::ostream using operator "<<". /// /// \code /// auto headerOnce = AtMostOnce("Transaction Details:\n"); /// if (detailOne) /// os << headerOnce << *detailOne; /// if (const auto detailTwo = findAnotherDetail()) /// os << headerOnce << *detailTwo; /// \endcode template class AtMostOnce { public: /// caller must ensure `t` lifetime extends to the last use of this AtMostOnce instance explicit AtMostOnce(const T &t): toPrint(t) {} void print(std::ostream &os) { if (!printed) { os << toPrint; printed = true; } } private: const T &toPrint; bool printed = false; }; /// Prints AtMostOnce argument if needed. The argument is not constant to /// prevent wrong usage: /// /// \code /// /* Compiler error: cannot bind non-const lvalue reference to an rvalue */ /// os << AtMostOnce(x); /// \endcode template inline auto & operator <<(std::ostream &os, AtMostOnce &a) { a.print(os); return os; } /// Helps prints T object using object's T::printWithExtras() method. template class WithExtras { public: /// caller must ensure `t` lifetime extends to the last use of this class instance explicit WithExtras(const T &t): toPrint(t) {} const T &toPrint; }; /// writes T object to the given stream using object's T::printWithExtras() method template inline auto & operator <<(std::ostream &os, const WithExtras &a) { a.toPrint.printWithExtras(os); return os; } #endif /* SQUID_SRC_BASE_IOMANIP_H */