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
a128b5b2
Commit
a128b5b2
authored
Sep 01, 2019
by
Victor Zverovich
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Simplify format string compilation
parent
466128de
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
95 additions
and
103 deletions
+95
-103
include/fmt/compile.h
include/fmt/compile.h
+83
-93
test/compile-test.cc
test/compile-test.cc
+12
-10
No files found.
include/fmt/compile.h
View file @
a128b5b2
...
@@ -172,11 +172,12 @@ template <typename Context, typename Range, typename CompiledFormat>
...
@@ -172,11 +172,12 @@ template <typename Context, typename Range, typename CompiledFormat>
auto
vformat_to
(
Range
out
,
CompiledFormat
&
cf
,
basic_format_args
<
Context
>
args
)
auto
vformat_to
(
Range
out
,
CompiledFormat
&
cf
,
basic_format_args
<
Context
>
args
)
->
typename
Context
::
iterator
{
->
typename
Context
::
iterator
{
using
char_type
=
typename
Context
::
char_type
;
using
char_type
=
typename
Context
::
char_type
;
basic_parse_context
<
char_type
>
parse_ctx
(
to_string_view
(
cf
.
format_
));
basic_parse_context
<
char_type
>
parse_ctx
(
to_string_view
(
cf
.
format_
str_
));
Context
ctx
(
out
.
begin
(),
args
);
Context
ctx
(
out
.
begin
(),
args
);
const
auto
&
parts
=
cf
.
parts_provider_
.
parts
();
const
auto
&
parts
=
cf
.
parts
();
for
(
auto
part_it
=
parts
.
begin
();
part_it
!=
parts
.
end
();
++
part_it
)
{
for
(
auto
part_it
=
std
::
begin
(
parts
);
part_it
!=
std
::
end
(
parts
);
++
part_it
)
{
const
auto
&
part
=
*
part_it
;
const
auto
&
part
=
*
part_it
;
const
auto
&
value
=
part
.
val
;
const
auto
&
value
=
part
.
val
;
...
@@ -188,7 +189,8 @@ auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
...
@@ -188,7 +189,8 @@ auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
auto
&&
it
=
reserve
(
output
,
text
.
size
());
auto
&&
it
=
reserve
(
output
,
text
.
size
());
it
=
std
::
copy_n
(
text
.
begin
(),
text
.
size
(),
it
);
it
=
std
::
copy_n
(
text
.
begin
(),
text
.
size
(),
it
);
ctx
.
advance_to
(
output
);
ctx
.
advance_to
(
output
);
}
break
;
break
;
}
case
format_part_t
:
:
kind
::
arg_index
:
case
format_part_t
:
:
kind
::
arg_index
:
advance_to
(
parse_ctx
,
part
.
arg_id_end
);
advance_to
(
parse_ctx
,
part
.
arg_id_end
);
...
@@ -222,132 +224,120 @@ auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
...
@@ -222,132 +224,120 @@ auto vformat_to(Range out, CompiledFormat& cf, basic_format_args<Context> args)
advance_to
(
parse_ctx
,
part
.
arg_id_end
);
advance_to
(
parse_ctx
,
part
.
arg_id_end
);
ctx
.
advance_to
(
ctx
.
advance_to
(
visit_format_arg
(
arg_formatter
<
Range
>
(
ctx
,
nullptr
,
&
specs
),
arg
));
visit_format_arg
(
arg_formatter
<
Range
>
(
ctx
,
nullptr
,
&
specs
),
arg
));
}
break
;
break
;
}
}
}
}
}
return
ctx
.
out
();
return
ctx
.
out
();
}
}
}
// namespace cf
}
// namespace cf
template
<
typename
S
,
typename
PreparedPartsProvider
,
typename
...
Args
>
template
<
typename
S
,
typename
=
void
>
struct
compiled_format_base
{
class
compiled_format
{
private:
S
format_
;
PreparedPartsProvider
parts_provider_
;
template
<
typename
Context
,
typename
Range
,
typename
CompiledFormat
>
friend
auto
cf
::
vformat_to
(
Range
out
,
CompiledFormat
&
cf
,
basic_format_args
<
Context
>
args
)
->
typename
Context
::
iterator
;
public:
using
char_type
=
char_t
<
S
>
;
using
char_type
=
char_t
<
S
>
;
using
parts_container
=
std
::
vector
<
internal
::
format_part
<
char_type
>>
;
compiled_format
()
=
delete
;
parts_container
compiled_parts
;
constexpr
compiled_format
(
S
f
)
:
format_
(
std
::
move
(
f
)),
parts_provider_
(
to_string_view
(
format_
))
{}
};
template
<
typename
Format
>
class
compiletime_prepared_parts_type_provider
{
explicit
compiled_format_base
(
basic_string_view
<
char_type
>
format_str
)
{
private:
compile_format_string
<
false
>
(
format_str
,
using
char_type
=
char_t
<
Format
>
;
[
this
](
const
format_part
<
char_type
>&
part
)
{
compiled_parts
.
push_back
(
part
);
// Workaround for old compilers. Compiletime parts preparation will not be
});
// performed with them anyway.
}
#if FMT_USE_CONSTEXPR
static
FMT_CONSTEXPR_DECL
const
unsigned
number_of_format_parts
=
count_parts
(
to_string_view
(
Format
()));
#else
static
const
unsigned
number_of_format_parts
=
0u
;
#endif
public:
template
<
unsigned
N
>
struct
format_parts_array
{
using
value_type
=
format_part
<
char_type
>
;
FMT_CONSTEXPR
format_parts_array
()
:
arr
{}
{}
FMT_CONSTEXPR
value_type
&
operator
[](
unsigned
ind
)
{
return
arr
[
ind
];
}
FMT_CONSTEXPR
const
value_type
*
begin
()
const
{
return
arr
;
}
FMT_CONSTEXPR
const
value_type
*
end
()
const
{
return
begin
()
+
N
;
}
private:
value_type
arr
[
N
];
};
struct
empty
{
const
parts_container
&
parts
()
const
{
return
compiled_parts
;
}
// Parts preparator will search for it
};
using
value_type
=
format_part
<
char_type
>
;
};
using
type
=
conditional_t
<
number_of_format_parts
!=
0
,
template
<
typename
Char
,
unsigned
N
>
struct
format_part_array
{
format_parts_array
<
number_of_format_parts
>
,
empty
>
;
format_part
<
Char
>
data
[
N
]
=
{};
FMT_CONSTEXPR
format_part_array
()
=
default
;
};
};
template
<
typename
PartsContainer
,
typename
Char
>
template
<
typename
Char
,
unsigned
N
>
FMT_CONSTEXPR
PartsContainer
FMT_CONSTEXPR
format_part_array
<
Char
,
N
>
compile_to_parts
(
prepare_compiletime_parts
(
basic_string_view
<
Char
>
format_str
)
{
basic_string_view
<
Char
>
format_str
)
{
format_part_array
<
Char
,
N
>
parts
;
unsigned
counter
=
0
;
// This is not a lambda for compatibility with older compilers.
// This is not a lambda for compatibility with older compilers.
struct
collector
{
struct
{
PartsContainer
&
parts
;
format_part
<
Char
>*
parts
;
unsigned
counter
=
0
;
unsigned
*
counter
;
FMT_CONSTEXPR
void
operator
()(
const
format_part
<
Char
>&
part
)
{
FMT_CONSTEXPR
void
operator
()(
const
format_part
<
Char
>&
part
)
{
parts
[
counter
++
]
=
part
;
parts
[
(
*
counter
)
++
]
=
part
;
}
}
};
}
collector
{
parts
.
data
,
&
counter
};
PartsContainer
parts
;
compile_format_string
<
true
>
(
format_str
,
collector
);
compile_format_string
<
true
>
(
format_str
,
collector
{
parts
});
if
(
counter
<
N
)
{
parts
.
data
[
counter
]
=
format_part
<
Char
>::
make_text
(
basic_string_view
<
Char
>
());
}
return
parts
;
return
parts
;
}
}
template
<
typename
Char
>
class
runtime_parts_provider
{
template
<
typename
T
>
constexpr
const
T
&
constexpr_max
(
const
T
&
a
,
const
T
&
b
)
{
public:
return
(
a
<
b
)
?
b
:
a
;
using
parts_container
=
std
::
vector
<
internal
::
format_part
<
Char
>>
;
}
runtime_parts_provider
(
basic_string_view
<
Char
>
format_str
)
{
compile_format_string
<
false
>
(
format_str
,
[
this
](
const
format_part
<
Char
>&
part
)
{
parts_
.
push_back
(
part
);
});
}
const
parts_container
&
parts
()
const
{
return
parts_
;
}
template
<
typename
S
>
struct
compiled_format_base
<
S
,
enable_if_t
<
is_compile_string
<
S
>::
value
>>
{
using
char_type
=
char_t
<
S
>
;
private:
FMT_CONSTEXPR
explicit
compiled_format_base
(
basic_string_view
<
char_type
>
)
{}
parts_container
parts_
;
};
template
<
typename
Format
>
struct
compiletime_parts_provider
{
// Workaround for old compilers. Format string compilation will not be
using
parts_container
=
// performed there anyway.
typename
internal
::
compiletime_prepared_parts_type_provider
<
Format
>::
type
;
#if FMT_USE_CONSTEXPR
static
FMT_CONSTEXPR_DECL
const
unsigned
num_format_parts
=
constexpr_max
(
count_parts
(
to_string_view
(
S
())),
1u
);
#else
static
const
unsigned
num_format_parts
=
1
;
#endif
template
<
typename
Char
>
using
parts_container
=
format_part
<
char_type
>
[
num_format_parts
];
FMT_CONSTEXPR
compiletime_parts_provider
(
basic_string_view
<
Char
>
)
{}
const
parts_container
&
parts
()
const
{
const
parts_container
&
parts
()
const
{
static
FMT_CONSTEXPR_DECL
const
parts_container
prepar
ed_parts
=
static
FMT_CONSTEXPR_DECL
const
auto
compil
ed_parts
=
prepare_compiletime_parts
<
parts_container
>
(
compile_to_parts
<
char_type
,
num_format_parts
>
(
internal
::
to_string_view
(
Format
()));
internal
::
to_string_view
(
S
()));
return
prepared_parts
;
return
compiled_parts
.
data
;
}
}
};
};
template
<
typename
S
,
typename
...
Args
>
class
compiled_format
:
private
compiled_format_base
<
S
>
{
public:
using
typename
compiled_format_base
<
S
>::
char_type
;
private:
basic_string_view
<
char_type
>
format_str_
;
template
<
typename
Context
,
typename
Range
,
typename
CompiledFormat
>
friend
auto
cf
::
vformat_to
(
Range
out
,
CompiledFormat
&
cf
,
basic_format_args
<
Context
>
args
)
->
typename
Context
::
iterator
;
public:
compiled_format
()
=
delete
;
explicit
constexpr
compiled_format
(
basic_string_view
<
char_type
>
format_str
)
:
compiled_format_base
<
S
>
(
format_str
),
format_str_
(
format_str
)
{}
};
}
// namespace internal
}
// namespace internal
#if FMT_USE_CONSTEXPR
#if FMT_USE_CONSTEXPR
template
<
typename
...
Args
,
typename
S
,
template
<
typename
...
Args
,
typename
S
,
FMT_ENABLE_IF
(
is_compile_string
<
S
>
::
value
)
>
FMT_ENABLE_IF
(
is_compile_string
<
S
>
::
value
)
>
FMT_CONSTEXPR
auto
compile
(
S
format_str
)
FMT_CONSTEXPR
auto
compile
(
S
format_str
)
->
internal
::
compiled_format
<
S
,
internal
::
compiletime_parts_provider
<
S
>
,
->
internal
::
compiled_format
<
S
,
Args
...
>
{
Args
...
>
{
return
internal
::
compiled_format
<
S
,
Args
...
>
(
to_string_view
(
format_str
));
return
format_str
;
}
}
#endif
#endif
// Compiles the format string which must be a string literal.
// Compiles the format string which must be a string literal.
template
<
typename
...
Args
,
typename
Char
,
size_t
N
>
template
<
typename
...
Args
,
typename
Char
,
size_t
N
>
auto
compile
(
const
Char
(
&
format_str
)[
N
])
->
internal
::
compiled_format
<
auto
compile
(
const
Char
(
&
format_str
)[
N
])
std
::
basic_string
<
Char
>
,
internal
::
runtime_parts_provider
<
Char
>
,
Args
...
>
{
->
internal
::
compiled_format
<
const
Char
*
,
Args
...
>
{
return
std
::
basic_string
<
Char
>
(
format_str
,
N
-
1
);
return
internal
::
compiled_format
<
const
Char
*
,
Args
...
>
(
basic_string_view
<
Char
>
(
format_str
,
N
-
1
));
}
}
template
<
typename
CompiledFormat
,
typename
...
Args
,
template
<
typename
CompiledFormat
,
typename
...
Args
,
...
@@ -383,7 +373,7 @@ format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
...
@@ -383,7 +373,7 @@ format_to_n_result<OutputIt> format_to_n(OutputIt out, size_t n,
template
<
typename
CompiledFormat
,
typename
...
Args
>
template
<
typename
CompiledFormat
,
typename
...
Args
>
std
::
size_t
formatted_size
(
const
CompiledFormat
&
cf
,
const
Args
&
...
args
)
{
std
::
size_t
formatted_size
(
const
CompiledFormat
&
cf
,
const
Args
&
...
args
)
{
return
f
mt
::
f
ormat_to
(
return
format_to
(
internal
::
counting_iterator
<
typename
CompiledFormat
::
char_type
>
(),
internal
::
counting_iterator
<
typename
CompiledFormat
::
char_type
>
(),
cf
,
args
...)
cf
,
args
...)
.
count
();
.
count
();
...
...
test/compile-test.cc
View file @
a128b5b2
...
@@ -39,14 +39,12 @@ using testing::StrictMock;
...
@@ -39,14 +39,12 @@ using testing::StrictMock;
#if FMT_USE_CONSTEXPR
#if FMT_USE_CONSTEXPR
template
<
unsigned
EXPECTED_PARTS_COUNT
,
typename
Format
>
template
<
unsigned
EXPECTED_PARTS_COUNT
,
typename
Format
>
void
check_prepared_parts_type
(
Format
format
)
{
void
check_prepared_parts_type
(
Format
format
)
{
typedef
fmt
::
internal
::
compiletime_prepared_parts_type_provider
<
decltype
(
typedef
fmt
::
internal
::
compiled_format_base
<
decltype
(
format
)
>
provider
;
format
)
>
typedef
fmt
::
internal
::
format_part
<
char
>
provider
;
expected_parts_type
[
EXPECTED_PARTS_COUNT
];
typedef
typename
provider
::
template
format_parts_array
<
EXPECTED_PARTS_COUNT
>
static_assert
(
std
::
is_same
<
typename
provider
::
parts_container
,
expected_parts_type
;
expected_parts_type
>::
value
,
static_assert
(
"CompileTimePreparedPartsTypeProvider test failed"
);
std
::
is_same
<
typename
provider
::
type
,
expected_parts_type
>::
value
,
"CompileTimePreparedPartsTypeProvider test failed"
);
}
}
TEST
(
CompileTest
,
CompileTimePreparedPartsTypeProvider
)
{
TEST
(
CompileTest
,
CompileTimePreparedPartsTypeProvider
)
{
...
@@ -122,8 +120,7 @@ TEST(CompileTest, FormattedSize) {
...
@@ -122,8 +120,7 @@ TEST(CompileTest, FormattedSize) {
struct
formattable
{};
struct
formattable
{};
FMT_BEGIN_NAMESPACE
FMT_BEGIN_NAMESPACE
template
<
>
template
<
>
struct
formatter
<
formattable
>
:
formatter
<
const
char
*>
{
struct
formatter
<
formattable
>
:
formatter
<
const
char
*>
{
auto
format
(
formattable
,
format_context
&
ctx
)
->
decltype
(
ctx
.
out
())
{
auto
format
(
formattable
,
format_context
&
ctx
)
->
decltype
(
ctx
.
out
())
{
return
formatter
<
const
char
*>::
format
(
"foo"
,
ctx
);
return
formatter
<
const
char
*>::
format
(
"foo"
,
ctx
);
}
}
...
@@ -134,3 +131,8 @@ TEST(CompileTest, FormatUserDefinedType) {
...
@@ -134,3 +131,8 @@ TEST(CompileTest, FormatUserDefinedType) {
auto
f
=
fmt
::
compile
<
formattable
>
(
"{}"
);
auto
f
=
fmt
::
compile
<
formattable
>
(
"{}"
);
EXPECT_EQ
(
fmt
::
format
(
f
,
formattable
()),
"foo"
);
EXPECT_EQ
(
fmt
::
format
(
f
,
formattable
()),
"foo"
);
}
}
TEST
(
CompileTest
,
EmptyFormatString
)
{
auto
f
=
fmt
::
compile
<>
(
""
);
EXPECT_EQ
(
fmt
::
format
(
f
),
""
);
}
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