X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;f=folly%2FFormat.h;h=7601ae528aa7417dea1f8aa244d92452ee4b2315;hb=3cdd3857fbfacd9312a23214f54a47f156726927;hp=a270b43b36e0a6c6f961afc4984336c84de62659;hpb=afa85fd054e5e8c0297c440acb6be6a40f30fb86;p=folly.git diff --git a/folly/Format.h b/folly/Format.h index a270b43b..7601ae52 100644 --- a/folly/Format.h +++ b/folly/Format.h @@ -1,5 +1,5 @@ /* - * Copyright 2014 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,28 +14,18 @@ * limitations under the License. */ -#ifndef FOLLY_FORMAT_H_ +#pragma once #define FOLLY_FORMAT_H_ -#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 @@ -51,6 +41,11 @@ template Formatter vformat(StringPiece fmt, C&& container); template class FormatValue; +// meta-attribute to identify formatters in this sea of template weirdness +namespace detail { +class FormatterTag {}; +}; + /** * Formatter class. * @@ -59,22 +54,13 @@ template class FormatValue; * this directly, you have to use format(...) below. */ -template -class Formatter { +/* BaseFormatter class. Currently, the only behavior that can be + * overridden is the actual formatting of positional parameters in + * `doFormatArg`. The Formatter class provides the default implementation. + */ +template +class BaseFormatter { public: - /* - * Change whether or not Formatter should crash or throw exceptions if the - * format string is invalid. - * - * Crashing is desirable for literal format strings that are fixed at compile - * time. Errors in the format string are generally programmer bugs, and - * should be caught early on in development. Crashing helps ensure these - * problems are noticed. - */ - void setCrashOnError(bool crash) { - crashOnError_ = crash; - } - /** * Append to output. out(StringPiece sp) may be called (more than once) */ @@ -109,32 +95,20 @@ 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; + private: typedef std::tuple::type>...> ValueTuple; static constexpr size_t valueCount = std::tuple_size::value; - void handleFormatStrError() const FOLLY_NORETURN; - template - void appendOutput(Output& out) const; - template typename std::enable_if::type - doFormatFrom(size_t i, FormatArg& arg, Callback& cb) const { + doFormatFrom(size_t i, FormatArg& arg, Callback& /*cb*/) const { arg.error("argument index out of range, max=", i); } @@ -142,7 +116,7 @@ class Formatter { 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); + static_cast(this)->template doFormatArg(arg, cb); } else { doFormatFrom(i, arg, cb); } @@ -153,18 +127,82 @@ 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::value && + !std::is_same::value, int>::type + getValue(const FormatValue& format, const FormatArg&) const { + return static_cast(format.getValue()); + } + + template + typename std::enable_if::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::type + getSizeArgFrom(size_t i, const FormatArg& arg) const { + if (i == K) { + return getValue(std::get(values_), 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 hold references, possibly to temporaries) + BaseFormatter(BaseFormatter&&) = default; + BaseFormatter& operator=(BaseFormatter&&) = default; + ValueTuple values_; - bool crashOnError_{true}; +}; + +template +class Formatter : public BaseFormatter, + containerMode, + Args...> { + private: + explicit Formatter(StringPiece& str, Args&&... args) + : BaseFormatter, + containerMode, + Args...>(str, std::forward(args)...) {} + + template + void doFormatArg(FormatArg& arg, Callback& cb) const { + std::get(this->values_).format(arg, cb); + } + + friend class BaseFormatter, + containerMode, + Args...>; template friend Formatter format(StringPiece fmt, A&&... arg); - template - friend Formatter formatChecked(StringPiece fmt, A&&... arg); template friend Formatter vformat(StringPiece fmt, C&& container); - template - friend Formatter vformatChecked(StringPiece fmt, C&& container); }; /** @@ -173,7 +211,9 @@ class Formatter { template std::ostream& operator<<(std::ostream& out, const Formatter& formatter) { - auto writer = [&out] (StringPiece sp) { out.write(sp.data(), sp.size()); }; + auto writer = [&out](StringPiece sp) { + out.write(sp.data(), std::streamsize(sp.size())); + }; formatter(writer); return out; } @@ -181,8 +221,9 @@ std::ostream& operator<<(std::ostream& out, /** * Formatter objects can be written to stdio FILEs. */ -template -void writeTo(FILE* fp, const Formatter& formatter); +template +void writeTo(FILE* fp, + const BaseFormatter& formatter); /** * Create a formatter object. @@ -190,16 +231,6 @@ void writeTo(FILE* fp, const Formatter& formatter); * std::string formatted = format("{} {}", 23, 42).str(); * LOG(INFO) << format("{} {}", 23, 42); * writeTo(stdout, format("{} {}", 23, 42)); - * - * Note that format() will crash the program if the format string is invalid. - * Normally, the format string is a fixed string literal specified by the - * programmer. Invalid format strings are normally programmer bugs, and should - * be caught early on during development. Crashing helps ensure these bugs are - * found. - * - * Use formatChecked() if you have a dynamic format string (for example, a user - * supplied value). formatChecked() will throw an exception rather than - * crashing the program. */ template Formatter format(StringPiece fmt, Args&&... args) { @@ -208,18 +239,12 @@ Formatter format(StringPiece fmt, Args&&... args) { } /** - * Create a formatter object from a dynamic format string. - * - * This is identical to format(), but throws an exception if the format string - * is invalid, rather than aborting the program. This allows it to be used - * with user-specified format strings which are not guaranteed to be well - * formed. + * Like format(), but immediately returns the formatted string instead of an + * intermediate format object. */ template -Formatter formatChecked(StringPiece fmt, Args&&... args) { - Formatter f(fmt, std::forward(args)...); - f.setCrashOnError(false); - return f; +inline std::string sformat(StringPiece fmt, Args&&... args) { + return format(fmt, std::forward(args)...).str(); } /** @@ -242,19 +267,37 @@ Formatter vformat(StringPiece fmt, Container&& container) { } /** - * Create a formatter object from a dynamic format string. - * - * This is identical to vformat(), but throws an exception if the format string - * is invalid, rather than aborting the program. This allows it to be used - * with user-specified format strings which are not guaranteed to be well - * formed. + * Like vformat(), but immediately returns the formatted string instead of an + * intermediate format object. */ template -Formatter vformatChecked(StringPiece fmt, - Container&& container) { - Formatter f(fmt, std::forward(container)); - f.setCrashOnError(false); - return f; +inline std::string svformat(StringPiece fmt, Container&& container) { + return vformat(fmt, std::forward(container)).str(); +} + +/** + * 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 + +template +detail::DefaultValueWrapper +defaulted(const Container& c, const Value& v) { + return detail::DefaultValueWrapper(c, v); } /** @@ -311,10 +354,14 @@ void formatNumber(StringPiece val, int prefixLen, FormatArg& arg, * formatString(fmt.str(), arg, cb); but avoids creating a temporary * string if possible. */ -template -void formatFormatter(const Formatter& formatter, - FormatArg& arg, - FormatCallback& cb); +template +void formatFormatter( + const BaseFormatter& formatter, + FormatArg& arg, + FormatCallback& cb); } // namespace format_value @@ -340,10 +387,51 @@ void formatFormatter(const Formatter& formatter, * empty string) */ +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 {}; +} // folly::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); +} + } // namespace folly -#include "folly/Format-inl.h" +#include #pragma GCC diagnostic pop - -#endif /* FOLLY_FORMAT_H_ */