From: Phil Willoughby Date: Tue, 7 Feb 2017 08:45:01 +0000 (-0800) Subject: Modify BaseFormatter to help static analysers X-Git-Tag: v2017.03.06.00~51 X-Git-Url: http://demsky.eecs.uci.edu/git/?a=commitdiff_plain;h=115059030653fe6f73e351073097ba1e06507f21;p=folly.git Modify BaseFormatter to help static analysers Summary: This decouples the format-string-parser from the storage of the arguments to be formatted: a static analyser can now define a class which derives from `BaseFormatter` with no storage and use `BaseFormatter::operator()` to verify the syntax of the format string and determine which arguments will be referenced. This method of allowing overrides is resolved at compile time; the benchmarks confirm that there is no run-time difference. Reviewed By: yfeldblum Differential Revision: D4507689 fbshipit-source-id: f109d81ae54dc074ac363720ef6e565520435d26 --- diff --git a/folly/Format-inl.h b/folly/Format-inl.h index 1b998dec..70f5564d 100644 --- a/folly/Format-inl.h +++ b/folly/Format-inl.h @@ -159,11 +159,7 @@ BaseFormatter::BaseFormatter( Args&&... args) : str_(str), values_(FormatValue::type>( - std::forward(args))...) { - static_assert( - !containerMode || sizeof...(Args) == 1, - "Exactly one argument required in container mode"); -} + std::forward(args))...) {} template template @@ -246,7 +242,7 @@ void BaseFormatter::operator()( arg.widthIndex == FormatArg::kNoIndex, "cannot provide width arg index without value arg index"); int sizeArg = nextArg++; - arg.width = getSizeArg(size_t(sizeArg), arg); + arg.width = asDerived().getSizeArg(size_t(sizeArg), arg); } argIndex = nextArg++; @@ -256,7 +252,7 @@ void BaseFormatter::operator()( arg.enforce( arg.widthIndex != FormatArg::kNoIndex, "cannot provide value arg index without width arg index"); - arg.width = getSizeArg(size_t(arg.widthIndex), arg); + arg.width = asDerived().getSizeArg(size_t(arg.widthIndex), arg); } try { @@ -274,7 +270,7 @@ void BaseFormatter::operator()( "folly::format: may not have both default and explicit arg indexes"); } - doFormat(size_t(argIndex), arg, out); + asDerived().template doFormat(size_t(argIndex), arg, out); } } diff --git a/folly/Format.h b/folly/Format.h index f87d90a9..8986c4ca 100644 --- a/folly/Format.h +++ b/folly/Format.h @@ -56,9 +56,14 @@ 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 { @@ -108,6 +113,10 @@ class BaseFormatter { 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 { @@ -118,7 +127,7 @@ 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); } @@ -196,7 +205,11 @@ class Formatter : public BaseFormatter< : BaseFormatter< Formatter, containerMode, - Args...>(str, std::forward(args)...) {} + 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 {