X-Git-Url: http://plrg.eecs.uci.edu/git/?p=folly.git;a=blobdiff_plain;f=folly%2FFormat.h;h=b8df86fbfa1e8d02c54251011ea3b570df62e892;hp=c4e18f75f0de030fa07f35510191418307aee107;hb=95d9935053bd95825ecd84fd647d697df1113daf;hpb=6f3352378fe7effd50c2b7496f11348f27449977 diff --git a/folly/Format.h b/folly/Format.h index c4e18f75..b8df86fb 100644 --- a/folly/Format.h +++ b/folly/Format.h @@ -1,5 +1,5 @@ /* - * Copyright 2013 Facebook, Inc. + * Copyright 2017 Facebook, Inc. * * Licensed under the Apache License, Version 2.0 (the "License"); * you may not use this file except in compliance with the License. @@ -14,56 +14,61 @@ * limitations under the License. */ -#ifndef FOLLY_FORMAT_H_ +#pragma once #define FOLLY_FORMAT_H_ -#include +#include +#include #include #include -#include -#include -#include -#include - -#include - -#include "folly/FBVector.h" -#include "folly/Conv.h" -#include "folly/Range.h" -#include "folly/Traits.h" -#include "folly/Likely.h" -#include "folly/String.h" -#include "folly/small_vector.h" -#include "folly/FormatArg.h" + +#include +#include +#include +#include +#include // Ignore shadowing warnings within this file, so includers can use -Wshadow. -#pragma GCC diagnostic push -#pragma GCC diagnostic ignored "-Wshadow" +FOLLY_PUSH_WARNING +FOLLY_GCC_DISABLE_WARNING("-Wshadow") namespace folly { // forward declarations -template class Formatter; +template +class Formatter; template Formatter format(StringPiece fmt, Args&&... args); template Formatter vformat(StringPiece fmt, C&& container); -template class FormatValue; +template +class FormatValue; + +// meta-attribute to identify formatters in this sea of template weirdness +namespace detail { +class FormatterTag {}; +} // namespace detail /** * Formatter class. * - * Note that this class is tricky, as it keeps *references* to its arguments - * (and doesn't copy the passed-in format string). Thankfully, you can't use - * this directly, you have to use format(...) below. + * Note that this class is tricky, as it keeps *references* to its lvalue + * arguments (while it takes ownership of the temporaries), and it doesn't + * copy the passed-in format string. Thankfully, you can't use this + * directly, you have to use format(...) below. */ -template -class Formatter { - template - friend Formatter format(StringPiece fmt, A&&... arg); - template - friend Formatter vformat(StringPiece fmt, C&& container); +/* BaseFormatter class. + * Overridable behaviours: + * You may override the actual formatting of positional parameters in + * `doFormatArg`. The Formatter class provides the default implementation. + * + * You may also override `doFormat` and `getSizeArg`. These override points were + * added to permit static analysis of format strings, when it is inconvenient + * or impossible to instantiate a BaseFormatter with the correct storage + */ +template +class BaseFormatter { public: /** * Append to output. out(StringPiece sp) may be called (more than once) @@ -75,9 +80,9 @@ class Formatter { * Append to a string. */ template - typename std::enable_if::value>::type - appendTo(Str& str) const { - auto appender = [&str] (StringPiece s) { str.append(s.data(), s.size()); }; + typename std::enable_if::value>::type appendTo( + Str& str) const { + auto appender = [&str](StringPiece s) { str.append(s.data(), s.size()); }; (*this)(appender); } @@ -99,38 +104,33 @@ class Formatter { return s; } - private: - explicit Formatter(StringPiece str, Args&&... args); - - // Not copyable - Formatter(const Formatter&) = delete; - Formatter& operator=(const Formatter&) = delete; - - // Movable, but the move constructor and assignment operator are private, - // for the exclusive use of format() (below). This way, you can't create - // a Formatter object, but can handle references to it (for streaming, - // conversion to string, etc) -- which is good, as Formatter objects are - // dangerous (they hold references, possibly to temporaries) - Formatter(Formatter&&) = default; - Formatter& operator=(Formatter&&) = default; + /** + * Metadata to identify generated children of BaseFormatter + */ + typedef detail::FormatterTag IsFormatter; + typedef BaseFormatter BaseType; - typedef std::tuple::type>...> ValueTuple; + private: + typedef std::tuple ValueTuple; static constexpr size_t valueCount = std::tuple_size::value; + Derived const& asDerived() const { + return *static_cast(this); + } + template typename std::enable_if::type - doFormatFrom(size_t i, FormatArg& arg, Callback& cb) const { - LOG(FATAL) << arg.errorStr("argument index out of range, max=", i); + doFormatFrom(size_t i, FormatArg& arg, Callback& /*cb*/) const { + arg.error("argument index out of range, max=", i); } template typename std::enable_if<(K < valueCount)>::type doFormatFrom(size_t i, FormatArg& arg, Callback& cb) const { if (i == K) { - std::get(values_).format(arg, cb); + asDerived().template doFormatArg(arg, cb); } else { - doFormatFrom(i, arg, cb); + doFormatFrom(i, arg, cb); } } @@ -139,31 +139,145 @@ class Formatter { return doFormatFrom<0>(i, arg, cb); } + template + typename std::enable_if::type getSizeArgFrom( + size_t i, + const FormatArg& arg) const { + arg.error("argument index out of range, max=", i); + } + + template + typename std::enable_if< + std::is_integral::value && !std::is_same::value, + int>::type + getValue(const FormatValue& format, const FormatArg&) const { + return static_cast(format.getValue()); + } + + template + typename std::enable_if< + !std::is_integral::value || std::is_same::value, + int>::type + getValue(const FormatValue&, const FormatArg& arg) const { + arg.error("dynamic field width argument must be integral"); + } + + template + typename std::enable_if < + K::type getSizeArgFrom(size_t i, const FormatArg& arg) + const { + if (i == K) { + return getValue(getFormatValue(), arg); + } + return getSizeArgFrom(i, arg); + } + + int getSizeArg(size_t i, const FormatArg& arg) const { + return getSizeArgFrom<0>(i, arg); + } + StringPiece str_; + + protected: + explicit BaseFormatter(StringPiece str, Args&&... args); + + // Not copyable + BaseFormatter(const BaseFormatter&) = delete; + BaseFormatter& operator=(const BaseFormatter&) = delete; + + // Movable, but the move constructor and assignment operator are private, + // for the exclusive use of format() (below). This way, you can't create + // a Formatter object, but can handle references to it (for streaming, + // conversion to string, etc) -- which is good, as Formatter objects are + // dangerous (they may hold references). + BaseFormatter(BaseFormatter&&) = default; + BaseFormatter& operator=(BaseFormatter&&) = default; + + template + using ArgType = typename std::tuple_element::type; + + template + FormatValue>::type> getFormatValue() const { + return FormatValue>::type>( + std::get(values_)); + } + ValueTuple values_; }; +template +class Formatter : public BaseFormatter< + Formatter, + containerMode, + Args...> { + private: + explicit Formatter(StringPiece& str, Args&&... args) + : BaseFormatter< + Formatter, + containerMode, + Args...>(str, std::forward(args)...) { + static_assert( + !containerMode || sizeof...(Args) == 1, + "Exactly one argument required in container mode"); + } + + template + void doFormatArg(FormatArg& arg, Callback& cb) const { + this->template getFormatValue().format(arg, cb); + } + + friend class BaseFormatter< + Formatter, + containerMode, + Args...>; + + template + friend Formatter format(StringPiece fmt, A&&... arg); + template + friend Formatter vformat(StringPiece fmt, C&& container); +}; + /** * Formatter objects can be written to streams. */ -template -std::ostream& operator<<(std::ostream& out, - const Formatter& formatter) { - auto writer = [&out] (StringPiece sp) { out.write(sp.data(), sp.size()); }; +template +std::ostream& operator<<( + std::ostream& out, + const Formatter& formatter) { + auto writer = [&out](StringPiece sp) { + out.write(sp.data(), std::streamsize(sp.size())); + }; formatter(writer); return out; } +/** + * Formatter objects can be written to stdio FILEs. + */ +template +void writeTo( + FILE* fp, + const BaseFormatter& formatter); + /** * Create a formatter object. * * std::string formatted = format("{} {}", 23, 42).str(); * LOG(INFO) << format("{} {}", 23, 42); + * writeTo(stdout, format("{} {}", 23, 42)); */ template Formatter format(StringPiece fmt, Args&&... args) { - return Formatter( - fmt, std::forward(args)...); + return Formatter(fmt, std::forward(args)...); +} + +/** + * Like format(), but immediately returns the formatted string instead of an + * intermediate format object. + */ +template +inline std::string sformat(StringPiece fmt, Args&&... args) { + return format(fmt, std::forward(args)...).str(); } /** @@ -181,8 +295,68 @@ Formatter format(StringPiece fmt, Args&&... args) { */ template Formatter vformat(StringPiece fmt, Container&& container) { - return Formatter( - fmt, std::forward(container)); + return Formatter(fmt, std::forward(container)); +} + +/** + * Like vformat(), but immediately returns the formatted string instead of an + * intermediate format object. + */ +template +inline std::string svformat(StringPiece fmt, Container&& container) { + return vformat(fmt, std::forward(container)).str(); +} + +/** + * Exception class thrown when a format key is not found in the given + * associative container keyed by strings. We inherit std::out_of_range for + * compatibility with callers that expect exception to be thrown directly + * by std::map or std::unordered_map. + * + * Having the key be at the end of the message string, we can access it by + * simply adding its offset to what(). Not storing separate std::string key + * makes the exception type small and noexcept-copyable like std::out_of_range, + * and therefore able to fit in-situ in exception_wrapper. + */ +class FormatKeyNotFoundException : public std::out_of_range { + public: + explicit FormatKeyNotFoundException(StringPiece key); + + char const* key() const noexcept { + return what() + kMessagePrefix.size(); + } + + private: + static constexpr StringPiece const kMessagePrefix = "format key not found: "; +}; + +namespace detail { +[[noreturn]] void throwFormatKeyNotFoundException(StringPiece key); +} // namespace detail + +/** + * Wrap a sequence or associative container so that out-of-range lookups + * return a default value rather than throwing an exception. + * + * Usage: + * format("[no_such_key"], defaulted(map, 42)) -> 42 + */ +namespace detail { +template +struct DefaultValueWrapper { + DefaultValueWrapper(const Container& container, const Value& defaultValue) + : container(container), defaultValue(defaultValue) {} + + const Container& container; + const Value& defaultValue; +}; +} // namespace detail + +template +detail::DefaultValueWrapper defaulted( + const Container& c, + const Value& v) { + return detail::DefaultValueWrapper(c, v); } /** @@ -194,7 +368,7 @@ Formatter vformat(StringPiece fmt, Container&& container) { * Shortcut for toAppend(format(...), &foo); */ template -typename std::enable_if::value>::type +typename std::enable_if::value>::type format(Str* out, StringPiece fmt, Args&&... args) { format(fmt, std::forward(args)...).appendTo(*out); } @@ -203,7 +377,7 @@ format(Str* out, StringPiece fmt, Args&&... args) { * Append vformatted output to a string. */ template -typename std::enable_if::value>::type +typename std::enable_if::value>::type vformat(Str* out, StringPiece fmt, Container&& container) { vformat(fmt, std::forward(container)).appendTo(*out); } @@ -230,21 +404,28 @@ void formatString(StringPiece val, FormatArg& arg, FormatCallback& cb); * field width") */ template -void formatNumber(StringPiece val, int prefixLen, FormatArg& arg, - FormatCallback& cb); - +void formatNumber( + StringPiece val, + int prefixLen, + FormatArg& arg, + FormatCallback& cb); /** * Format a Formatter object recursively. Behaves just like * formatString(fmt.str(), arg, cb); but avoids creating a temporary * string if possible. */ -template -void formatFormatter(const Formatter& formatter, - FormatArg& arg, - FormatCallback& cb); - -} // namespace format_value +template < + class FormatCallback, + class Derived, + bool containerMode, + class... Args> +void formatFormatter( + const BaseFormatter& formatter, + FormatArg& arg, + FormatCallback& cb); + +} // namespace format_value /* * Specialize folly::FormatValue for your type. @@ -268,10 +449,52 @@ void formatFormatter(const Formatter& formatter, * empty string) */ -} // namespace folly +namespace detail { + +template +struct IsFormatter : public std::false_type {}; + +template +struct IsFormatter< + T, + typename std::enable_if< + std::is_same::value>:: + type> : public std::true_type {}; +} // namespace detail + +// Deprecated API. formatChecked() et. al. now behave identically to their +// non-Checked counterparts. +template +Formatter formatChecked(StringPiece fmt, Args&&... args) { + return format(fmt, std::forward(args)...); +} +template +inline std::string sformatChecked(StringPiece fmt, Args&&... args) { + return formatChecked(fmt, std::forward(args)...).str(); +} +template +Formatter vformatChecked( + StringPiece fmt, + Container&& container) { + return vformat(fmt, std::forward(container)); +} +template +inline std::string svformatChecked(StringPiece fmt, Container&& container) { + return vformatChecked(fmt, std::forward(container)).str(); +} +template +typename std::enable_if::value>::type +formatChecked(Str* out, StringPiece fmt, Args&&... args) { + formatChecked(fmt, std::forward(args)...).appendTo(*out); +} +template +typename std::enable_if::value>::type +vformatChecked(Str* out, StringPiece fmt, Container&& container) { + vformatChecked(fmt, std::forward(container)).appendTo(*out); +} -#include "folly/Format-inl.h" +} // namespace folly -#pragma GCC diagnostic pop +#include -#endif /* FOLLY_FORMAT_H_ */ +FOLLY_POP_WARNING