| /* |
| * Copyright 2015 Facebook, Inc. |
| * |
| * Licensed under the Apache License, Version 2.0 (the "License"); |
| * you may not use this file except in compliance with the License. |
| * You may obtain a copy of the License at |
| * |
| * http://www.apache.org/licenses/LICENSE-2.0 |
| * |
| * Unless required by applicable law or agreed to in writing, software |
| * distributed under the License is distributed on an "AS IS" BASIS, |
| * WITHOUT WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. |
| * See the License for the specific language governing permissions and |
| * limitations under the License. |
| */ |
| |
| #ifndef FOLLY_FORMAT_H_ |
| #define FOLLY_FORMAT_H_ |
| |
| #include <cstdio> |
| #include <tuple> |
| #include <type_traits> |
| |
| #include <folly/Conv.h> |
| #include <folly/Range.h> |
| #include <folly/Traits.h> |
| #include <folly/String.h> |
| #include <folly/FormatArg.h> |
| |
| // Ignore shadowing warnings within this file, so includers can use -Wshadow. |
| #pragma GCC diagnostic push |
| #pragma GCC diagnostic ignored "-Wshadow" |
| |
| namespace folly { |
| |
| // forward declarations |
| template <bool containerMode, class... Args> class Formatter; |
| template <class... Args> |
| Formatter<false, Args...> format(StringPiece fmt, Args&&... args); |
| template <class C> |
| Formatter<true, C> vformat(StringPiece fmt, C&& container); |
| template <class T, class Enable=void> class FormatValue; |
| |
| // meta-attribute to identify formatters in this sea of template weirdness |
| namespace detail { |
| class FormatterTag {}; |
| }; |
| |
| /** |
| * 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. |
| */ |
| |
| /* 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 Derived, bool containerMode, class... Args> |
| class BaseFormatter { |
| public: |
| /** |
| * Append to output. out(StringPiece sp) may be called (more than once) |
| */ |
| template <class Output> |
| void operator()(Output& out) const; |
| |
| /** |
| * Append to a string. |
| */ |
| template <class Str> |
| typename std::enable_if<IsSomeString<Str>::value>::type |
| appendTo(Str& str) const { |
| auto appender = [&str] (StringPiece s) { str.append(s.data(), s.size()); }; |
| (*this)(appender); |
| } |
| |
| /** |
| * Conversion to string |
| */ |
| std::string str() const { |
| std::string s; |
| appendTo(s); |
| return s; |
| } |
| |
| /** |
| * Conversion to fbstring |
| */ |
| fbstring fbstr() const { |
| fbstring s; |
| appendTo(s); |
| return s; |
| } |
| |
| /** |
| * metadata to identify generated children of BaseFormatter |
| */ |
| typedef detail::FormatterTag IsFormatter; |
| typedef BaseFormatter BaseType; |
| |
| private: |
| typedef std::tuple<FormatValue< |
| typename std::decay<Args>::type>...> ValueTuple; |
| static constexpr size_t valueCount = std::tuple_size<ValueTuple>::value; |
| |
| template <size_t K, class Callback> |
| typename std::enable_if<K == valueCount>::type |
| doFormatFrom(size_t i, FormatArg& arg, Callback& /*cb*/) const { |
| arg.error("argument index out of range, max=", i); |
| } |
| |
| template <size_t K, class Callback> |
| typename std::enable_if<(K < valueCount)>::type |
| doFormatFrom(size_t i, FormatArg& arg, Callback& cb) const { |
| if (i == K) { |
| static_cast<const Derived*>(this)->template doFormatArg<K>(arg, cb); |
| } else { |
| doFormatFrom<K+1>(i, arg, cb); |
| } |
| } |
| |
| template <class Callback> |
| void doFormat(size_t i, FormatArg& arg, Callback& cb) const { |
| return doFormatFrom<0>(i, arg, cb); |
| } |
| |
| template <size_t K> |
| typename std::enable_if<K == valueCount, int>::type |
| getSizeArgFrom(size_t i, const FormatArg& arg) const { |
| arg.error("argument index out of range, max=", i); |
| } |
| |
| template <class T> |
| typename std::enable_if<std::is_integral<T>::value && |
| !std::is_same<T, bool>::value, int>::type |
| getValue(const FormatValue<T>& format, const FormatArg&) const { |
| return static_cast<int>(format.getValue()); |
| } |
| |
| template <class T> |
| typename std::enable_if<!std::is_integral<T>::value || |
| std::is_same<T, bool>::value, int>::type |
| getValue(const FormatValue<T>&, const FormatArg& arg) const { |
| arg.error("dynamic field width argument must be integral"); |
| } |
| |
| template <size_t K> |
| typename std::enable_if<K < valueCount, int>::type |
| getSizeArgFrom(size_t i, const FormatArg& arg) const { |
| if (i == K) { |
| return getValue(std::get<K>(values_), arg); |
| } |
| return getSizeArgFrom<K+1>(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_; |
| }; |
| |
| template <bool containerMode, class... Args> |
| class Formatter : public BaseFormatter<Formatter<containerMode, Args...>, |
| containerMode, |
| Args...> { |
| private: |
| explicit Formatter(StringPiece& str, Args&&... args) |
| : BaseFormatter<Formatter<containerMode, Args...>, |
| containerMode, |
| Args...>(str, std::forward<Args>(args)...) {} |
| |
| template <size_t K, class Callback> |
| void doFormatArg(FormatArg& arg, Callback& cb) const { |
| std::get<K>(this->values_).format(arg, cb); |
| } |
| |
| friend class BaseFormatter<Formatter<containerMode, Args...>, |
| containerMode, |
| Args...>; |
| |
| template <class... A> |
| friend Formatter<false, A...> format(StringPiece fmt, A&&... arg); |
| template <class C> |
| friend Formatter<true, C> vformat(StringPiece fmt, C&& container); |
| }; |
| |
| /** |
| * Formatter objects can be written to streams. |
| */ |
| template<bool containerMode, class... Args> |
| std::ostream& operator<<(std::ostream& out, |
| const Formatter<containerMode, Args...>& formatter) { |
| auto writer = [&out] (StringPiece sp) { out.write(sp.data(), sp.size()); }; |
| formatter(writer); |
| return out; |
| } |
| |
| /** |
| * Formatter objects can be written to stdio FILEs. |
| */ |
| template <class Derived, bool containerMode, class... Args> |
| void writeTo(FILE* fp, |
| const BaseFormatter<Derived, containerMode, Args...>& formatter); |
| |
| /** |
| * Create a formatter object. |
| * |
| * std::string formatted = format("{} {}", 23, 42).str(); |
| * LOG(INFO) << format("{} {}", 23, 42); |
| * writeTo(stdout, format("{} {}", 23, 42)); |
| */ |
| template <class... Args> |
| Formatter<false, Args...> format(StringPiece fmt, Args&&... args) { |
| return Formatter<false, Args...>( |
| fmt, std::forward<Args>(args)...); |
| } |
| |
| /** |
| * Like format(), but immediately returns the formatted string instead of an |
| * intermediate format object. |
| */ |
| template <class... Args> |
| inline std::string sformat(StringPiece fmt, Args&&... args) { |
| return format(fmt, std::forward<Args>(args)...).str(); |
| } |
| |
| /** |
| * Create a formatter object that takes one argument (of container type) |
| * and uses that container to get argument values from. |
| * |
| * std::map<string, string> map { {"hello", "world"}, {"answer", "42"} }; |
| * |
| * The following are equivalent: |
| * format("{0[hello]} {0[answer]}", map); |
| * |
| * vformat("{hello} {answer}", map); |
| * |
| * but the latter is cleaner. |
| */ |
| template <class Container> |
| Formatter<true, Container> vformat(StringPiece fmt, Container&& container) { |
| return Formatter<true, Container>( |
| fmt, std::forward<Container>(container)); |
| } |
| |
| /** |
| * Like vformat(), but immediately returns the formatted string instead of an |
| * intermediate format object. |
| */ |
| template <class Container> |
| inline std::string svformat(StringPiece fmt, Container&& container) { |
| return vformat(fmt, std::forward<Container>(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 <class Container, class Value> struct DefaultValueWrapper { |
| DefaultValueWrapper(const Container& container, const Value& defaultValue) |
| : container(container), |
| defaultValue(defaultValue) { |
| } |
| |
| const Container& container; |
| const Value& defaultValue; |
| }; |
| } // namespace |
| |
| template <class Container, class Value> |
| detail::DefaultValueWrapper<Container, Value> |
| defaulted(const Container& c, const Value& v) { |
| return detail::DefaultValueWrapper<Container, Value>(c, v); |
| } |
| |
| /** |
| * Append formatted output to a string. |
| * |
| * std::string foo; |
| * format(&foo, "{} {}", 42, 23); |
| * |
| * Shortcut for toAppend(format(...), &foo); |
| */ |
| template <class Str, class... Args> |
| typename std::enable_if<IsSomeString<Str>::value>::type |
| format(Str* out, StringPiece fmt, Args&&... args) { |
| format(fmt, std::forward<Args>(args)...).appendTo(*out); |
| } |
| |
| /** |
| * Append vformatted output to a string. |
| */ |
| template <class Str, class Container> |
| typename std::enable_if<IsSomeString<Str>::value>::type |
| vformat(Str* out, StringPiece fmt, Container&& container) { |
| vformat(fmt, std::forward<Container>(container)).appendTo(*out); |
| } |
| |
| /** |
| * Utilities for all format value specializations. |
| */ |
| namespace format_value { |
| |
| /** |
| * Format a string in "val", obeying appropriate alignment, padding, width, |
| * and precision. Treats Align::DEFAULT as Align::LEFT, and |
| * Align::PAD_AFTER_SIGN as Align::RIGHT; use formatNumber for |
| * number-specific formatting. |
| */ |
| template <class FormatCallback> |
| void formatString(StringPiece val, FormatArg& arg, FormatCallback& cb); |
| |
| /** |
| * Format a number in "val"; the first prefixLen characters form the prefix |
| * (sign, "0x" base prefix, etc) which must be left-aligned if the alignment |
| * is Align::PAD_AFTER_SIGN. Treats Align::DEFAULT as Align::LEFT. Ignores |
| * arg.precision, as that has a different meaning for numbers (not "maximum |
| * field width") |
| */ |
| template <class FormatCallback> |
| 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 <class FormatCallback, |
| class Derived, |
| bool containerMode, |
| class... Args> |
| void formatFormatter( |
| const BaseFormatter<Derived, containerMode, Args...>& formatter, |
| FormatArg& arg, |
| FormatCallback& cb); |
| |
| } // namespace format_value |
| |
| /* |
| * Specialize folly::FormatValue for your type. |
| * |
| * FormatValue<T> is constructed with a (reference-collapsed) T&&, which is |
| * guaranteed to stay alive until the FormatValue object is destroyed, so you |
| * may keep a reference (or pointer) to it instead of making a copy. |
| * |
| * You must define |
| * template <class Callback> |
| * void format(FormatArg& arg, Callback& cb) const; |
| * with the following semantics: format the value using the given argument. |
| * |
| * arg is given by non-const reference for convenience -- it won't be reused, |
| * so feel free to modify it in place if necessary. (For example, wrap an |
| * existing conversion but change the default, or remove the "key" when |
| * extracting an element from a container) |
| * |
| * Call the callback to append data to the output. You may call the callback |
| * as many times as you'd like (or not at all, if you want to output an |
| * empty string) |
| */ |
| |
| namespace detail { |
| |
| template <class T, class Enable = void> |
| struct IsFormatter : public std::false_type {}; |
| |
| template <class T> |
| struct IsFormatter< |
| T, |
| typename std::enable_if< |
| std::is_same<typename T::IsFormatter, detail::FormatterTag>::value>:: |
| type> : public std::true_type {}; |
| } // folly::detail |
| |
| // Deprecated API. formatChecked() et. al. now behave identically to their |
| // non-Checked counterparts. |
| template <class... Args> |
| Formatter<false, Args...> formatChecked(StringPiece fmt, Args&&... args) { |
| return format(fmt, std::forward<Args>(args)...); |
| } |
| template <class... Args> |
| inline std::string sformatChecked(StringPiece fmt, Args&&... args) { |
| return formatChecked(fmt, std::forward<Args>(args)...).str(); |
| } |
| template <class Container> |
| Formatter<true, Container> vformatChecked(StringPiece fmt, |
| Container&& container) { |
| return vformat(fmt, std::forward<Container>(container)); |
| } |
| template <class Container> |
| inline std::string svformatChecked(StringPiece fmt, Container&& container) { |
| return vformatChecked(fmt, std::forward<Container>(container)).str(); |
| } |
| template <class Str, class... Args> |
| typename std::enable_if<IsSomeString<Str>::value>::type |
| formatChecked(Str* out, StringPiece fmt, Args&&... args) { |
| formatChecked(fmt, std::forward<Args>(args)...).appendTo(*out); |
| } |
| template <class Str, class Container> |
| typename std::enable_if<IsSomeString<Str>::value>::type |
| vformatChecked(Str* out, StringPiece fmt, Container&& container) { |
| vformatChecked(fmt, std::forward<Container>(container)).appendTo(*out); |
| } |
| |
| } // namespace folly |
| |
| #include <folly/Format-inl.h> |
| |
| #pragma GCC diagnostic pop |
| |
| #endif /* FOLLY_FORMAT_H_ */ |