code/include/swoc/bwf_ex.h (110 lines of code) (raw):

// SPDX-License-Identifier: Apache-2.0 // Copyright Apache Software Foundation 2019 /** @file BufferWriter formatters for types in the std namespace. */ #pragma once #include <array> #include <string_view> #include "swoc/swoc_version.h" #include "swoc/bwf_base.h" #include "swoc/swoc_meta.h" namespace swoc { inline namespace SWOC_VERSION_NS { namespace bwf { using namespace swoc::literals; // enable ""sv /** Output @a text @a n times. * */ struct Pattern { int _n; ///< # of instances of @a pattern. std::string_view _text; ///< output text. }; /** Format wrapper for @c errno. * This stores a copy of the argument or @c errno if an argument isn't provided. The output * is then formatted with the short, long, and numeric value of @c errno. If the format specifier * is type 'd' then just the numeric value is printed. */ struct Errno { int _e; ///< Errno value. /// Construct wrapper, default to current @c errno explicit Errno(int e = errno) : _e(e) {} }; /** Format wrapper for time stamps. * If the time isn't provided, the current epoch time is used. If the format string isn't * provided a format like "2017 Jun 29 14:11:29" is used. */ struct Date { /// Default format static constexpr std::string_view DEFAULT_FORMAT{"%Y %b %d %H:%M:%S"_sv}; time_t _epoch; ///< The time. std::string_view _fmt; ///< Data format. /** Constructor. * * @param t The timestamp. * @param fmt Timestamp format. */ Date(time_t t, std::string_view fmt = DEFAULT_FORMAT) : _epoch(t), _fmt(fmt) {} /// Default construct using current time with optional format. Date(std::string_view fmt = DEFAULT_FORMAT); }; namespace detail { // Special case conversions - these handle nullptr because the @c std::string_view spec is stupid. inline std::string_view FirstOfConverter(std::nullptr_t) { return std::string_view{}; } inline std::string_view FirstOfConverter(char const *s) { return std::string_view{s ? s : ""}; } // Otherwise do any compliant conversion. template <typename T> std::string_view FirstOfConverter(T &&t) { return t; } } // namespace detail /// Print the first of a list of strings that is not an empty string. /// All arguments must be convertible to @c std::string_view. template <typename... Args> std::string_view FirstOf(Args &&...args) { std::array<std::string_view, sizeof...(args)> strings{{detail::FirstOfConverter(args)...}}; for (auto &s : strings) { if (!s.empty()) return s; } return std::string_view{}; } /** Wrapper for a sub-text, where the @a args are output according to @a fmt. * * @tparam Args Argument types. */ template <typename... Args> struct SubText { using arg_pack = std::tuple<Args...>; ///< The pack of arguments for format string. TextView _fmt; ///< Format string. If empty, do not generate output. arg_pack _args; ///< Arguments to format string. /// Construct with a specific @a fmt and @a args. SubText(TextView const &fmt, arg_pack const &args) : _fmt(fmt), _args(args){}; /// Check for output not enabled. bool operator!() const; /// Check for output enabled. explicit operator bool() const; }; template <typename... Args> SubText<Args...>::operator bool() const { return !_fmt.empty(); } template <typename... Args> bool SubText<Args...>::operator!() const { return _fmt.empty(); } /** Optional printing wrapper. * * @tparam Args Arguments for output. * @param flag Generate output flag. * @param fmt Format for output and args. * @param args The arguments. * @return A wrapper for the optional text. * * This function is passed a @a flag, a printing format @a fmt, and a set of arguments @a args to * be used by the format. Output is generated if @a flag is @c true, otherwise the empty string * (no output) is generated. For example, if in a function there was a flag to determine if an * extra tag with delimiters, e.g. "[tag]", was to be generated, this could be done with * * @code * w.print("Some other text{}.", bwf::If(flag, " [{}]", tag)); * @endcode * * @internal To disambiguate overloads, this is enabled only if there is at least one argument * to be passed to the format string. */ template <typename... Args> SubText<Args...> If(bool flag, TextView const &fmt, Args &&...args) { return SubText<Args...>(flag ? fmt : TextView{}, std::forward_as_tuple(args...)); } namespace detail { // @a T has the @c empty() method. template <typename T> auto Optional(meta::CaseTag<2>, TextView fmt, T &&t) -> decltype(void(t.empty()), meta::TypeFunc<SubText<T>>()) { return SubText<T>(t.empty() ? TextView{} : fmt, std::forward_as_tuple(t)); } // @a T is convertible to @c bool. template <typename T> auto Optional(meta::CaseTag<1>, TextView fmt, T &&t) -> decltype(bool(t), meta::TypeFunc<SubText<T>>()) { return SubText<T>(bool(t) ? fmt : TextView{}, std::forward_as_tuple(t)); } // @a T is not optional, always print. template <typename T> auto Optional(meta::CaseTag<0>, TextView fmt, T &&t) -> SubText<T> { return SubText<T>(fmt, std::forward_as_tuple(t)); } } // namespace detail /** Simplified optional text wrapper. * * @tparam ARG the type of the (single) argument. * @param fmt Format string. * @param arg The single argument to the format string and predicate. * @return An optional text wrapper. * * This generates output iff @a arg is not empty. @a fmt is required to take only a single * argument, which will be @a arg. This is a convenience overload, to handle the common case * where the argument and the conditional are the same. The argument must have one of the * following properties in order to serve as the conditional. These are checked in order. * * - The @c empty() method which returns @c true if the argument is empty and should not be printed. * This handles the case of C++ string types. * * - Conversion to @c bool which is @c false if the argument should not be printed. This covers the * case of pointers. * * As an example, if an output function had three strings @a alpha, @a bravo, and * @a charlie, each of which could be null, which should be output with space separators, * this would be * @code * w.print("Leading text{}{}{}.", Optional(" {}", alpha) * , Optional(" {}", bravo) * , Optional(" {}", charlie)); * @endcode * * Because of the property handling, these strings can be C styles strings ( @c char* ) or C++ * string types (such as @c std::string_view ). * */ template <typename ARG> SubText<ARG> Optional(TextView fmt, ARG &&arg) { return detail::Optional(meta::CaseArg, fmt, std::forward<ARG>(arg)); } /** Convert from ASCII hexadecimal to raw bytes. * * E.g. if the source span contains "4576696c20446176652052756c7a" then "Evil Dave Rulz" is the output. * For format specifier support, on lhe max width is used. Any @c MemSpan compatible class can be used * as the target, including @c std::string and @c std::string_view. * * @code * void f(std::string const& str) { * w.print("{}", bwf::UnHex(str)); * // ... * @endcode */ struct UnHex { UnHex(MemSpan<void const> const &span) : _span(span) {} MemSpan<void const> _span; ///< Source span. }; } // namespace bwf /** Repeatedly output a pattern. * * @param w Output. * @param spec Format specifier. * @param pattern Output patterning. * @return @a w * * The @a pattern contains the count and text to output. */ BufferWriter &bwformat(BufferWriter &w, bwf::Spec const &spec, bwf::Pattern const &pattern); /** Format an integer as an @c errno value. * * @param w Output. * @param spec Format specifier. * @param e Error code. * @return @a w */ BufferWriter &bwformat(BufferWriter &w, bwf::Spec const &spec, bwf::Errno const &e); /** Format a timestamp wrapped in a @c Date. * * @param w Output. * @param spec Format specifier. * @param date Timestamp. * @return @a w */ BufferWriter &bwformat(BufferWriter &w, bwf::Spec const &spec, bwf::Date const &date); BufferWriter &bwformat(BufferWriter &w, bwf::Spec const &spec, bwf::UnHex const &obj); /** Output a nested formatted string. * * @tparam Args Argument pack for @a subtext. * @param w Output * @param subtext Format string and arguments. * @return @a w * * This supports a nested format string and arguments inside another format string. This is most often useful * if one of the formats is fixed or pre-compiled. * * @code * bwformat(w, "Line {} offset {} with data {}.", line_no, line_off, bwf::SubText("alpha {} bravo {}", alpha, bravo")); * @endcode * * @see bwf::Subtext */ template <typename... Args> BufferWriter & bwformat(BufferWriter &w, bwf::Spec const &, bwf::SubText<Args...> const &subtext) { if (!subtext._fmt.empty()) { w.print_v(subtext._fmt, subtext._args); } return w; } }} // namespace swoc::SWOC_VERSION_NS