/*
- * Copyright 2012 Facebook, Inc.
+ * Copyright 2014 Facebook, Inc.
*
* Licensed under the Apache License, Version 2.0 (the "License");
* you may not use this file except in compliance with the License.
#define FOLLY_FORMAT_H_
#include <array>
+#include <cstdio>
#include <tuple>
#include <type_traits>
#include <vector>
#include <map>
#include <unordered_map>
-#include <double-conversion.h>
+#include <double-conversion/double-conversion.h>
-#include "folly/FBVector.h"
-#include "folly/Conv.h"
-#include "folly/Range.h"
-#include "folly/Likely.h"
-#include "folly/String.h"
-#include "folly/small_vector.h"
-#include "folly/FormatArg.h"
+#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>
+
+// Ignore shadowing warnings within this file, so includers can use -Wshadow.
+#pragma GCC diagnostic push
+#pragma GCC diagnostic ignored "-Wshadow"
namespace folly {
template <bool containerMode, class... Args>
class Formatter {
- template <class... A>
- friend Formatter<false, A...> format(StringPiece fmt, A&&... arg);
- template <class C>
- friend Formatter<true, C> vformat(StringPiece fmt, C&& container);
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)
*/
* Append to a string.
*/
template <class Str>
- typename std::enable_if<detail::IsSomeString<Str>::value>::type
+ 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);
typename std::decay<Args>::type>...> ValueTuple;
static constexpr size_t valueCount = std::tuple_size<ValueTuple>::value;
+ FOLLY_NORETURN void handleFormatStrError() const;
+ template <class Output>
+ void appendOutput(Output& out) const;
+
template <size_t K, class Callback>
typename std::enable_if<K == valueCount>::type
doFormatFrom(size_t i, FormatArg& arg, Callback& cb) const {
StringPiece str_;
ValueTuple values_;
+ bool crashOnError_{true};
+
+ template <class... A>
+ friend Formatter<false, A...> format(StringPiece fmt, A&&... arg);
+ template <class... A>
+ friend Formatter<false, A...> formatChecked(StringPiece fmt, A&&... arg);
+ template <class C>
+ friend Formatter<true, C> vformat(StringPiece fmt, C&& container);
+ template <class C>
+ friend Formatter<true, C> vformatChecked(StringPiece fmt, C&& container);
};
/**
return out;
}
+/**
+ * Formatter objects can be written to stdio FILEs.
+ */
+template<bool containerMode, class... Args>
+void writeTo(FILE* fp, const Formatter<containerMode, Args...>& formatter);
+
/**
* Create a formatter object.
*
- * std::string formatted = format("{} {}", 23, 42);
+ * 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 <class... Args>
Formatter<false, Args...> format(StringPiece fmt, Args&&... 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 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 <class... Args>
+Formatter<false, Args...> formatChecked(StringPiece fmt, Args&&... args) {
+ Formatter<false, Args...> f(fmt, std::forward<Args>(args)...);
+ f.setCrashOnError(false);
+ return f;
+}
+
+/**
+ * Like formatChecked(), but immediately returns the formatted string instead of
+ * an intermediate format object.
+ */
+template <class... Args>
+inline std::string sformatChecked(StringPiece fmt, Args&&... args) {
+ return formatChecked(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.
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();
+}
+
+/**
+ * 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 <class Container>
+Formatter<true, Container> vformatChecked(StringPiece fmt,
+ Container&& container) {
+ Formatter<true, Container> f(fmt, std::forward<Container>(container));
+ f.setCrashOnError(false);
+ return f;
+}
+
+/**
+ * Like vformatChecked(), but immediately returns the formatted string instead
+ * of an intermediate format object.
+ */
+template <class Container>
+inline std::string svformatChecked(StringPiece fmt, Container&& container) {
+ return vformatChecked(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.
*
* Shortcut for toAppend(format(...), &foo);
*/
template <class Str, class... Args>
-typename std::enable_if<detail::IsSomeString<Str>::value>::type
+typename std::enable_if<IsSomeString<Str>::value>::type
format(Str* out, StringPiece fmt, Args&&... args) {
format(fmt, std::forward<Args>(args)...).appendTo(*out);
}
+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);
+}
+
/**
* Append vformatted output to a string.
*/
template <class Str, class Container>
-typename std::enable_if<detail::IsSomeString<Str>::value>::type
+typename std::enable_if<IsSomeString<Str>::value>::type
vformat(Str* out, StringPiece fmt, Container&& container) {
vformat(fmt, std::forward<Container>(container)).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);
+}
+
/**
* Utilities for all format value specializations.
*/
} // namespace folly
-#include "folly/Format-inl.h"
+#include <folly/Format-inl.h>
-#endif /* FOLLY_FORMAT_H_ */
+#pragma GCC diagnostic pop
+#endif /* FOLLY_FORMAT_H_ */