diff --git a/format.cc b/format.cc index 20ab9bb0..ad970355 100644 --- a/format.cc +++ b/format.cc @@ -359,7 +359,7 @@ void fmt::BasicWriter::FormatDouble( // FormatError reporting unmatched '{'. The idea is that unmatched '{' // should override other errors. template -void fmt::BasicFormatter::ReportError( +void fmt::BasicWriter::FormatParser::ReportError( const Char *s, StringRef message) const { for (int num_open_braces = num_open_braces_; *s; ++s) { if (*s == '{') { @@ -375,7 +375,7 @@ void fmt::BasicFormatter::ReportError( // Parses an unsigned integer advancing s to the end of the parsed input. // This function assumes that the first character of s is a digit. template -unsigned fmt::BasicFormatter::ParseUInt(const Char *&s) const { +unsigned fmt::BasicWriter::FormatParser::ParseUInt(const Char *&s) const { assert('0' <= *s && *s <= '9'); unsigned value = 0; do { @@ -388,8 +388,8 @@ unsigned fmt::BasicFormatter::ParseUInt(const Char *&s) const { } template -inline const typename fmt::BasicFormatter::ArgInfo - &fmt::BasicFormatter::ParseArgIndex(const Char *&s) { +inline const typename fmt::BasicWriter::ArgInfo + &fmt::BasicWriter::FormatParser::ParseArgIndex(const Char *&s) { unsigned arg_index = 0; if (*s < '0' || *s > '9') { if (*s != '}' && *s != ':') @@ -407,32 +407,35 @@ inline const typename fmt::BasicFormatter::ArgInfo next_arg_index_ = -1; arg_index = ParseUInt(s); } - if (arg_index >= args_.size()) + if (arg_index >= num_args_) ReportError(s, "argument index is out of range in format"); return args_[arg_index]; } template -void fmt::BasicFormatter::CheckSign(const Char *&s, const ArgInfo &arg) { +void fmt::BasicWriter::FormatParser::CheckSign( + const Char *&s, const ArgInfo &arg) { char sign = static_cast(*s); if (arg.type > LAST_NUMERIC_TYPE) { ReportError(s, - Format("format specifier '{}' requires numeric argument") << sign); + fmt::Format("format specifier '{}' requires numeric argument") << sign); } if (arg.type == UINT || arg.type == ULONG || arg.type == ULONG_LONG) { ReportError(s, - Format("format specifier '{}' requires signed argument") << sign); + fmt::Format("format specifier '{}' requires signed argument") << sign); } ++s; } template -void fmt::BasicFormatter::DoFormat() { - const Char *start = format_; - format_ = 0; +void fmt::BasicWriter::FormatParser::Format( + BasicWriter &writer, BasicStringRef format, + std::size_t num_args, const ArgInfo *args) { + const Char *start = format.c_str(); + num_args_ = num_args; + args_ = args; next_arg_index_ = 0; const Char *s = start; - BasicWriter &writer = *writer_; while (*s) { Char c = *s++; if (c != '{' && c != '}') continue; @@ -697,18 +700,21 @@ template fmt::BasicWriter::CharPtr fmt::BasicWriter::PrepareFilledBuffer( unsigned size, const AlignSpec &spec, char sign); -template void fmt::BasicFormatter::ReportError( +template void fmt::BasicWriter::FormatParser::ReportError( const char *s, StringRef message) const; -template unsigned fmt::BasicFormatter::ParseUInt(const char *&s) const; +template unsigned fmt::BasicWriter::FormatParser::ParseUInt( + const char *&s) const; -template const fmt::BasicFormatter::ArgInfo - &fmt::BasicFormatter::ParseArgIndex(const char *&s); +template const fmt::BasicWriter::ArgInfo + &fmt::BasicWriter::FormatParser::ParseArgIndex(const char *&s); -template void fmt::BasicFormatter::CheckSign( +template void fmt::BasicWriter::FormatParser::CheckSign( const char *&s, const ArgInfo &arg); -template void fmt::BasicFormatter::DoFormat(); +template void fmt::BasicWriter::FormatParser::Format( + BasicWriter &writer, BasicStringRef format, + std::size_t num_args, const ArgInfo *args); // Explicit instantiations for wchar_t. @@ -726,19 +732,21 @@ template fmt::BasicWriter::CharPtr fmt::BasicWriter::PrepareFilledBuffer( unsigned size, const AlignSpec &spec, char sign); -template void fmt::BasicFormatter::ReportError( +template void fmt::BasicWriter::FormatParser::ReportError( const wchar_t *s, StringRef message) const; -template unsigned fmt::BasicFormatter::ParseUInt( +template unsigned fmt::BasicWriter::FormatParser::ParseUInt( const wchar_t *&s) const; -template const fmt::BasicFormatter::ArgInfo - &fmt::BasicFormatter::ParseArgIndex(const wchar_t *&s); +template const fmt::BasicWriter::ArgInfo + &fmt::BasicWriter::FormatParser::ParseArgIndex(const wchar_t *&s); -template void fmt::BasicFormatter::CheckSign( +template void fmt::BasicWriter::FormatParser::CheckSign( const wchar_t *&s, const ArgInfo &arg); -template void fmt::BasicFormatter::DoFormat(); +template void fmt::BasicWriter::FormatParser::Format( + BasicWriter &writer, BasicStringRef format, + std::size_t num_args, const ArgInfo *args); #if _MSC_VER # pragma warning(pop) diff --git a/format.h b/format.h index c22cb850..328b8ae6 100644 --- a/format.h +++ b/format.h @@ -87,6 +87,14 @@ namespace fmt { FMT_GCC_EXTENSION typedef long long LongLong; FMT_GCC_EXTENSION typedef unsigned long long ULongLong; +template +class BasicWriter; + +template +class BasicFormatter; + +struct FormatSpec; + namespace internal { enum { INLINE_BUFFER_SIZE = 500 }; @@ -324,6 +332,10 @@ void FormatDecimal(Char *buffer, UInt value, unsigned num_digits) { buffer[1] = internal::DIGITS[index + 1]; buffer[0] = internal::DIGITS[index]; } + +template +void FormatCustomArg( + BasicWriter &w, const void *arg, const FormatSpec &spec); } /** @@ -607,9 +619,6 @@ inline StrFormatSpec pad( return StrFormatSpec(str, width, fill); } -template -class BasicFormatter; - /** \rst This template provides operations for formatting and writing data into @@ -696,7 +705,176 @@ class BasicWriter { // Do not implement! void operator<<(typename internal::CharTraits::UnsupportedStrType); - public: + enum Type { + // Numeric types should go first. + INT, UINT, LONG, ULONG, LONG_LONG, ULONG_LONG, DOUBLE, LONG_DOUBLE, + LAST_NUMERIC_TYPE = LONG_DOUBLE, + CHAR, STRING, WSTRING, POINTER, CUSTOM + }; + + struct StringValue { + const Char *value; + std::size_t size; + }; + + typedef void (*FormatFunc)( + BasicWriter &w, const void *arg, const FormatSpec &spec); + + struct CustomValue { + const void *value; + FormatFunc format; + }; + + // Information about a format argument. It is a POD type to allow + // storage in internal::Array. + struct ArgInfo { + Type type; + union { + int int_value; + unsigned uint_value; + double double_value; + long long_value; + unsigned long ulong_value; + LongLong long_long_value; + ULongLong ulong_long_value; + long double long_double_value; + const void *pointer_value; + StringValue string; + CustomValue custom; + }; + }; + + // A wrapper around a format argument used to ensure that the formatting + // is performed before the argument is destroyed. It is private so that + // its objects are only created by automatic conversions and not by users. + // Example: + // + // Format("{}") << std::string("test"); + // + // Here an Arg object that wraps a temporary string is automatically + // created. It triggers formatting when destroyed which makes sure that + // the temporary string is still alive at the time of the formatting. + class Arg : public ArgInfo { + private: + // This method is private to disallow formatting of arbitrary pointers. + // If you want to output a pointer cast it to const void*. Do not implement! + template + Arg(const T *value); + + // This method is private to disallow formatting of arbitrary pointers. + // If you want to output a pointer cast it to void*. Do not implement! + template + Arg(T *value); + + public: + mutable BasicFormatter *formatter; + using ArgInfo::type; + + Arg(short value) : formatter(0) { type = INT; this->int_value = value; } + Arg(unsigned short value) + : formatter(0) { type = UINT; this->int_value = value; } + Arg(int value) : formatter(0) { type = INT; this->int_value = value; } + Arg(unsigned value) + : formatter(0) { type = UINT; this->uint_value = value; } + Arg(long value) : formatter(0) { type = LONG; this->long_value = value; } + Arg(unsigned long value) + : formatter(0) { type = ULONG; this->ulong_value = value; } + Arg(LongLong value) + : formatter(0) { type = LONG_LONG; this->long_long_value = value; } + Arg(ULongLong value) + : formatter(0) { type = ULONG_LONG; this->ulong_long_value = value; } + Arg(float value) + : formatter(0) { type = DOUBLE; this->double_value = value; } + Arg(double value) + : formatter(0) { type = DOUBLE; this->double_value = value; } + Arg(long double value) + : formatter(0) { type = LONG_DOUBLE; this->long_double_value = value; } + Arg(char value) : formatter(0) { type = CHAR; this->int_value = value; } + Arg(wchar_t value) : formatter(0) { + type = CHAR; + this->int_value = internal::CharTraits::ConvertChar(value); + } + + Arg(const Char *value) : formatter(0) { + type = STRING; + this->string.value = value; + this->string.size = 0; + } + + Arg(Char *value) : formatter(0) { + type = STRING; + this->string.value = value; + this->string.size = 0; + } + + Arg(const void *value) : formatter(0) { + type = POINTER; + this->pointer_value = value; + } + + Arg(void *value) : formatter(0) { + type = POINTER; + this->pointer_value = value; + } + + Arg(const std::basic_string &value) : formatter(0) { + type = STRING; + this->string.value = value.c_str(); + this->string.size = value.size(); + } + + Arg(BasicStringRef value) : formatter(0) { + type = STRING; + this->string.value = value.c_str(); + this->string.size = value.size(); + } + + template + Arg(const T &value) : formatter(0) { + type = CUSTOM; + this->custom.value = &value; + this->custom.format = &internal::FormatCustomArg; + } + + ~Arg() FMT_NOEXCEPT(false) { + // Format is called here to make sure that a referred object is + // still alive, for example: + // + // Print("{}") << std::string("test"); + // + // Here an Arg object refers to a temporary std::string which is + // destroyed at the end of the statement. Since the string object is + // constructed before the Arg object, it will be destroyed after, + // so it will be alive in the Arg's destructor where Format is called. + // Note that the string object will not necessarily be alive when + // the destructor of BasicFormatter is called. + if (formatter) + formatter->CompleteFormatting(); + } + }; + + class FormatParser { + private: + std::size_t num_args_; + const ArgInfo *args_; + int num_open_braces_; + int next_arg_index_; + + void ReportError(const Char *s, StringRef message) const; + + unsigned ParseUInt(const Char *&s) const; + + // Parses argument index and returns an argument with this index. + const ArgInfo &ParseArgIndex(const Char *&s); + + void CheckSign(const Char *&s, const ArgInfo &arg); + + public: + void Format(BasicWriter &writer, + BasicStringRef format, std::size_t num_args, const ArgInfo *args); + }; + + public: /** Returns the number of characters written to the output buffer. */ @@ -752,6 +930,20 @@ class BasicWriter { */ BasicFormatter Format(StringRef format); + // TODO: ArgInfo should be made public for this to be usable + inline void VFormat(BasicStringRef format, + std::size_t num_args, const ArgInfo *args) { + FormatParser().Format(*this, format, num_args, args); + } + +#if FMT_USE_VARIADIC_TEMPLATES + template + void Format(BasicStringRef format, const Args & ... args) { + Arg arg_array[] = {args...}; + VFormat(format, sizeof...(Args), arg_array); + } +#endif + BasicWriter &operator<<(int value) { return *this << IntFormatSpec(value); } @@ -981,162 +1173,15 @@ class BasicFormatter { private: BasicWriter *writer_; - enum Type { - // Numeric types should go first. - INT, UINT, LONG, ULONG, LONG_LONG, ULONG_LONG, DOUBLE, LONG_DOUBLE, - LAST_NUMERIC_TYPE = LONG_DOUBLE, - CHAR, STRING, WSTRING, POINTER, CUSTOM - }; - - typedef void (*FormatFunc)( - BasicWriter &w, const void *arg, const FormatSpec &spec); - - struct StringValue { - const Char *value; - std::size_t size; - }; - - struct CustomValue { - const void *value; - FormatFunc format; - }; - - // Information about a format argument. It is a POD type to allow - // storage in internal::Array. - struct ArgInfo { - Type type; - union { - int int_value; - unsigned uint_value; - double double_value; - long long_value; - unsigned long ulong_value; - LongLong long_long_value; - ULongLong ulong_long_value; - long double long_double_value; - const void *pointer_value; - StringValue string; - CustomValue custom; - }; - }; - - // A wrapper around a format argument used to ensure that the formatting - // is performed before the argument is destroyed. It is private so that - // its objects are only created by automatic conversions and not by users. - // Example: - // - // Format("{}") << std::string("test"); - // - // Here an Arg object that wraps a temporary string is automatically - // created. It triggers formatting when destroyed which makes sure that - // the temporary string is still alive at the time of the formatting. - class Arg : public ArgInfo { - private: - // This method is private to disallow formatting of arbitrary pointers. - // If you want to output a pointer cast it to const void*. Do not implement! - template - Arg(const T *value); - - // This method is private to disallow formatting of arbitrary pointers. - // If you want to output a pointer cast it to void*. Do not implement! - template - Arg(T *value); - - public: - mutable BasicFormatter *formatter; - using ArgInfo::type; - - Arg(short value) : formatter(0) { type = INT; this->int_value = value; } - Arg(unsigned short value) - : formatter(0) { type = UINT; this->int_value = value; } - Arg(int value) : formatter(0) { type = INT; this->int_value = value; } - Arg(unsigned value) - : formatter(0) { type = UINT; this->uint_value = value; } - Arg(long value) : formatter(0) { type = LONG; this->long_value = value; } - Arg(unsigned long value) - : formatter(0) { type = ULONG; this->ulong_value = value; } - Arg(LongLong value) - : formatter(0) { type = LONG_LONG; this->long_long_value = value; } - Arg(ULongLong value) - : formatter(0) { type = ULONG_LONG; this->ulong_long_value = value; } - Arg(float value) - : formatter(0) { type = DOUBLE; this->double_value = value; } - Arg(double value) - : formatter(0) { type = DOUBLE; this->double_value = value; } - Arg(long double value) - : formatter(0) { type = LONG_DOUBLE; this->long_double_value = value; } - Arg(char value) : formatter(0) { type = CHAR; this->int_value = value; } - Arg(wchar_t value) : formatter(0) { - type = CHAR; - this->int_value = internal::CharTraits::ConvertChar(value); - } - - Arg(const Char *value) : formatter(0) { - type = STRING; - this->string.value = value; - this->string.size = 0; - } - - Arg(Char *value) : formatter(0) { - type = STRING; - this->string.value = value; - this->string.size = 0; - } - - Arg(const void *value) : formatter(0) { - type = POINTER; - this->pointer_value = value; - } - - Arg(void *value) : formatter(0) { - type = POINTER; - this->pointer_value = value; - } - - Arg(const std::basic_string &value) : formatter(0) { - type = STRING; - this->string.value = value.c_str(); - this->string.size = value.size(); - } - - Arg(BasicStringRef value) : formatter(0) { - type = STRING; - this->string.value = value.c_str(); - this->string.size = value.size(); - } - - template - Arg(const T &value) : formatter(0) { - type = CUSTOM; - this->custom.value = &value; - this->custom.format = &internal::FormatCustomArg; - } - - ~Arg() FMT_NOEXCEPT(false) { - // Format is called here to make sure that a referred object is - // still alive, for example: - // - // Print("{}") << std::string("test"); - // - // Here an Arg object refers to a temporary std::string which is - // destroyed at the end of the statement. Since the string object is - // constructed before the Arg object, it will be destroyed after, - // so it will be alive in the Arg's destructor where Format is called. - // Note that the string object will not necessarily be alive when - // the destructor of BasicFormatter is called. - if (formatter) - formatter->CompleteFormatting(); - } - }; + typedef typename BasicWriter::ArgInfo ArgInfo; enum { NUM_INLINE_ARGS = 10 }; internal::Array args_; // Format arguments. const Char *format_; // Format string. - int num_open_braces_; - int next_arg_index_; friend class internal::FormatterProxy; + friend class BasicWriter::Arg; // TODO: remove (currently used for CompleteFormatting to be accessible) // Forbid copying from a temporary as in the following example: // fmt::Formatter<> f = Format("test"); // not allowed @@ -1146,19 +1191,6 @@ class BasicFormatter { BasicFormatter(const BasicFormatter &); BasicFormatter& operator=(const BasicFormatter &); - void ReportError(const Char *s, StringRef message) const; - - unsigned ParseUInt(const Char *&s) const; - - // Parses argument index and returns an argument with this index. - const ArgInfo &ParseArgIndex(const Char *&s); - - void CheckSign(const Char *&s, const ArgInfo &arg); - - // Parses the format string and performs the actual formatting, - // writing the output to writer_. - void DoFormat(); - struct Proxy { BasicWriter *writer; const Char *format; @@ -1175,7 +1207,9 @@ class BasicFormatter { void CompleteFormatting() { if (!format_) return; - DoFormat(); + const Char *format = format_; + format_ = 0; + writer_->VFormat(format, args_.size(), &args_[0]); } public: @@ -1184,21 +1218,6 @@ class BasicFormatter { BasicFormatter(BasicWriter &w, const Char *format = 0) : writer_(&w), format_(format) {} -#if FMT_USE_VARIADIC_TEMPLATES - // Constructs a formatter with variable number of arguments. - template - BasicFormatter( - BasicWriter &w, const Char *format, const Args & ... args) - : writer_(&w), format_(format) { - std::size_t num_args = sizeof...(Args); - Arg arg_array[] = {args...}; - // TODO: use array directly instead of copying - args_.reserve(num_args); - for (std::size_t i = 0; i < num_args; ++i) - args_.push_back(arg_array[i]); - } -#endif - // Performs formatting if the format string is non-null. The format string // can be null if its ownership has been transferred to another formatter. ~BasicFormatter() { @@ -1210,7 +1229,7 @@ class BasicFormatter { } // Feeds an argument to a formatter. - BasicFormatter &operator<<(const Arg &arg) { + BasicFormatter &operator<<(const typename BasicWriter::Arg &arg) { arg.formatter = this; args_.push_back(arg); return *this; @@ -1526,15 +1545,15 @@ inline Formatter PrintColored(Color c, StringRef format) { template inline std::string Format(const StringRef &format, const Args & ... args) { Writer w; - BasicFormatter f(w, format.c_str(), args...); - return fmt::str(f); + w.Format(format, args...); + return fmt::str(w); } template inline std::wstring Format(const WStringRef &format, const Args & ... args) { WWriter w; - BasicFormatter f(w, format.c_str(), args...); - return fmt::str(f); + w.Format(format, args...); + return fmt::str(w); } #endif // FMT_USE_VARIADIC_TEMPLATES