X-Git-Url: http://plrg.eecs.uci.edu/git/?a=blobdiff_plain;ds=sidebyside;f=folly%2FFormat.h;h=7c9929a9751411826d7b42a979e1b6090afb717d;hb=a6b10d84b12734b6cde0d94530b1e3e4635ce00a;hp=bbfb54c743d1a4aedbcbe97ae41d08ed7335db90;hpb=bd1cd83b305dceb9b5c5b6da1732f22ec9d2deef;p=folly.git diff --git a/folly/Format.h b/folly/Format.h index bbfb54c7..7c9929a9 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,42 +14,34 @@ * 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 +#include #include -#include -#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 { @@ -64,26 +56,18 @@ class FormatterTag {}; * this directly, you have to use format(...) below. */ -/* BaseFormatter class. Currently, the only behavior that can be - * overridden is the actual formatting of positional parameters in +/* 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: - /* - * 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) */ @@ -94,9 +78,9 @@ class BaseFormatter { * 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); } @@ -125,17 +109,17 @@ class BaseFormatter { typedef BaseFormatter BaseType; private: - typedef std::tuple::type>...> ValueTuple; + typedef std::tuple::type>...> + ValueTuple; static constexpr size_t valueCount = std::tuple_size::value; - FOLLY_NORETURN void handleFormatStrError() const; - template - void appendOutput(Output& out) const; + Derived const& asDerived() const { + return *static_cast(this); + } 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); } @@ -143,9 +127,9 @@ class BaseFormatter { typename std::enable_if<(K < valueCount)>::type doFormatFrom(size_t i, FormatArg& arg, Callback& cb) const { if (i == K) { - static_cast(this)->template doFormatArg(arg, cb); + asDerived().template doFormatArg(arg, cb); } else { - doFormatFrom(i, arg, cb); + doFormatFrom(i, arg, cb); } } @@ -154,8 +138,44 @@ class BaseFormatter { 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(std::get(values_), arg); + } + return getSizeArgFrom(i, arg); + } + + int getSizeArg(size_t i, const FormatArg& arg) const { + return getSizeArgFrom<0>(i, arg); + } + StringPiece str_; - bool crashOnError_{true}; protected: explicit BaseFormatter(StringPiece str, Args&&... args); @@ -176,41 +196,47 @@ class BaseFormatter { }; template -class Formatter : public BaseFormatter, - containerMode, - Args...> { +class Formatter : public BaseFormatter< + Formatter, + containerMode, + Args...> { private: explicit Formatter(StringPiece& str, Args&&... args) - : BaseFormatter, - containerMode, - Args...>(str, std::forward(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 { std::get(this->values_).format(arg, cb); } - friend class BaseFormatter, - containerMode, - Args...>; + friend class BaseFormatter< + Formatter, + 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); }; /** * 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; } @@ -219,8 +245,9 @@ std::ostream& operator<<(std::ostream& out, * Formatter objects can be written to stdio FILEs. */ template -void writeTo(FILE* fp, - const BaseFormatter& formatter); +void writeTo( + FILE* fp, + const BaseFormatter& formatter); /** * Create a formatter object. @@ -228,21 +255,10 @@ void writeTo(FILE* fp, * 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) { - return Formatter( - fmt, std::forward(args)...); + return Formatter(fmt, std::forward(args)...); } /** @@ -254,30 +270,6 @@ inline std::string sformat(StringPiece fmt, Args&&... args) { return format(fmt, std::forward(args)...).str(); } -/** - * 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. - */ -template -Formatter formatChecked(StringPiece fmt, Args&&... args) { - Formatter f(fmt, std::forward(args)...); - f.setCrashOnError(false); - return f; -} - -/** - * Like formatChecked(), but immediately returns the formatted string instead of - * an intermediate format object. - */ -template -inline std::string sformatChecked(StringPiece fmt, Args&&... args) { - return formatChecked(fmt, std::forward(args)...).str(); -} - /** * Create a formatter object that takes one argument (of container type) * and uses that container to get argument values from. @@ -293,8 +285,7 @@ inline std::string sformatChecked(StringPiece fmt, Args&&... args) { */ template Formatter vformat(StringPiece fmt, Container&& container) { - return Formatter( - fmt, std::forward(container)); + return Formatter(fmt, std::forward(container)); } /** @@ -306,31 +297,6 @@ inline std::string svformat(StringPiece fmt, Container&& container) { return vformat(fmt, std::forward(container)).str(); } -/** - * 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. - */ -template -Formatter vformatChecked(StringPiece fmt, - Container&& container) { - Formatter f(fmt, std::forward(container)); - f.setCrashOnError(false); - return f; -} - -/** - * Like vformatChecked(), but immediately returns the formatted string instead - * of an intermediate format object. - */ -template -inline std::string svformatChecked(StringPiece fmt, Container&& container) { - return vformatChecked(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. @@ -339,20 +305,20 @@ inline std::string svformatChecked(StringPiece fmt, Container&& container) { * format("[no_such_key"], defaulted(map, 42)) -> 42 */ namespace detail { -template struct DefaultValueWrapper { +template +struct DefaultValueWrapper { DefaultValueWrapper(const Container& container, const Value& defaultValue) - : container(container), - defaultValue(defaultValue) { - } + : container(container), defaultValue(defaultValue) {} const Container& container; const Value& defaultValue; }; -} // namespace +} // namespace template -detail::DefaultValueWrapper -defaulted(const Container& c, const Value& v) { +detail::DefaultValueWrapper defaulted( + const Container& c, + const Value& v) { return detail::DefaultValueWrapper(c, v); } @@ -370,12 +336,6 @@ format(Str* out, StringPiece fmt, Args&&... args) { format(fmt, std::forward(args)...).appendTo(*out); } -template -typename std::enable_if::value>::type -formatChecked(Str* out, StringPiece fmt, Args&&... args) { - formatChecked(fmt, std::forward(args)...).appendTo(*out); -} - /** * Append vformatted output to a string. */ @@ -385,12 +345,6 @@ vformat(Str* out, StringPiece fmt, Container&& container) { vformat(fmt, std::forward(container)).appendTo(*out); } -template -typename std::enable_if::value>::type -vformatChecked(Str* out, StringPiece fmt, Container&& container) { - vformatChecked(fmt, std::forward(container)).appendTo(*out); -} - /** * Utilities for all format value specializations. */ @@ -413,25 +367,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 +template < + class FormatCallback, + class Derived, + bool containerMode, + class... Args> void formatFormatter( const BaseFormatter& formatter, FormatArg& arg, FormatCallback& cb); -} // namespace format_value +} // namespace format_value /* * Specialize folly::FormatValue for your type. @@ -468,10 +425,39 @@ struct IsFormatter< type> : public std::true_type {}; } // folly::detail -} // namespace folly +// 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 -#pragma GCC diagnostic pop - -#endif /* FOLLY_FORMAT_H_ */ +FOLLY_POP_WARNING