Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
F
fmt
Project overview
Project overview
Details
Activity
Releases
Repository
Repository
Files
Commits
Branches
Tags
Contributors
Graph
Compare
Issues
0
Issues
0
List
Boards
Labels
Milestones
Merge Requests
0
Merge Requests
0
CI / CD
CI / CD
Pipelines
Jobs
Schedules
Analytics
Analytics
CI / CD
Repository
Value Stream
Wiki
Wiki
Snippets
Snippets
Members
Members
Collapse sidebar
Close sidebar
Activity
Graph
Create a new issue
Jobs
Commits
Issue Boards
Open sidebar
Libraries
fmt
Commits
688de77b
Commit
688de77b
authored
Feb 05, 2013
by
Victor Zverovich
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Parameterize Formatter on character type and rename it to BasicFormatter.
parent
03dccc3c
Changes
3
Hide whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
412 additions
and
410 deletions
+412
-410
format.cc
format.cc
+0
-322
format.h
format.h
+405
-74
format_test.cc
format_test.cc
+7
-14
No files found.
format.cc
View file @
688de77b
...
@@ -33,47 +33,18 @@
...
@@ -33,47 +33,18 @@
#include <math.h>
#include <math.h>
// Wrap signbit because when compiled in C++11 mode signbit is no longer a
// macro but a function defined in namespace std and the macro is undefined.
#ifndef _MSC_VER
inline
int
SignBit
(
double
value
)
{
return
signbit
(
value
);
}
#endif
#include "format.h"
#include "format.h"
#include <cassert>
#include <cctype>
#include <cctype>
#include <climits>
#include <cstring>
#include <cstring>
#include <algorithm>
#include <algorithm>
using
std
::
size_t
;
using
fmt
::
IntFormatter
;
using
fmt
::
Formatter
;
using
fmt
::
AlignSpec
;
using
fmt
::
FormatSpec
;
using
fmt
::
WidthSpec
;
using
fmt
::
StringRef
;
#if _MSC_VER
#if _MSC_VER
# undef snprintf
# undef snprintf
# define snprintf _snprintf
# define snprintf _snprintf
# define isinf(x) (!_finite(x))
# define isinf(x) (!_finite(x))
#endif
#endif
namespace
{
#ifdef _MSC_VER
int
SignBit
(
double
value
)
{
if
(
value
<
0
)
return
1
;
if
(
value
==
value
)
return
0
;
int
dec
=
0
,
sign
=
0
;
_ecvt
(
value
,
0
,
&
dec
,
&
sign
);
return
sign
;
}
#endif
}
const
char
fmt
::
internal
::
DIGITS
[]
=
const
char
fmt
::
internal
::
DIGITS
[]
=
"0001020304050607080910111213141516171819"
"0001020304050607080910111213141516171819"
"2021222324252627282930313233343536373839"
"2021222324252627282930313233343536373839"
...
@@ -90,296 +61,3 @@ void fmt::internal::ReportUnknownType(char code, const char *type) {
...
@@ -90,296 +61,3 @@ void fmt::internal::ReportUnknownType(char code, const char *type) {
fmt
::
str
(
fmt
::
Format
(
"unknown format code '
\\
x{:02x}' for {}"
)
fmt
::
str
(
fmt
::
Format
(
"unknown format code '
\\
x{:02x}' for {}"
)
<<
static_cast
<
unsigned
>
(
code
)
<<
type
));
<<
static_cast
<
unsigned
>
(
code
)
<<
type
));
}
}
// Throws Exception(message) if format contains '}', otherwise throws
// FormatError reporting unmatched '{'. The idea is that unmatched '{'
// should override other errors.
void
Formatter
::
ReportError
(
const
char
*
s
,
StringRef
message
)
const
{
for
(
int
num_open_braces
=
num_open_braces_
;
*
s
;
++
s
)
{
if
(
*
s
==
'{'
)
{
++
num_open_braces
;
}
else
if
(
*
s
==
'}'
)
{
if
(
--
num_open_braces
==
0
)
throw
fmt
::
FormatError
(
message
);
}
}
throw
fmt
::
FormatError
(
"unmatched '{' in format"
);
}
// 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.
unsigned
Formatter
::
ParseUInt
(
const
char
*&
s
)
const
{
assert
(
'0'
<=
*
s
&&
*
s
<=
'9'
);
unsigned
value
=
0
;
do
{
unsigned
new_value
=
value
*
10
+
(
*
s
++
-
'0'
);
if
(
new_value
<
value
)
// Check if value wrapped around.
ReportError
(
s
,
"number is too big in format"
);
value
=
new_value
;
}
while
(
'0'
<=
*
s
&&
*
s
<=
'9'
);
return
value
;
}
inline
const
Formatter
::
Arg
&
Formatter
::
ParseArgIndex
(
const
char
*&
s
)
{
unsigned
arg_index
=
0
;
if
(
*
s
<
'0'
||
*
s
>
'9'
)
{
if
(
*
s
!=
'}'
&&
*
s
!=
':'
)
ReportError
(
s
,
"invalid argument index in format string"
);
if
(
next_arg_index_
<
0
)
{
ReportError
(
s
,
"cannot switch from manual to automatic argument indexing"
);
}
arg_index
=
next_arg_index_
++
;
}
else
{
if
(
next_arg_index_
>
0
)
{
ReportError
(
s
,
"cannot switch from automatic to manual argument indexing"
);
}
next_arg_index_
=
-
1
;
arg_index
=
ParseUInt
(
s
);
if
(
arg_index
>=
args_
.
size
())
ReportError
(
s
,
"argument index is out of range in format"
);
}
return
*
args_
[
arg_index
];
}
void
Formatter
::
CheckSign
(
const
char
*&
s
,
const
Arg
&
arg
)
{
if
(
arg
.
type
>
LAST_NUMERIC_TYPE
)
{
ReportError
(
s
,
Format
(
"format specifier '{0}' requires numeric argument"
)
<<
*
s
);
}
if
(
arg
.
type
==
UINT
||
arg
.
type
==
ULONG
)
{
ReportError
(
s
,
Format
(
"format specifier '{0}' requires signed argument"
)
<<
*
s
);
}
++
s
;
}
void
Formatter
::
DoFormat
()
{
const
char
*
start
=
format_
;
format_
=
0
;
next_arg_index_
=
0
;
const
char
*
s
=
start
;
while
(
*
s
)
{
char
c
=
*
s
++
;
if
(
c
!=
'{'
&&
c
!=
'}'
)
continue
;
if
(
*
s
==
c
)
{
buffer_
.
append
(
start
,
s
);
start
=
++
s
;
continue
;
}
if
(
c
==
'}'
)
throw
FormatError
(
"unmatched '}' in format"
);
num_open_braces_
=
1
;
buffer_
.
append
(
start
,
s
-
1
);
const
Arg
&
arg
=
ParseArgIndex
(
s
);
FormatSpec
spec
;
int
precision
=
-
1
;
if
(
*
s
==
':'
)
{
++
s
;
// Parse fill and alignment.
if
(
char
c
=
*
s
)
{
const
char
*
p
=
s
+
1
;
spec
.
align_
=
ALIGN_DEFAULT
;
do
{
switch
(
*
p
)
{
case
'<'
:
spec
.
align_
=
ALIGN_LEFT
;
break
;
case
'>'
:
spec
.
align_
=
ALIGN_RIGHT
;
break
;
case
'='
:
spec
.
align_
=
ALIGN_NUMERIC
;
break
;
case
'^'
:
spec
.
align_
=
ALIGN_CENTER
;
break
;
}
if
(
spec
.
align_
!=
ALIGN_DEFAULT
)
{
if
(
p
!=
s
)
{
if
(
c
==
'}'
)
break
;
if
(
c
==
'{'
)
ReportError
(
s
,
"invalid fill character '{'"
);
s
+=
2
;
spec
.
fill_
=
c
;
}
else
++
s
;
if
(
spec
.
align_
==
ALIGN_NUMERIC
&&
arg
.
type
>
LAST_NUMERIC_TYPE
)
ReportError
(
s
,
"format specifier '=' requires numeric argument"
);
break
;
}
}
while
(
--
p
>=
s
);
}
// Parse sign.
switch
(
*
s
)
{
case
'+'
:
CheckSign
(
s
,
arg
);
spec
.
flags_
|=
SIGN_FLAG
|
PLUS_FLAG
;
break
;
case
'-'
:
CheckSign
(
s
,
arg
);
break
;
case
' '
:
CheckSign
(
s
,
arg
);
spec
.
flags_
|=
SIGN_FLAG
;
break
;
}
if
(
*
s
==
'#'
)
{
if
(
arg
.
type
>
LAST_NUMERIC_TYPE
)
ReportError
(
s
,
"format specifier '#' requires numeric argument"
);
spec
.
flags_
|=
HASH_FLAG
;
++
s
;
}
// Parse width and zero flag.
if
(
'0'
<=
*
s
&&
*
s
<=
'9'
)
{
if
(
*
s
==
'0'
)
{
if
(
arg
.
type
>
LAST_NUMERIC_TYPE
)
ReportError
(
s
,
"format specifier '0' requires numeric argument"
);
spec
.
align_
=
ALIGN_NUMERIC
;
spec
.
fill_
=
'0'
;
}
// Zero may be parsed again as a part of the width, but it is simpler
// and more efficient than checking if the next char is a digit.
unsigned
value
=
ParseUInt
(
s
);
if
(
value
>
INT_MAX
)
ReportError
(
s
,
"number is too big in format"
);
spec
.
width_
=
value
;
}
// Parse precision.
if
(
*
s
==
'.'
)
{
++
s
;
precision
=
0
;
if
(
'0'
<=
*
s
&&
*
s
<=
'9'
)
{
unsigned
value
=
ParseUInt
(
s
);
if
(
value
>
INT_MAX
)
ReportError
(
s
,
"number is too big in format"
);
precision
=
value
;
}
else
if
(
*
s
==
'{'
)
{
++
s
;
++
num_open_braces_
;
const
Arg
&
precision_arg
=
ParseArgIndex
(
s
);
unsigned
long
value
=
0
;
switch
(
precision_arg
.
type
)
{
case
INT
:
if
(
precision_arg
.
int_value
<
0
)
ReportError
(
s
,
"negative precision in format"
);
value
=
precision_arg
.
int_value
;
break
;
case
UINT
:
value
=
precision_arg
.
uint_value
;
break
;
case
LONG
:
if
(
precision_arg
.
long_value
<
0
)
ReportError
(
s
,
"negative precision in format"
);
value
=
precision_arg
.
long_value
;
break
;
case
ULONG
:
value
=
precision_arg
.
ulong_value
;
break
;
default:
ReportError
(
s
,
"precision is not integer"
);
}
if
(
value
>
INT_MAX
)
ReportError
(
s
,
"number is too big in format"
);
precision
=
value
;
if
(
*
s
++
!=
'}'
)
throw
FormatError
(
"unmatched '{' in format"
);
--
num_open_braces_
;
}
else
{
ReportError
(
s
,
"missing precision in format"
);
}
if
(
arg
.
type
!=
DOUBLE
&&
arg
.
type
!=
LONG_DOUBLE
)
{
ReportError
(
s
,
"precision specifier requires floating-point argument"
);
}
}
// Parse type.
if
(
*
s
!=
'}'
&&
*
s
)
spec
.
type_
=
*
s
++
;
}
if
(
*
s
++
!=
'}'
)
throw
FormatError
(
"unmatched '{' in format"
);
start
=
s
;
// Format argument.
switch
(
arg
.
type
)
{
case
INT
:
FormatInt
(
arg
.
int_value
,
spec
);
break
;
case
UINT
:
FormatInt
(
arg
.
uint_value
,
spec
);
break
;
case
LONG
:
FormatInt
(
arg
.
long_value
,
spec
);
break
;
case
ULONG
:
FormatInt
(
arg
.
ulong_value
,
spec
);
break
;
case
DOUBLE
:
FormatDouble
(
arg
.
double_value
,
spec
,
precision
);
break
;
case
LONG_DOUBLE
:
FormatDouble
(
arg
.
long_double_value
,
spec
,
precision
);
break
;
case
CHAR
:
{
if
(
spec
.
type_
&&
spec
.
type_
!=
'c'
)
internal
::
ReportUnknownType
(
spec
.
type_
,
"char"
);
char
*
out
=
0
;
if
(
spec
.
width_
>
1
)
{
out
=
GrowBuffer
(
spec
.
width_
);
if
(
spec
.
align_
==
ALIGN_RIGHT
)
{
std
::
fill_n
(
out
,
spec
.
width_
-
1
,
spec
.
fill_
);
out
+=
spec
.
width_
-
1
;
}
else
if
(
spec
.
align_
==
ALIGN_CENTER
)
{
out
=
FillPadding
(
out
,
spec
.
width_
,
1
,
spec
.
fill_
);
}
else
{
std
::
fill_n
(
out
+
1
,
spec
.
width_
-
1
,
spec
.
fill_
);
}
}
else
{
out
=
GrowBuffer
(
1
);
}
*
out
=
arg
.
int_value
;
break
;
}
case
STRING
:
{
if
(
spec
.
type_
&&
spec
.
type_
!=
's'
)
internal
::
ReportUnknownType
(
spec
.
type_
,
"string"
);
const
char
*
str
=
arg
.
string
.
value
;
size_t
size
=
arg
.
string
.
size
;
if
(
size
==
0
)
{
if
(
!
str
)
throw
FormatError
(
"string pointer is null"
);
if
(
*
str
)
size
=
std
::
strlen
(
str
);
}
FormatString
(
str
,
size
,
spec
);
break
;
}
case
POINTER
:
if
(
spec
.
type_
&&
spec
.
type_
!=
'p'
)
internal
::
ReportUnknownType
(
spec
.
type_
,
"pointer"
);
spec
.
flags_
=
HASH_FLAG
;
spec
.
type_
=
'x'
;
FormatInt
(
reinterpret_cast
<
uintptr_t
>
(
arg
.
pointer_value
),
spec
);
break
;
case
CUSTOM
:
if
(
spec
.
type_
)
internal
::
ReportUnknownType
(
spec
.
type_
,
"object"
);
(
this
->*
arg
.
custom
.
format
)(
arg
.
custom
.
value
,
spec
);
break
;
default:
assert
(
false
);
break
;
}
}
buffer_
.
append
(
start
,
s
);
}
format.h
View file @
688de77b
...
@@ -30,6 +30,9 @@
...
@@ -30,6 +30,9 @@
#include <stdint.h>
#include <stdint.h>
#include <cassert>
#include <climits>
#include <cmath>
#include <cstddef>
#include <cstddef>
#include <cstdio>
#include <cstdio>
#include <cstring>
#include <cstring>
...
@@ -146,7 +149,44 @@ extern const char DIGITS[];
...
@@ -146,7 +149,44 @@ extern const char DIGITS[];
void
ReportUnknownType
(
char
code
,
const
char
*
type
);
void
ReportUnknownType
(
char
code
,
const
char
*
type
);
// Returns the number of decimal digits in n. Trailing zeros are not counted
// except for n == 0 in which case CountDigits returns 1.
inline
unsigned
CountDigits
(
uint64_t
n
)
{
unsigned
count
=
1
;
for
(;;)
{
// Integer division is slow so do it for a group of four digits instead
// of for every digit. The idea comes from the talk by Alexandrescu
// "Three Optimization Tips for C++". See speed-test for a comparison.
if
(
n
<
10
)
return
count
;
if
(
n
<
100
)
return
count
+
1
;
if
(
n
<
1000
)
return
count
+
2
;
if
(
n
<
10000
)
return
count
+
3
;
n
/=
10000u
;
count
+=
4
;
}
}
#ifndef _MSC_VER
inline
int
SignBit
(
double
value
)
{
// When compiled in C++11 mode signbit is no longer a macro but a function
// defined in namespace std and the macro is undefined.
using
namespace
std
;
return
signbit
(
value
);
}
#else
inline
int
SignBit
(
double
value
)
{
if
(
value
<
0
)
return
1
;
if
(
value
==
value
)
return
0
;
int
dec
=
0
,
sign
=
0
;
_ecvt
(
value
,
0
,
&
dec
,
&
sign
);
return
sign
;
}
#endif
template
<
typename
Char
>
class
ArgInserter
;
class
ArgInserter
;
template
<
typename
Char
>
class
FormatterProxy
;
class
FormatterProxy
;
}
}
...
@@ -354,23 +394,6 @@ DEFINE_INT_FORMATTERS(unsigned long)
...
@@ -354,23 +394,6 @@ DEFINE_INT_FORMATTERS(unsigned long)
template
<
typename
Char
>
template
<
typename
Char
>
class
BasicWriter
{
class
BasicWriter
{
private:
private:
// Returns the number of decimal digits in n. Trailing zeros are not counted
// except for n == 0 in which case CountDigits returns 1.
static
unsigned
CountDigits
(
uint64_t
n
)
{
unsigned
count
=
1
;
for
(;;)
{
// Integer division is slow so do it for a group of four digits instead
// of for every digit. The idea comes from the talk by Alexandrescu
// "Three Optimization Tips for C++". See speed-test for a comparison.
if
(
n
<
10
)
return
count
;
if
(
n
<
100
)
return
count
+
1
;
if
(
n
<
1000
)
return
count
+
2
;
if
(
n
<
10000
)
return
count
+
3
;
n
/=
10000u
;
count
+=
4
;
}
}
static
void
FormatDecimal
(
Char
*
buffer
,
uint64_t
value
,
unsigned
num_digits
);
static
void
FormatDecimal
(
Char
*
buffer
,
uint64_t
value
,
unsigned
num_digits
);
protected:
protected:
...
@@ -477,7 +500,7 @@ Char *BasicWriter<Char>::FillPadding(Char *buffer,
...
@@ -477,7 +500,7 @@ Char *BasicWriter<Char>::FillPadding(Char *buffer,
std
::
size_t
left_padding
=
padding
/
2
;
std
::
size_t
left_padding
=
padding
/
2
;
std
::
fill_n
(
buffer
,
left_padding
,
fill
);
std
::
fill_n
(
buffer
,
left_padding
,
fill
);
buffer
+=
left_padding
;
buffer
+=
left_padding
;
c
har
*
content
=
buffer
;
C
har
*
content
=
buffer
;
std
::
fill_n
(
buffer
+
content_size
,
padding
-
left_padding
,
fill
);
std
::
fill_n
(
buffer
+
content_size
,
padding
-
left_padding
,
fill
);
return
content
;
return
content
;
}
}
...
@@ -510,7 +533,7 @@ Char *BasicWriter<Char>::PrepareFilledBuffer(
...
@@ -510,7 +533,7 @@ Char *BasicWriter<Char>::PrepareFilledBuffer(
unsigned
size
,
const
AlignSpec
&
spec
,
char
sign
)
{
unsigned
size
,
const
AlignSpec
&
spec
,
char
sign
)
{
unsigned
width
=
spec
.
width
();
unsigned
width
=
spec
.
width
();
if
(
width
<=
size
)
{
if
(
width
<=
size
)
{
c
har
*
p
=
GrowBuffer
(
size
);
C
har
*
p
=
GrowBuffer
(
size
);
*
p
=
sign
;
*
p
=
sign
;
return
p
+
size
-
1
;
return
p
+
size
-
1
;
}
}
...
@@ -570,7 +593,7 @@ void BasicWriter<Char>::FormatDouble(
...
@@ -570,7 +593,7 @@ void BasicWriter<Char>::FormatDouble(
char
sign
=
0
;
char
sign
=
0
;
// Use SignBit instead of value < 0 because the latter is always
// Use SignBit instead of value < 0 because the latter is always
// false for NaN.
// false for NaN.
if
(
SignBit
(
value
))
{
if
(
internal
::
SignBit
(
value
))
{
sign
=
'-'
;
sign
=
'-'
;
value
=
-
value
;
value
=
-
value
;
}
else
if
(
spec
.
sign_flag
())
{
}
else
if
(
spec
.
sign_flag
())
{
...
@@ -607,7 +630,7 @@ void BasicWriter<Char>::FormatDouble(
...
@@ -607,7 +630,7 @@ void BasicWriter<Char>::FormatDouble(
return
;
return
;
}
}
size_t
offset
=
buffer_
.
size
();
s
td
::
s
ize_t
offset
=
buffer_
.
size
();
unsigned
width
=
spec
.
width
();
unsigned
width
=
spec
.
width
();
if
(
sign
)
{
if
(
sign
)
{
buffer_
.
reserve
(
buffer_
.
size
()
+
std
::
max
(
width
,
1u
));
buffer_
.
reserve
(
buffer_
.
size
()
+
std
::
max
(
width
,
1u
));
...
@@ -643,9 +666,9 @@ void BasicWriter<Char>::FormatDouble(
...
@@ -643,9 +666,9 @@ void BasicWriter<Char>::FormatDouble(
// Format using snprintf.
// Format using snprintf.
for
(;;)
{
for
(;;)
{
size_t
size
=
buffer_
.
capacity
()
-
offset
;
s
td
::
s
ize_t
size
=
buffer_
.
capacity
()
-
offset
;
int
n
=
0
;
int
n
=
0
;
c
har
*
start
=
&
buffer_
[
offset
];
C
har
*
start
=
&
buffer_
[
offset
];
if
(
width_for_sprintf
==
0
)
{
if
(
width_for_sprintf
==
0
)
{
n
=
precision
<
0
?
n
=
precision
<
0
?
snprintf
(
start
,
size
,
format
,
value
)
:
snprintf
(
start
,
size
,
format
,
value
)
:
...
@@ -726,7 +749,7 @@ BasicWriter<Char> &BasicWriter<Char>::operator<<(
...
@@ -726,7 +749,7 @@ BasicWriter<Char> &BasicWriter<Char>::operator<<(
}
}
switch
(
f
.
type
())
{
switch
(
f
.
type
())
{
case
0
:
case
'd'
:
{
case
0
:
case
'd'
:
{
unsigned
num_digits
=
BasicWriter
::
CountDigits
(
abs_value
);
unsigned
num_digits
=
internal
::
CountDigits
(
abs_value
);
Char
*
p
=
PrepareFilledBuffer
(
size
+
num_digits
,
f
,
sign
)
-
num_digits
+
1
;
Char
*
p
=
PrepareFilledBuffer
(
size
+
num_digits
,
f
,
sign
)
-
num_digits
+
1
;
BasicWriter
::
FormatDecimal
(
p
,
abs_value
,
num_digits
);
BasicWriter
::
FormatDecimal
(
p
,
abs_value
,
num_digits
);
break
;
break
;
...
@@ -787,7 +810,7 @@ void Format(BasicWriter<Char> &f, const FormatSpec &spec, const T &value) {
...
@@ -787,7 +810,7 @@ void Format(BasicWriter<Char> &f, const FormatSpec &spec, const T &value) {
/**
/**
\rst
\rst
The :cpp:class:`fmt::
Formatter` class
provides string formatting
The :cpp:class:`fmt::
BasicFormatter` template
provides string formatting
functionality similar to Python's `str.format
functionality similar to Python's `str.format
<http://docs.python.org/3/library/stdtypes.html#str.format>`__.
<http://docs.python.org/3/library/stdtypes.html#str.format>`__.
The output is stored in a memory buffer that grows dynamically.
The output is stored in a memory buffer that grows dynamically.
...
@@ -809,7 +832,8 @@ void Format(BasicWriter<Char> &f, const FormatSpec &spec, const T &value) {
...
@@ -809,7 +832,8 @@ void Format(BasicWriter<Char> &f, const FormatSpec &spec, const T &value) {
The buffer can be accessed using :meth:`data` or :meth:`c_str`.
The buffer can be accessed using :meth:`data` or :meth:`c_str`.
\endrst
\endrst
*/
*/
class
Formatter
:
public
BasicWriter
<
char
>
{
template
<
typename
Char
>
class
BasicFormatter
:
public
BasicWriter
<
Char
>
{
private:
private:
enum
Type
{
enum
Type
{
// Numeric types should go first.
// Numeric types should go first.
...
@@ -818,7 +842,7 @@ class Formatter : public BasicWriter<char> {
...
@@ -818,7 +842,7 @@ class Formatter : public BasicWriter<char> {
CHAR
,
STRING
,
WSTRING
,
POINTER
,
CUSTOM
CHAR
,
STRING
,
WSTRING
,
POINTER
,
CUSTOM
};
};
typedef
void
(
Formatter
::*
FormatFunc
)(
typedef
void
(
Basic
Formatter
::*
FormatFunc
)(
const
void
*
arg
,
const
FormatSpec
&
spec
);
const
void
*
arg
,
const
FormatSpec
&
spec
);
// A format argument.
// A format argument.
...
@@ -858,7 +882,7 @@ class Formatter : public BasicWriter<char> {
...
@@ -858,7 +882,7 @@ class Formatter : public BasicWriter<char> {
FormatFunc
format
;
FormatFunc
format
;
}
custom
;
}
custom
;
};
};
mutable
Formatter
*
formatter
;
mutable
Basic
Formatter
*
formatter
;
Arg
(
int
value
)
:
type
(
INT
),
int_value
(
value
),
formatter
(
0
)
{}
Arg
(
int
value
)
:
type
(
INT
),
int_value
(
value
),
formatter
(
0
)
{}
Arg
(
unsigned
value
)
:
type
(
UINT
),
uint_value
(
value
),
formatter
(
0
)
{}
Arg
(
unsigned
value
)
:
type
(
UINT
),
uint_value
(
value
),
formatter
(
0
)
{}
...
@@ -892,7 +916,7 @@ class Formatter : public BasicWriter<char> {
...
@@ -892,7 +916,7 @@ class Formatter : public BasicWriter<char> {
template
<
typename
T
>
template
<
typename
T
>
Arg
(
const
T
&
value
)
:
type
(
CUSTOM
),
formatter
(
0
)
{
Arg
(
const
T
&
value
)
:
type
(
CUSTOM
),
formatter
(
0
)
{
custom
.
value
=
&
value
;
custom
.
value
=
&
value
;
custom
.
format
=
&
Formatter
::
FormatCustomArg
<
T
>
;
custom
.
format
=
&
Basic
Formatter
::
FormatCustomArg
<
T
>
;
}
}
~
Arg
()
{
~
Arg
()
{
...
@@ -914,12 +938,12 @@ class Formatter : public BasicWriter<char> {
...
@@ -914,12 +938,12 @@ class Formatter : public BasicWriter<char> {
enum
{
NUM_INLINE_ARGS
=
10
};
enum
{
NUM_INLINE_ARGS
=
10
};
internal
::
Array
<
const
Arg
*
,
NUM_INLINE_ARGS
>
args_
;
// Format arguments.
internal
::
Array
<
const
Arg
*
,
NUM_INLINE_ARGS
>
args_
;
// Format arguments.
const
c
har
*
format_
;
// Format string.
const
C
har
*
format_
;
// Format string.
int
num_open_braces_
;
int
num_open_braces_
;
int
next_arg_index_
;
int
next_arg_index_
;
friend
class
internal
::
ArgInserter
;
friend
class
internal
::
ArgInserter
<
Char
>
;
friend
class
internal
::
FormatterProxy
;
friend
class
internal
::
FormatterProxy
<
Char
>
;
void
Add
(
const
Arg
&
arg
)
{
void
Add
(
const
Arg
&
arg
)
{
args_
.
push_back
(
&
arg
);
args_
.
push_back
(
&
arg
);
...
@@ -930,7 +954,7 @@ class Formatter : public BasicWriter<char> {
...
@@ -930,7 +954,7 @@ class Formatter : public BasicWriter<char> {
// Formats an argument of a custom type, such as a user-defined class.
// Formats an argument of a custom type, such as a user-defined class.
template
<
typename
T
>
template
<
typename
T
>
void
FormatCustomArg
(
const
void
*
arg
,
const
FormatSpec
&
spec
)
{
void
FormatCustomArg
(
const
void
*
arg
,
const
FormatSpec
&
spec
)
{
BasicWriter
&
f
=
*
this
;
BasicWriter
<
Char
>
&
f
=
*
this
;
Format
(
f
,
spec
,
*
static_cast
<
const
T
*>
(
arg
));
Format
(
f
,
spec
,
*
static_cast
<
const
T
*>
(
arg
));
}
}
...
@@ -952,16 +976,19 @@ class Formatter : public BasicWriter<char> {
...
@@ -952,16 +976,19 @@ class Formatter : public BasicWriter<char> {
/**
/**
Constructs a formatter with an empty output buffer.
Constructs a formatter with an empty output buffer.
*/
*/
Formatter
()
:
format_
(
0
)
{}
Basic
Formatter
()
:
format_
(
0
)
{}
/**
/**
Formats a string appending the output to the internal buffer.
Formats a string appending the output to the internal buffer.
Arguments are accepted through the returned `ArgInserter` object
Arguments are accepted through the returned `ArgInserter` object
using inserter operator `<<`.
using inserter operator `<<`.
*/
*/
internal
::
ArgInserter
operator
()(
StringRef
format
);
internal
::
ArgInserter
<
Char
>
operator
()(
StringRef
format
);
}
;
}
;
typedef
BasicFormatter
<
char
>
Formatter
;
typedef
BasicFormatter
<
wchar_t
>
WFormatter
;
template
<
typename
Char
>
template
<
typename
Char
>
inline
std
::
basic_string
<
Char
>
str
(
const
BasicWriter
<
Char
>
&
f
)
{
inline
std
::
basic_string
<
Char
>
str
(
const
BasicWriter
<
Char
>
&
f
)
{
return
f
.
str
();
return
f
.
str
();
...
@@ -970,22 +997,23 @@ inline std::basic_string<Char> str(const BasicWriter<Char> &f) {
...
@@ -970,22 +997,23 @@ inline std::basic_string<Char> str(const BasicWriter<Char> &f) {
template
<
typename
Char
>
template
<
typename
Char
>
inline
const
Char
*
c_str
(
const
BasicWriter
<
Char
>
&
f
)
{
return
f
.
c_str
();
}
inline
const
Char
*
c_str
(
const
BasicWriter
<
Char
>
&
f
)
{
return
f
.
c_str
();
}
std
::
string
str
(
internal
::
FormatterProxy
p
);
std
::
string
str
(
internal
::
FormatterProxy
<
char
>
p
);
const
char
*
c_str
(
internal
::
FormatterProxy
p
);
const
char
*
c_str
(
internal
::
FormatterProxy
<
char
>
p
);
namespace
internal
{
namespace
internal
{
using
fmt
::
str
;
using
fmt
::
str
;
using
fmt
::
c_str
;
using
fmt
::
c_str
;
template
<
typename
Char
>
class
FormatterProxy
{
class
FormatterProxy
{
private:
private:
Formatter
*
formatter_
;
BasicFormatter
<
Char
>
*
formatter_
;
public:
public:
explicit
FormatterProxy
(
Formatter
*
f
)
:
formatter_
(
f
)
{}
explicit
FormatterProxy
(
BasicFormatter
<
Char
>
*
f
)
:
formatter_
(
f
)
{}
Formatter
*
Format
()
{
BasicFormatter
<
Char
>
*
Format
()
{
formatter_
->
CompleteFormatting
();
formatter_
->
CompleteFormatting
();
return
formatter_
;
return
formatter_
;
}
}
...
@@ -995,20 +1023,21 @@ class FormatterProxy {
...
@@ -995,20 +1023,21 @@ class FormatterProxy {
// returned by one of the formatting functions. It stores a reference
// returned by one of the formatting functions. It stores a reference
// to a formatter and provides operator<< that feeds arguments to the
// to a formatter and provides operator<< that feeds arguments to the
// formatter.
// formatter.
template
<
typename
Char
>
class
ArgInserter
{
class
ArgInserter
{
private:
private:
mutable
Formatter
*
formatter_
;
mutable
BasicFormatter
<
Char
>
*
formatter_
;
friend
class
fmt
::
Formatter
;
friend
class
fmt
::
BasicFormatter
<
Char
>
;
friend
class
fmt
::
StringRef
;
friend
class
fmt
::
StringRef
;
// Do not implement.
// Do not implement.
void
operator
=
(
const
ArgInserter
&
other
);
void
operator
=
(
const
ArgInserter
&
other
);
protected:
protected:
explicit
ArgInserter
(
Formatter
*
f
=
0
)
:
formatter_
(
f
)
{}
explicit
ArgInserter
(
BasicFormatter
<
Char
>
*
f
=
0
)
:
formatter_
(
f
)
{}
void
Init
(
Formatter
&
f
,
const
char
*
format
)
{
void
Init
(
BasicFormatter
<
Char
>
&
f
,
const
char
*
format
)
{
const
ArgInserter
&
other
=
f
(
format
);
const
ArgInserter
&
other
=
f
(
format
);
formatter_
=
other
.
formatter_
;
formatter_
=
other
.
formatter_
;
other
.
formatter_
=
0
;
other
.
formatter_
=
0
;
...
@@ -1019,8 +1048,8 @@ class ArgInserter {
...
@@ -1019,8 +1048,8 @@ class ArgInserter {
other
.
formatter_
=
0
;
other
.
formatter_
=
0
;
}
}
const
Formatter
*
Format
()
const
{
const
BasicFormatter
<
Char
>
*
Format
()
const
{
Formatter
*
f
=
formatter_
;
BasicFormatter
<
Char
>
*
f
=
formatter_
;
if
(
f
)
{
if
(
f
)
{
formatter_
=
0
;
formatter_
=
0
;
f
->
CompleteFormatting
();
f
->
CompleteFormatting
();
...
@@ -1028,7 +1057,7 @@ class ArgInserter {
...
@@ -1028,7 +1057,7 @@ class ArgInserter {
return
f
;
return
f
;
}
}
Formatter
*
formatter
()
const
{
return
formatter_
;
}
BasicFormatter
<
Char
>
*
formatter
()
const
{
return
formatter_
;
}
const
char
*
format
()
const
{
return
formatter_
->
format_
;
}
const
char
*
format
()
const
{
return
formatter_
->
format_
;
}
void
ResetFormatter
()
const
{
formatter_
=
0
;
}
void
ResetFormatter
()
const
{
formatter_
=
0
;
}
...
@@ -1040,20 +1069,20 @@ class ArgInserter {
...
@@ -1040,20 +1069,20 @@ class ArgInserter {
}
}
// Feeds an argument to a formatter.
// Feeds an argument to a formatter.
ArgInserter
&
operator
<<
(
const
Formatter
::
Arg
&
arg
)
{
ArgInserter
&
operator
<<
(
const
typename
BasicFormatter
<
Char
>
::
Arg
&
arg
)
{
arg
.
formatter
=
formatter_
;
arg
.
formatter
=
formatter_
;
formatter_
->
Add
(
arg
);
formatter_
->
Add
(
arg
);
return
*
this
;
return
*
this
;
}
}
operator
FormatterProxy
()
{
operator
FormatterProxy
<
Char
>
()
{
Formatter
*
f
=
formatter_
;
BasicFormatter
<
Char
>
*
f
=
formatter_
;
formatter_
=
0
;
formatter_
=
0
;
return
FormatterProxy
(
f
);
return
FormatterProxy
<
Char
>
(
f
);
}
}
operator
StringRef
()
{
operator
StringRef
()
{
const
Formatter
*
f
=
Format
();
const
BasicFormatter
<
Char
>
*
f
=
Format
();
return
StringRef
(
f
->
c_str
(),
f
->
size
());
return
StringRef
(
f
->
c_str
(),
f
->
size
());
}
}
};
};
...
@@ -1062,7 +1091,7 @@ class ArgInserter {
...
@@ -1062,7 +1091,7 @@ class ArgInserter {
/**
/**
Returns the content of the output buffer as an `std::string`.
Returns the content of the output buffer as an `std::string`.
*/
*/
inline
std
::
string
str
(
internal
::
FormatterProxy
p
)
{
inline
std
::
string
str
(
internal
::
FormatterProxy
<
char
>
p
)
{
return
p
.
Format
()
->
str
();
return
p
.
Format
()
->
str
();
}
}
...
@@ -1070,15 +1099,17 @@ inline std::string str(internal::FormatterProxy p) {
...
@@ -1070,15 +1099,17 @@ inline std::string str(internal::FormatterProxy p) {
Returns a pointer to the output buffer content with terminating null
Returns a pointer to the output buffer content with terminating null
character appended.
character appended.
*/
*/
inline
const
char
*
c_str
(
internal
::
FormatterProxy
p
)
{
inline
const
char
*
c_str
(
internal
::
FormatterProxy
<
char
>
p
)
{
return
p
.
Format
()
->
c_str
();
return
p
.
Format
()
->
c_str
();
}
}
inline
internal
::
ArgInserter
Formatter
::
operator
()(
StringRef
format
)
{
template
<
typename
Char
>
internal
::
ArgInserter
formatter
(
this
);
inline
internal
::
ArgInserter
<
Char
>
BasicFormatter
<
Char
>::
operator
()(
StringRef
format
)
{
internal
::
ArgInserter
<
Char
>
inserter
(
this
);
format_
=
format
.
c_str
();
format_
=
format
.
c_str
();
args_
.
clear
();
args_
.
clear
();
return
format
ter
;
return
inser
ter
;
}
}
/**
/**
...
@@ -1087,7 +1118,8 @@ inline internal::ArgInserter Formatter::operator()(StringRef format) {
...
@@ -1087,7 +1118,8 @@ inline internal::ArgInserter Formatter::operator()(StringRef format) {
class
NoAction
{
class
NoAction
{
public:
public:
/** Does nothing. */
/** Does nothing. */
void
operator
()(
const
Formatter
&
)
const
{}
template
<
typename
Char
>
void
operator
()(
const
BasicFormatter
<
Char
>
&
)
const
{}
};
};
/**
/**
...
@@ -1095,10 +1127,10 @@ class NoAction {
...
@@ -1095,10 +1127,10 @@ class NoAction {
Objects of this class normally exist only as temporaries returned
Objects of this class normally exist only as temporaries returned
by one of the formatting functions which explains the name.
by one of the formatting functions which explains the name.
*/
*/
template
<
typename
Action
=
NoAction
>
template
<
typename
Char
,
typename
Action
=
NoAction
>
class
TempFormatter
:
public
internal
::
ArgInserter
{
class
TempFormatter
:
public
internal
::
ArgInserter
<
Char
>
{
private:
private:
Formatter
formatter_
;
BasicFormatter
<
Char
>
formatter_
;
Action
action_
;
Action
action_
;
// Forbid copying other than from a temporary. Do not implement.
// Forbid copying other than from a temporary. Do not implement.
...
@@ -1119,38 +1151,38 @@ class TempFormatter : public internal::ArgInserter {
...
@@ -1119,38 +1151,38 @@ class TempFormatter : public internal::ArgInserter {
\rst
\rst
Constructs a temporary formatter with a format string and an action.
Constructs a temporary formatter with a format string and an action.
The action should be an unary function object that takes a const
The action should be an unary function object that takes a const
reference to :cpp:class:`fmt::Formatter` as an argument.
reference to :cpp:class:`fmt::
Basic
Formatter` as an argument.
See :cpp:class:`fmt::NoAction` and :cpp:class:`fmt::Write` for
See :cpp:class:`fmt::NoAction` and :cpp:class:`fmt::Write` for
examples of action classes.
examples of action classes.
\endrst
\endrst
*/
*/
explicit
TempFormatter
(
StringRef
format
,
Action
a
=
Action
())
explicit
TempFormatter
(
StringRef
format
,
Action
a
=
Action
())
:
action_
(
a
)
{
:
action_
(
a
)
{
Init
(
formatter_
,
format
.
c_str
());
this
->
Init
(
formatter_
,
format
.
c_str
());
}
}
/**
/**
Constructs a temporary formatter from a proxy object.
Constructs a temporary formatter from a proxy object.
*/
*/
TempFormatter
(
const
Proxy
&
p
)
TempFormatter
(
const
Proxy
&
p
)
:
ArgInserter
(
0
),
action_
(
p
.
action
)
{
:
internal
::
ArgInserter
<
Char
>
(
0
),
action_
(
p
.
action
)
{
Init
(
formatter_
,
p
.
format
);
this
->
Init
(
formatter_
,
p
.
format
);
}
}
/**
/**
Performs the actual formatting, invokes the action and destroys the object.
Performs the actual formatting, invokes the action and destroys the object.
*/
*/
~
TempFormatter
()
{
~
TempFormatter
()
{
if
(
formatter
())
if
(
this
->
formatter
())
action_
(
*
Format
());
action_
(
*
this
->
Format
());
}
}
/**
/**
Converts a temporary formatter into a proxy object.
Converts a temporary formatter into a proxy object.
*/
*/
operator
Proxy
()
{
operator
Proxy
()
{
const
char
*
fmt
=
format
();
const
char
*
fmt
=
this
->
format
();
ResetFormatter
();
this
->
ResetFormatter
();
return
Proxy
(
fmt
,
action_
);
return
Proxy
(
fmt
,
action_
);
}
}
};
};
...
@@ -1172,13 +1204,13 @@ class TempFormatter : public internal::ArgInserter {
...
@@ -1172,13 +1204,13 @@ class TempFormatter : public internal::ArgInserter {
See also `Format String Syntax`_.
See also `Format String Syntax`_.
\endrst
\endrst
*/
*/
inline
TempFormatter
<>
Format
(
StringRef
format
)
{
inline
TempFormatter
<
char
>
Format
(
StringRef
format
)
{
return
TempFormatter
<>
(
format
);
return
TempFormatter
<
char
>
(
format
);
}
}
// A formatting action that writes formatted output to stdout.
// A formatting action that writes formatted output to stdout.
struct
Write
{
struct
Write
{
void
operator
()(
const
Formatter
&
f
)
const
{
void
operator
()(
const
BasicFormatter
<
char
>
&
f
)
const
{
std
::
fwrite
(
f
.
data
(),
1
,
f
.
size
(),
stdout
);
std
::
fwrite
(
f
.
data
(),
1
,
f
.
size
(),
stdout
);
}
}
};
};
...
@@ -1186,8 +1218,307 @@ struct Write {
...
@@ -1186,8 +1218,307 @@ struct Write {
// Formats a string and prints it to stdout.
// Formats a string and prints it to stdout.
// Example:
// Example:
// Print("Elapsed time: {0:.2f} seconds") << 1.23;
// Print("Elapsed time: {0:.2f} seconds") << 1.23;
inline
TempFormatter
<
Write
>
Print
(
StringRef
format
)
{
inline
TempFormatter
<
char
,
Write
>
Print
(
StringRef
format
)
{
return
TempFormatter
<
Write
>
(
format
);
return
TempFormatter
<
char
,
Write
>
(
format
);
}
// Throws Exception(message) if format contains '}', otherwise throws
// FormatError reporting unmatched '{'. The idea is that unmatched '{'
// should override other errors.
template
<
typename
Char
>
void
BasicFormatter
<
Char
>::
ReportError
(
const
char
*
s
,
StringRef
message
)
const
{
for
(
int
num_open_braces
=
num_open_braces_
;
*
s
;
++
s
)
{
if
(
*
s
==
'{'
)
{
++
num_open_braces
;
}
else
if
(
*
s
==
'}'
)
{
if
(
--
num_open_braces
==
0
)
throw
fmt
::
FormatError
(
message
);
}
}
throw
fmt
::
FormatError
(
"unmatched '{' in format"
);
}
// 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
<
typename
Char
>
unsigned
BasicFormatter
<
Char
>::
ParseUInt
(
const
char
*&
s
)
const
{
assert
(
'0'
<=
*
s
&&
*
s
<=
'9'
);
unsigned
value
=
0
;
do
{
unsigned
new_value
=
value
*
10
+
(
*
s
++
-
'0'
);
if
(
new_value
<
value
)
// Check if value wrapped around.
ReportError
(
s
,
"number is too big in format"
);
value
=
new_value
;
}
while
(
'0'
<=
*
s
&&
*
s
<=
'9'
);
return
value
;
}
template
<
typename
Char
>
inline
const
typename
BasicFormatter
<
Char
>::
Arg
&
BasicFormatter
<
Char
>::
ParseArgIndex
(
const
char
*&
s
)
{
unsigned
arg_index
=
0
;
if
(
*
s
<
'0'
||
*
s
>
'9'
)
{
if
(
*
s
!=
'}'
&&
*
s
!=
':'
)
ReportError
(
s
,
"invalid argument index in format string"
);
if
(
next_arg_index_
<
0
)
{
ReportError
(
s
,
"cannot switch from manual to automatic argument indexing"
);
}
arg_index
=
next_arg_index_
++
;
}
else
{
if
(
next_arg_index_
>
0
)
{
ReportError
(
s
,
"cannot switch from automatic to manual argument indexing"
);
}
next_arg_index_
=
-
1
;
arg_index
=
ParseUInt
(
s
);
if
(
arg_index
>=
args_
.
size
())
ReportError
(
s
,
"argument index is out of range in format"
);
}
return
*
args_
[
arg_index
];
}
template
<
typename
Char
>
void
BasicFormatter
<
Char
>::
CheckSign
(
const
char
*&
s
,
const
Arg
&
arg
)
{
if
(
arg
.
type
>
LAST_NUMERIC_TYPE
)
{
ReportError
(
s
,
Format
(
"format specifier '{0}' requires numeric argument"
)
<<
*
s
);
}
if
(
arg
.
type
==
UINT
||
arg
.
type
==
ULONG
)
{
ReportError
(
s
,
Format
(
"format specifier '{0}' requires signed argument"
)
<<
*
s
);
}
++
s
;
}
template
<
typename
Char
>
void
BasicFormatter
<
Char
>::
DoFormat
()
{
const
Char
*
start
=
format_
;
format_
=
0
;
next_arg_index_
=
0
;
const
Char
*
s
=
start
;
while
(
*
s
)
{
char
c
=
*
s
++
;
if
(
c
!=
'{'
&&
c
!=
'}'
)
continue
;
if
(
*
s
==
c
)
{
this
->
buffer_
.
append
(
start
,
s
);
start
=
++
s
;
continue
;
}
if
(
c
==
'}'
)
throw
FormatError
(
"unmatched '}' in format"
);
num_open_braces_
=
1
;
this
->
buffer_
.
append
(
start
,
s
-
1
);
const
Arg
&
arg
=
ParseArgIndex
(
s
);
FormatSpec
spec
;
int
precision
=
-
1
;
if
(
*
s
==
':'
)
{
++
s
;
// Parse fill and alignment.
if
(
char
c
=
*
s
)
{
const
char
*
p
=
s
+
1
;
spec
.
align_
=
ALIGN_DEFAULT
;
do
{
switch
(
*
p
)
{
case
'<'
:
spec
.
align_
=
ALIGN_LEFT
;
break
;
case
'>'
:
spec
.
align_
=
ALIGN_RIGHT
;
break
;
case
'='
:
spec
.
align_
=
ALIGN_NUMERIC
;
break
;
case
'^'
:
spec
.
align_
=
ALIGN_CENTER
;
break
;
}
if
(
spec
.
align_
!=
ALIGN_DEFAULT
)
{
if
(
p
!=
s
)
{
if
(
c
==
'}'
)
break
;
if
(
c
==
'{'
)
ReportError
(
s
,
"invalid fill character '{'"
);
s
+=
2
;
spec
.
fill_
=
c
;
}
else
++
s
;
if
(
spec
.
align_
==
ALIGN_NUMERIC
&&
arg
.
type
>
LAST_NUMERIC_TYPE
)
ReportError
(
s
,
"format specifier '=' requires numeric argument"
);
break
;
}
}
while
(
--
p
>=
s
);
}
// Parse sign.
switch
(
*
s
)
{
case
'+'
:
CheckSign
(
s
,
arg
);
spec
.
flags_
|=
SIGN_FLAG
|
PLUS_FLAG
;
break
;
case
'-'
:
CheckSign
(
s
,
arg
);
break
;
case
' '
:
CheckSign
(
s
,
arg
);
spec
.
flags_
|=
SIGN_FLAG
;
break
;
}
if
(
*
s
==
'#'
)
{
if
(
arg
.
type
>
LAST_NUMERIC_TYPE
)
ReportError
(
s
,
"format specifier '#' requires numeric argument"
);
spec
.
flags_
|=
HASH_FLAG
;
++
s
;
}
// Parse width and zero flag.
if
(
'0'
<=
*
s
&&
*
s
<=
'9'
)
{
if
(
*
s
==
'0'
)
{
if
(
arg
.
type
>
LAST_NUMERIC_TYPE
)
ReportError
(
s
,
"format specifier '0' requires numeric argument"
);
spec
.
align_
=
ALIGN_NUMERIC
;
spec
.
fill_
=
'0'
;
}
// Zero may be parsed again as a part of the width, but it is simpler
// and more efficient than checking if the next char is a digit.
unsigned
value
=
ParseUInt
(
s
);
if
(
value
>
INT_MAX
)
ReportError
(
s
,
"number is too big in format"
);
spec
.
width_
=
value
;
}
// Parse precision.
if
(
*
s
==
'.'
)
{
++
s
;
precision
=
0
;
if
(
'0'
<=
*
s
&&
*
s
<=
'9'
)
{
unsigned
value
=
ParseUInt
(
s
);
if
(
value
>
INT_MAX
)
ReportError
(
s
,
"number is too big in format"
);
precision
=
value
;
}
else
if
(
*
s
==
'{'
)
{
++
s
;
++
num_open_braces_
;
const
Arg
&
precision_arg
=
ParseArgIndex
(
s
);
unsigned
long
value
=
0
;
switch
(
precision_arg
.
type
)
{
case
INT
:
if
(
precision_arg
.
int_value
<
0
)
ReportError
(
s
,
"negative precision in format"
);
value
=
precision_arg
.
int_value
;
break
;
case
UINT
:
value
=
precision_arg
.
uint_value
;
break
;
case
LONG
:
if
(
precision_arg
.
long_value
<
0
)
ReportError
(
s
,
"negative precision in format"
);
value
=
precision_arg
.
long_value
;
break
;
case
ULONG
:
value
=
precision_arg
.
ulong_value
;
break
;
default:
ReportError
(
s
,
"precision is not integer"
);
}
if
(
value
>
INT_MAX
)
ReportError
(
s
,
"number is too big in format"
);
precision
=
value
;
if
(
*
s
++
!=
'}'
)
throw
FormatError
(
"unmatched '{' in format"
);
--
num_open_braces_
;
}
else
{
ReportError
(
s
,
"missing precision in format"
);
}
if
(
arg
.
type
!=
DOUBLE
&&
arg
.
type
!=
LONG_DOUBLE
)
{
ReportError
(
s
,
"precision specifier requires floating-point argument"
);
}
}
// Parse type.
if
(
*
s
!=
'}'
&&
*
s
)
spec
.
type_
=
*
s
++
;
}
if
(
*
s
++
!=
'}'
)
throw
FormatError
(
"unmatched '{' in format"
);
start
=
s
;
// Format argument.
switch
(
arg
.
type
)
{
case
INT
:
this
->
FormatInt
(
arg
.
int_value
,
spec
);
break
;
case
UINT
:
this
->
FormatInt
(
arg
.
uint_value
,
spec
);
break
;
case
LONG
:
this
->
FormatInt
(
arg
.
long_value
,
spec
);
break
;
case
ULONG
:
this
->
FormatInt
(
arg
.
ulong_value
,
spec
);
break
;
case
DOUBLE
:
this
->
FormatDouble
(
arg
.
double_value
,
spec
,
precision
);
break
;
case
LONG_DOUBLE
:
this
->
FormatDouble
(
arg
.
long_double_value
,
spec
,
precision
);
break
;
case
CHAR
:
{
if
(
spec
.
type_
&&
spec
.
type_
!=
'c'
)
internal
::
ReportUnknownType
(
spec
.
type_
,
"char"
);
char
*
out
=
0
;
if
(
spec
.
width_
>
1
)
{
out
=
this
->
GrowBuffer
(
spec
.
width_
);
if
(
spec
.
align_
==
ALIGN_RIGHT
)
{
std
::
fill_n
(
out
,
spec
.
width_
-
1
,
spec
.
fill_
);
out
+=
spec
.
width_
-
1
;
}
else
if
(
spec
.
align_
==
ALIGN_CENTER
)
{
out
=
this
->
FillPadding
(
out
,
spec
.
width_
,
1
,
spec
.
fill_
);
}
else
{
std
::
fill_n
(
out
+
1
,
spec
.
width_
-
1
,
spec
.
fill_
);
}
}
else
{
out
=
this
->
GrowBuffer
(
1
);
}
*
out
=
arg
.
int_value
;
break
;
}
case
STRING
:
{
if
(
spec
.
type_
&&
spec
.
type_
!=
's'
)
internal
::
ReportUnknownType
(
spec
.
type_
,
"string"
);
const
char
*
str
=
arg
.
string
.
value
;
std
::
size_t
size
=
arg
.
string
.
size
;
if
(
size
==
0
)
{
if
(
!
str
)
throw
FormatError
(
"string pointer is null"
);
if
(
*
str
)
size
=
std
::
strlen
(
str
);
}
this
->
FormatString
(
str
,
size
,
spec
);
break
;
}
case
POINTER
:
if
(
spec
.
type_
&&
spec
.
type_
!=
'p'
)
internal
::
ReportUnknownType
(
spec
.
type_
,
"pointer"
);
spec
.
flags_
=
HASH_FLAG
;
spec
.
type_
=
'x'
;
this
->
FormatInt
(
reinterpret_cast
<
uintptr_t
>
(
arg
.
pointer_value
),
spec
);
break
;
case
CUSTOM
:
if
(
spec
.
type_
)
internal
::
ReportUnknownType
(
spec
.
type_
,
"object"
);
(
this
->*
arg
.
custom
.
format
)(
arg
.
custom
.
value
,
spec
);
break
;
default:
assert
(
false
);
break
;
}
}
this
->
buffer_
.
append
(
start
,
s
);
}
}
}
}
...
...
format_test.cc
View file @
688de77b
...
@@ -25,12 +25,6 @@
...
@@ -25,12 +25,6 @@
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
*/
*/
// Disable useless MSVC warnings.
#undef _CRT_SECURE_NO_WARNINGS
#define _CRT_SECURE_NO_WARNINGS
#undef _SCL_SECURE_NO_WARNINGS
#define _SCL_SECURE_NO_WARNINGS
#include <cctype>
#include <cctype>
#include <cfloat>
#include <cfloat>
#include <climits>
#include <climits>
...
@@ -980,7 +974,7 @@ struct CountCalls {
...
@@ -980,7 +974,7 @@ struct CountCalls {
TEST
(
TempFormatterTest
,
Action
)
{
TEST
(
TempFormatterTest
,
Action
)
{
int
num_calls
=
0
;
int
num_calls
=
0
;
{
{
fmt
::
TempFormatter
<
CountCalls
>
af
(
"test"
,
CountCalls
(
num_calls
));
fmt
::
TempFormatter
<
char
,
CountCalls
>
af
(
"test"
,
CountCalls
(
num_calls
));
EXPECT_EQ
(
0
,
num_calls
);
EXPECT_EQ
(
0
,
num_calls
);
}
}
EXPECT_EQ
(
1
,
num_calls
);
EXPECT_EQ
(
1
,
num_calls
);
...
@@ -989,9 +983,8 @@ TEST(TempFormatterTest, Action) {
...
@@ -989,9 +983,8 @@ TEST(TempFormatterTest, Action) {
TEST
(
TempFormatterTest
,
ActionNotCalledOnError
)
{
TEST
(
TempFormatterTest
,
ActionNotCalledOnError
)
{
int
num_calls
=
0
;
int
num_calls
=
0
;
{
{
EXPECT_THROW
(
typedef
fmt
::
TempFormatter
<
char
,
CountCalls
>
TestFormatter
;
fmt
::
TempFormatter
<
CountCalls
>
af
(
"{0"
,
CountCalls
(
num_calls
)),
EXPECT_THROW
(
TestFormatter
af
(
"{0"
,
CountCalls
(
num_calls
)),
FormatError
);
FormatError
);
}
}
EXPECT_EQ
(
0
,
num_calls
);
EXPECT_EQ
(
0
,
num_calls
);
}
}
...
@@ -1003,8 +996,8 @@ TEST(TempFormatterTest, ActionNotCalledOnError) {
...
@@ -1003,8 +996,8 @@ TEST(TempFormatterTest, ActionNotCalledOnError) {
TEST
(
TempFormatterTest
,
ArgLifetime
)
{
TEST
(
TempFormatterTest
,
ArgLifetime
)
{
// The following code is for testing purposes only. It is a definite abuse
// The following code is for testing purposes only. It is a definite abuse
// of the API and shouldn't be used in real applications.
// of the API and shouldn't be used in real applications.
const
fmt
::
TempFormatter
<>
&
af
=
fmt
::
Format
(
"{0}"
);
const
fmt
::
TempFormatter
<
char
>
&
af
=
fmt
::
Format
(
"{0}"
);
const_cast
<
fmt
::
TempFormatter
<>&>
(
af
)
<<
std
::
string
(
"test"
);
const_cast
<
fmt
::
TempFormatter
<
char
>&>
(
af
)
<<
std
::
string
(
"test"
);
// String object passed as an argument to TempFormatter has
// String object passed as an argument to TempFormatter has
// been destroyed, but ArgInserter dtor hasn't been called yet.
// been destroyed, but ArgInserter dtor hasn't been called yet.
// But that's OK since the Arg's dtor takes care of this and
// But that's OK since the Arg's dtor takes care of this and
...
@@ -1023,8 +1016,8 @@ struct PrintError {
...
@@ -1023,8 +1016,8 @@ struct PrintError {
}
}
};
};
fmt
::
TempFormatter
<
PrintError
>
ReportError
(
const
char
*
format
)
{
fmt
::
TempFormatter
<
char
,
PrintError
>
ReportError
(
const
char
*
format
)
{
return
fmt
::
TempFormatter
<
PrintError
>
(
format
);
return
fmt
::
TempFormatter
<
char
,
PrintError
>
(
format
);
}
}
TEST
(
TempFormatterTest
,
Examples
)
{
TEST
(
TempFormatterTest
,
Examples
)
{
...
...
Write
Preview
Markdown
is supported
0%
Try again
or
attach a new file
Attach a file
Cancel
You are about to add
0
people
to the discussion. Proceed with caution.
Finish editing this message first!
Cancel
Please
register
or
sign in
to comment