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
3e01376e
Commit
3e01376e
authored
Dec 16, 2018
by
Victor Zverovich
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Implement fill/align/width parsing in chrono formatter
parent
1f92f8a9
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
137 additions
and
83 deletions
+137
-83
include/fmt/chrono.h
include/fmt/chrono.h
+51
-5
include/fmt/format.h
include/fmt/format.h
+78
-78
test/chrono-test.cc
test/chrono-test.cc
+8
-0
No files found.
include/fmt/chrono.h
View file @
3e01376e
...
@@ -370,12 +370,50 @@ template <> FMT_CONSTEXPR const char *get_units<std::ratio<3600>>() {
...
@@ -370,12 +370,50 @@ template <> FMT_CONSTEXPR const char *get_units<std::ratio<3600>>() {
template
<
typename
Rep
,
typename
Period
,
typename
Char
>
template
<
typename
Rep
,
typename
Period
,
typename
Char
>
struct
formatter
<
std
::
chrono
::
duration
<
Rep
,
Period
>
,
Char
>
{
struct
formatter
<
std
::
chrono
::
duration
<
Rep
,
Period
>
,
Char
>
{
private:
align_spec
spec
;
internal
::
arg_ref
<
Char
>
width_ref
;
mutable
basic_string_view
<
Char
>
format_str
;
mutable
basic_string_view
<
Char
>
format_str
;
typedef
std
::
chrono
::
duration
<
Rep
,
Period
>
duration
;
typedef
std
::
chrono
::
duration
<
Rep
,
Period
>
duration
;
struct
spec_handler
{
formatter
&
f
;
basic_parse_context
<
Char
>
&
context
;
typedef
internal
::
arg_ref
<
Char
>
arg_ref_type
;
template
<
typename
Id
>
FMT_CONSTEXPR
arg_ref_type
make_arg_ref
(
Id
arg_id
)
{
context
.
check_arg_id
(
arg_id
);
return
arg_ref_type
(
arg_id
);
}
FMT_CONSTEXPR
arg_ref_type
make_arg_ref
(
internal
::
auto_id
)
{
return
arg_ref_type
(
context
.
next_arg_id
());
}
void
on_error
(
const
char
*
msg
)
{
throw
format_error
(
msg
);
}
void
on_fill
(
Char
fill
)
{
f
.
spec
.
fill_
=
fill
;
}
void
on_align
(
alignment
align
)
{
f
.
spec
.
align_
=
align
;
}
void
on_width
(
unsigned
width
)
{
f
.
spec
.
width_
=
width
;
}
template
<
typename
Id
>
void
on_dynamic_width
(
Id
arg_id
)
{
f
.
width_ref
=
make_arg_ref
(
arg_id
);
}
};
public:
formatter
()
:
spec
()
{}
FMT_CONSTEXPR
auto
parse
(
basic_parse_context
<
Char
>
&
ctx
)
FMT_CONSTEXPR
auto
parse
(
basic_parse_context
<
Char
>
&
ctx
)
->
decltype
(
ctx
.
begin
())
{
->
decltype
(
ctx
.
begin
())
{
auto
begin
=
ctx
.
begin
(),
end
=
ctx
.
end
();
auto
begin
=
ctx
.
begin
(),
end
=
ctx
.
end
();
if
(
begin
==
end
)
return
begin
;
spec_handler
handler
{
*
this
,
ctx
};
begin
=
internal
::
parse_align
(
begin
,
end
,
handler
);
if
(
begin
==
end
)
return
begin
;
begin
=
internal
::
parse_width
(
begin
,
end
,
handler
);
end
=
parse_chrono_format
(
begin
,
end
,
internal
::
chrono_format_checker
());
end
=
parse_chrono_format
(
begin
,
end
,
internal
::
chrono_format_checker
());
format_str
=
basic_string_view
<
Char
>
(
&*
begin
,
end
-
begin
);
format_str
=
basic_string_view
<
Char
>
(
&*
begin
,
end
-
begin
);
return
end
;
return
end
;
...
@@ -386,13 +424,21 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
...
@@ -386,13 +424,21 @@ struct formatter<std::chrono::duration<Rep, Period>, Char> {
->
decltype
(
ctx
.
out
())
{
->
decltype
(
ctx
.
out
())
{
auto
begin
=
format_str
.
begin
(),
end
=
format_str
.
end
();
auto
begin
=
format_str
.
begin
(),
end
=
format_str
.
end
();
if
(
begin
==
end
||
*
begin
==
'}'
)
{
if
(
begin
==
end
||
*
begin
==
'}'
)
{
memory_buffer
buf
;
if
(
const
char
*
unit
=
get_units
<
Period
>
())
if
(
const
char
*
unit
=
get_units
<
Period
>
())
return
format_to
(
ctx
.
out
(),
"{}{}"
,
d
.
count
(),
unit
);
format_to
(
buf
,
"{}{}"
,
d
.
count
(),
unit
);
if
(
Period
::
den
==
1
)
else
if
(
Period
::
den
==
1
)
return
format_to
(
ctx
.
out
(),
"{}[{}]s"
,
d
.
count
(),
Period
::
num
);
format_to
(
buf
,
"{}[{}]s"
,
d
.
count
(),
Period
::
num
);
return
format_to
(
ctx
.
out
(),
"{}[{}/{}]s"
,
else
d
.
count
(),
Period
::
num
,
Period
::
den
);
format_to
(
buf
,
"{}[{}/{}]s"
,
d
.
count
(),
Period
::
num
,
Period
::
den
);
typedef
output_range
<
decltype
(
ctx
.
out
()),
Char
>
range
;
basic_writer
<
range
>
w
(
range
(
ctx
.
out
()));
internal
::
handle_dynamic_spec
<
internal
::
width_checker
>
(
spec
.
width_
,
width_ref
,
ctx
);
w
.
write
(
buf
.
data
(),
buf
.
size
(),
spec
);
return
w
.
out
();
}
}
// TODO: use fill and align
internal
::
chrono_formatter
<
FormatContext
>
f
(
ctx
);
internal
::
chrono_formatter
<
FormatContext
>
f
(
ctx
);
f
.
s
=
std
::
chrono
::
duration_cast
<
std
::
chrono
::
seconds
>
(
d
);
f
.
s
=
std
::
chrono
::
duration_cast
<
std
::
chrono
::
seconds
>
(
d
);
f
.
ms
=
std
::
chrono
::
duration_cast
<
std
::
chrono
::
milliseconds
>
(
d
-
f
.
s
);
f
.
ms
=
std
::
chrono
::
duration_cast
<
std
::
chrono
::
milliseconds
>
(
d
-
f
.
s
);
...
...
include/fmt/format.h
View file @
3e01376e
...
@@ -1342,7 +1342,7 @@ class arg_formatter_base {
...
@@ -1342,7 +1342,7 @@ class arg_formatter_base {
void
write
(
bool
value
)
{
void
write
(
bool
value
)
{
string_view
sv
(
value
?
"true"
:
"false"
);
string_view
sv
(
value
?
"true"
:
"false"
);
specs_
?
writer_
.
write
_str
(
sv
,
*
specs_
)
:
writer_
.
write
(
sv
);
specs_
?
writer_
.
write
(
sv
,
*
specs_
)
:
writer_
.
write
(
sv
);
}
}
void
write
(
const
char_type
*
value
)
{
void
write
(
const
char_type
*
value
)
{
...
@@ -1350,7 +1350,7 @@ class arg_formatter_base {
...
@@ -1350,7 +1350,7 @@ class arg_formatter_base {
FMT_THROW
(
format_error
(
"string pointer is null"
));
FMT_THROW
(
format_error
(
"string pointer is null"
));
auto
length
=
std
::
char_traits
<
char_type
>::
length
(
value
);
auto
length
=
std
::
char_traits
<
char_type
>::
length
(
value
);
basic_string_view
<
char_type
>
sv
(
value
,
length
);
basic_string_view
<
char_type
>
sv
(
value
,
length
);
specs_
?
writer_
.
write
_str
(
sv
,
*
specs_
)
:
writer_
.
write
(
sv
);
specs_
?
writer_
.
write
(
sv
,
*
specs_
)
:
writer_
.
write
(
sv
);
}
}
public:
public:
...
@@ -1426,7 +1426,7 @@ class arg_formatter_base {
...
@@ -1426,7 +1426,7 @@ class arg_formatter_base {
if
(
specs_
)
{
if
(
specs_
)
{
internal
::
check_string_type_spec
(
internal
::
check_string_type_spec
(
specs_
->
type
,
internal
::
error_handler
());
specs_
->
type
,
internal
::
error_handler
());
writer_
.
write
_str
(
value
,
*
specs_
);
writer_
.
write
(
value
,
*
specs_
);
}
else
{
}
else
{
writer_
.
write
(
value
);
writer_
.
write
(
value
);
}
}
...
@@ -1840,15 +1840,11 @@ struct precision_adapter {
...
@@ -1840,15 +1840,11 @@ struct precision_adapter {
SpecHandler
&
handler
;
SpecHandler
&
handler
;
};
};
// Parses standard format specifiers and sends notifications about parsed
// Parses fill and alignment.
// components to handler.
template
<
typename
Char
,
typename
Handler
>
template
<
typename
Char
,
typename
SpecHandler
>
FMT_CONSTEXPR
const
Char
*
parse_align
(
FMT_CONSTEXPR
const
Char
*
parse_format_specs
(
const
Char
*
begin
,
const
Char
*
end
,
Handler
&&
handler
)
{
const
Char
*
begin
,
const
Char
*
end
,
SpecHandler
&&
handler
)
{
FMT_ASSERT
(
begin
!=
end
,
""
);
if
(
begin
==
end
||
*
begin
==
'}'
)
return
begin
;
// Parse fill and alignment.
alignment
align
=
ALIGN_DEFAULT
;
alignment
align
=
ALIGN_DEFAULT
;
int
i
=
0
;
int
i
=
0
;
if
(
begin
+
1
!=
end
)
++
i
;
if
(
begin
+
1
!=
end
)
++
i
;
...
@@ -1876,10 +1872,39 @@ FMT_CONSTEXPR const Char *parse_format_specs(
...
@@ -1876,10 +1872,39 @@ FMT_CONSTEXPR const Char *parse_format_specs(
handler
.
on_fill
(
c
);
handler
.
on_fill
(
c
);
}
else
++
begin
;
}
else
++
begin
;
handler
.
on_align
(
align
);
handler
.
on_align
(
align
);
if
(
begin
==
end
)
return
begin
;
break
;
break
;
}
}
}
while
(
i
--
>
0
);
}
while
(
i
--
>
0
);
return
begin
;
}
template
<
typename
Char
,
typename
Handler
>
FMT_CONSTEXPR
const
Char
*
parse_width
(
const
Char
*
begin
,
const
Char
*
end
,
Handler
&&
handler
)
{
FMT_ASSERT
(
begin
!=
end
,
""
);
if
(
'0'
<=
*
begin
&&
*
begin
<=
'9'
)
{
handler
.
on_width
(
parse_nonnegative_int
(
begin
,
end
,
handler
));
}
else
if
(
*
begin
==
'{'
)
{
++
begin
;
if
(
begin
!=
end
)
begin
=
parse_arg_id
(
begin
,
end
,
width_adapter
<
Handler
,
Char
>
(
handler
));
if
(
begin
==
end
||
*
begin
!=
'}'
)
return
handler
.
on_error
(
"invalid format string"
),
begin
;
++
begin
;
}
return
begin
;
}
// Parses standard format specifiers and sends notifications about parsed
// components to handler.
template
<
typename
Char
,
typename
SpecHandler
>
FMT_CONSTEXPR
const
Char
*
parse_format_specs
(
const
Char
*
begin
,
const
Char
*
end
,
SpecHandler
&&
handler
)
{
if
(
begin
==
end
||
*
begin
==
'}'
)
return
begin
;
begin
=
parse_align
(
begin
,
end
,
handler
);
if
(
begin
==
end
)
return
begin
;
// Parse sign.
// Parse sign.
switch
(
static_cast
<
char
>
(
*
begin
))
{
switch
(
static_cast
<
char
>
(
*
begin
))
{
...
@@ -1909,20 +1934,8 @@ FMT_CONSTEXPR const Char *parse_format_specs(
...
@@ -1909,20 +1934,8 @@ FMT_CONSTEXPR const Char *parse_format_specs(
if
(
++
begin
==
end
)
return
begin
;
if
(
++
begin
==
end
)
return
begin
;
}
}
// Parse width.
begin
=
parse_width
(
begin
,
end
,
handler
);
if
(
'0'
<=
*
begin
&&
*
begin
<=
'9'
)
{
handler
.
on_width
(
parse_nonnegative_int
(
begin
,
end
,
handler
));
if
(
begin
==
end
)
return
begin
;
if
(
begin
==
end
)
return
begin
;
}
else
if
(
*
begin
==
'{'
)
{
++
begin
;
if
(
begin
!=
end
)
{
begin
=
parse_arg_id
(
begin
,
end
,
width_adapter
<
SpecHandler
,
Char
>
(
handler
));
}
if
(
begin
==
end
||
*
begin
!=
'}'
)
return
handler
.
on_error
(
"invalid format string"
),
begin
;
if
(
++
begin
==
end
)
return
begin
;
}
// Parse precision.
// Parse precision.
if
(
*
begin
==
'.'
)
{
if
(
*
begin
==
'.'
)
{
...
@@ -2251,8 +2264,6 @@ class basic_writer {
...
@@ -2251,8 +2264,6 @@ class basic_writer {
iterator
out_
;
// Output iterator.
iterator
out_
;
// Output iterator.
internal
::
locale_ref
locale_
;
internal
::
locale_ref
locale_
;
iterator
out
()
const
{
return
out_
;
}
// Attempts to reserve space for n extra characters in the output range.
// Attempts to reserve space for n extra characters in the output range.
// Returns a pointer to the reserved range or a reference to out_.
// Returns a pointer to the reserved range or a reference to out_.
auto
reserve
(
std
::
size_t
n
)
->
decltype
(
internal
::
reserve
(
out_
,
n
))
{
auto
reserve
(
std
::
size_t
n
)
->
decltype
(
internal
::
reserve
(
out_
,
n
))
{
...
@@ -2263,7 +2274,28 @@ class basic_writer {
...
@@ -2263,7 +2274,28 @@ class basic_writer {
// <left-padding><value><right-padding>
// <left-padding><value><right-padding>
// where <value> is written by f(it).
// where <value> is written by f(it).
template
<
typename
F
>
template
<
typename
F
>
void
write_padded
(
const
align_spec
&
spec
,
F
&&
f
);
void
write_padded
(
const
align_spec
&
spec
,
F
&&
f
)
{
unsigned
width
=
spec
.
width
();
// User-perceived width (in code points).
size_t
size
=
f
.
size
();
// The number of code units.
size_t
num_code_points
=
width
!=
0
?
f
.
width
()
:
size
;
if
(
width
<=
num_code_points
)
return
f
(
reserve
(
size
));
auto
&&
it
=
reserve
(
width
+
(
size
-
num_code_points
));
char_type
fill
=
static_cast
<
char_type
>
(
spec
.
fill
());
std
::
size_t
padding
=
width
-
num_code_points
;
if
(
spec
.
align
()
==
ALIGN_RIGHT
)
{
it
=
std
::
fill_n
(
it
,
padding
,
fill
);
f
(
it
);
}
else
if
(
spec
.
align
()
==
ALIGN_CENTER
)
{
std
::
size_t
left_padding
=
padding
/
2
;
it
=
std
::
fill_n
(
it
,
left_padding
,
fill
);
f
(
it
);
it
=
std
::
fill_n
(
it
,
padding
-
left_padding
,
fill
);
}
else
{
f
(
it
);
it
=
std
::
fill_n
(
it
,
padding
,
fill
);
}
}
template
<
typename
F
>
template
<
typename
F
>
struct
padded_int_writer
{
struct
padded_int_writer
{
...
@@ -2525,15 +2557,6 @@ class basic_writer {
...
@@ -2525,15 +2557,6 @@ class basic_writer {
}
}
};
};
// Writes a formatted string.
template
<
typename
Char
>
void
write_str
(
const
Char
*
s
,
std
::
size_t
size
,
const
align_spec
&
spec
)
{
write_padded
(
spec
,
str_writer
<
Char
>
{
s
,
size
});
}
template
<
typename
Char
>
void
write_str
(
basic_string_view
<
Char
>
str
,
const
format_specs
&
spec
);
template
<
typename
Char
>
template
<
typename
Char
>
friend
class
internal
::
arg_formatter_base
;
friend
class
internal
::
arg_formatter_base
;
...
@@ -2543,6 +2566,8 @@ class basic_writer {
...
@@ -2543,6 +2566,8 @@ class basic_writer {
Range
out
,
internal
::
locale_ref
loc
=
internal
::
locale_ref
())
Range
out
,
internal
::
locale_ref
loc
=
internal
::
locale_ref
())
:
out_
(
out
.
begin
()),
locale_
(
loc
)
{}
:
out_
(
out
.
begin
()),
locale_
(
loc
)
{}
iterator
out
()
const
{
return
out_
;
}
void
write
(
int
value
)
{
write_decimal
(
value
);
}
void
write
(
int
value
)
{
write_decimal
(
value
);
}
void
write
(
long
value
)
{
write_decimal
(
value
);
}
void
write
(
long
value
)
{
write_decimal
(
value
);
}
void
write
(
long
long
value
)
{
write_decimal
(
value
);
}
void
write
(
long
long
value
)
{
write_decimal
(
value
);
}
...
@@ -2602,9 +2627,20 @@ class basic_writer {
...
@@ -2602,9 +2627,20 @@ class basic_writer {
it
=
std
::
copy
(
value
.
begin
(),
value
.
end
(),
it
);
it
=
std
::
copy
(
value
.
begin
(),
value
.
end
(),
it
);
}
}
template
<
typename
...
FormatSpecs
>
// Writes a formatted string.
void
write
(
basic_string_view
<
char_type
>
str
,
FormatSpecs
...
specs
)
{
template
<
typename
Char
>
write_str
(
str
,
format_specs
(
specs
...));
void
write
(
const
Char
*
s
,
std
::
size_t
size
,
const
align_spec
&
spec
)
{
write_padded
(
spec
,
str_writer
<
Char
>
{
s
,
size
});
}
template
<
typename
Char
>
void
write
(
basic_string_view
<
Char
>
s
,
const
format_specs
&
spec
=
format_specs
())
{
const
Char
*
data
=
s
.
data
();
std
::
size_t
size
=
s
.
size
();
if
(
spec
.
precision
>=
0
&&
internal
::
to_unsigned
(
spec
.
precision
)
<
size
)
size
=
internal
::
to_unsigned
(
spec
.
precision
);
write
(
data
,
size
,
spec
);
}
}
template
<
typename
T
>
template
<
typename
T
>
...
@@ -2617,42 +2653,6 @@ class basic_writer {
...
@@ -2617,42 +2653,6 @@ class basic_writer {
}
}
};
};
template
<
typename
Range
>
template
<
typename
F
>
void
basic_writer
<
Range
>::
write_padded
(
const
align_spec
&
spec
,
F
&&
f
)
{
unsigned
width
=
spec
.
width
();
// User-perceived width (in code points).
size_t
size
=
f
.
size
();
// The number of code units.
size_t
num_code_points
=
width
!=
0
?
f
.
width
()
:
size
;
if
(
width
<=
num_code_points
)
return
f
(
reserve
(
size
));
auto
&&
it
=
reserve
(
width
+
(
size
-
num_code_points
));
char_type
fill
=
static_cast
<
char_type
>
(
spec
.
fill
());
std
::
size_t
padding
=
width
-
num_code_points
;
if
(
spec
.
align
()
==
ALIGN_RIGHT
)
{
it
=
std
::
fill_n
(
it
,
padding
,
fill
);
f
(
it
);
}
else
if
(
spec
.
align
()
==
ALIGN_CENTER
)
{
std
::
size_t
left_padding
=
padding
/
2
;
it
=
std
::
fill_n
(
it
,
left_padding
,
fill
);
f
(
it
);
it
=
std
::
fill_n
(
it
,
padding
-
left_padding
,
fill
);
}
else
{
f
(
it
);
it
=
std
::
fill_n
(
it
,
padding
,
fill
);
}
}
template
<
typename
Range
>
template
<
typename
Char
>
void
basic_writer
<
Range
>::
write_str
(
basic_string_view
<
Char
>
s
,
const
format_specs
&
spec
)
{
const
Char
*
data
=
s
.
data
();
std
::
size_t
size
=
s
.
size
();
if
(
spec
.
precision
>=
0
&&
internal
::
to_unsigned
(
spec
.
precision
)
<
size
)
size
=
internal
::
to_unsigned
(
spec
.
precision
);
write_str
(
data
,
size
,
spec
);
}
struct
float_spec_handler
{
struct
float_spec_handler
{
char
type
;
char
type
;
bool
upper
;
bool
upper
;
...
...
test/chrono-test.cc
View file @
3e01376e
...
@@ -91,6 +91,14 @@ TEST(ChronoTest, FormatDefault) {
...
@@ -91,6 +91,14 @@ TEST(ChronoTest, FormatDefault) {
std
::
chrono
::
duration
<
int
,
std
::
ratio
<
15
,
4
>>
(
42
)));
std
::
chrono
::
duration
<
int
,
std
::
ratio
<
15
,
4
>>
(
42
)));
}
}
TEST
(
ChronoTest
,
Align
)
{
auto
s
=
std
::
chrono
::
seconds
(
42
);
EXPECT_EQ
(
"42s "
,
fmt
::
format
(
"{:5}"
,
s
));
EXPECT_EQ
(
"42s "
,
fmt
::
format
(
"{:{}}"
,
s
,
5
));
EXPECT_EQ
(
" 42s"
,
fmt
::
format
(
"{:>5}"
,
s
));
EXPECT_EQ
(
"**42s**"
,
fmt
::
format
(
"{:*^7}"
,
s
));
}
TEST
(
ChronoTest
,
FormatSpecs
)
{
TEST
(
ChronoTest
,
FormatSpecs
)
{
EXPECT_EQ
(
"%"
,
fmt
::
format
(
"{:%%}"
,
std
::
chrono
::
seconds
(
0
)));
EXPECT_EQ
(
"%"
,
fmt
::
format
(
"{:%%}"
,
std
::
chrono
::
seconds
(
0
)));
EXPECT_EQ
(
"
\n
"
,
fmt
::
format
(
"{:%n}"
,
std
::
chrono
::
seconds
(
0
)));
EXPECT_EQ
(
"
\n
"
,
fmt
::
format
(
"{:%n}"
,
std
::
chrono
::
seconds
(
0
)));
...
...
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