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
4faadff0
Commit
4faadff0
authored
Jun 08, 2019
by
Victor Zverovich
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Add preliminary user-defined type support
parent
5d487335
Changes
2
Hide whitespace changes
Inline
Side-by-side
Showing
2 changed files
with
272 additions
and
160 deletions
+272
-160
test/scan-test.cc
test/scan-test.cc
+39
-160
test/scan.h
test/scan.h
+233
-0
No files found.
test/scan-test.cc
View file @
4faadff0
// Formatting library for C++ - scanning API
proof of concep
t
// Formatting library for C++ - scanning API
tes
t
//
// Copyright (c) 201
2
- present, Victor Zverovich
// Copyright (c) 201
9
- present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include <
array
>
#include <
time.h
>
#include <climits>
#include "fmt/format.h"
#include "gmock.h"
#include "gtest-extra.h"
FMT_BEGIN_NAMESPACE
namespace
internal
{
enum
class
scan_type
{
none_type
,
int_type
,
uint_type
,
long_long_type
,
ulong_long_type
,
string_type
,
string_view_type
};
struct
scan_arg
{
scan_type
arg_type
;
union
{
int
*
int_value
;
unsigned
*
uint_value
;
long
long
*
long_long_value
;
unsigned
long
long
*
ulong_long_value
;
std
::
string
*
string
;
fmt
::
string_view
*
string_view
;
// TODO: more types
};
scan_arg
()
:
arg_type
(
scan_type
::
none_type
)
{}
scan_arg
(
int
&
value
)
:
arg_type
(
scan_type
::
int_type
),
int_value
(
&
value
)
{}
scan_arg
(
unsigned
&
value
)
:
arg_type
(
scan_type
::
uint_type
),
uint_value
(
&
value
)
{}
scan_arg
(
long
long
&
value
)
:
arg_type
(
scan_type
::
long_long_type
),
long_long_value
(
&
value
)
{}
scan_arg
(
unsigned
long
long
&
value
)
:
arg_type
(
scan_type
::
ulong_long_type
),
ulong_long_value
(
&
value
)
{}
scan_arg
(
std
::
string
&
value
)
:
arg_type
(
scan_type
::
string_type
),
string
(
&
value
)
{}
scan_arg
(
fmt
::
string_view
&
value
)
:
arg_type
(
scan_type
::
string_view_type
),
string_view
(
&
value
)
{}
};
}
// namespace internal
struct
scan_args
{
int
size
;
const
internal
::
scan_arg
*
data
;
template
<
size_t
N
>
scan_args
(
const
std
::
array
<
internal
::
scan_arg
,
N
>&
store
)
:
size
(
N
),
data
(
store
.
data
())
{
static_assert
(
N
<
INT_MAX
,
"too many arguments"
);
}
};
namespace
internal
{
struct
scan_handler
:
error_handler
{
private:
const
char
*
begin_
;
const
char
*
end_
;
scan_args
args_
;
int
next_arg_id_
;
scan_arg
arg_
;
template
<
typename
T
=
unsigned
>
T
read_uint
()
{
T
value
=
0
;
while
(
begin_
!=
end_
)
{
char
c
=
*
begin_
++
;
if
(
c
<
'0'
||
c
>
'9'
)
on_error
(
"invalid input"
);
// TODO: check overflow
value
=
value
*
10
+
(
c
-
'0'
);
}
return
value
;
}
template
<
typename
T
=
int
>
T
read_int
()
{
T
value
=
0
;
bool
negative
=
begin_
!=
end_
&&
*
begin_
==
'-'
;
if
(
negative
)
++
begin_
;
value
=
read_uint
<
typename
std
::
make_unsigned
<
T
>::
type
>
();
if
(
negative
)
value
=
-
value
;
return
value
;
}
public:
scan_handler
(
string_view
input
,
scan_args
args
)
:
begin_
(
input
.
data
()),
end_
(
begin_
+
input
.
size
()),
args_
(
args
),
next_arg_id_
(
0
)
{}
const
char
*
pos
()
const
{
return
begin_
;
}
void
on_text
(
const
char
*
begin
,
const
char
*
end
)
{
auto
size
=
end
-
begin
;
if
(
begin_
+
size
>
end_
||
!
std
::
equal
(
begin
,
end
,
begin_
))
on_error
(
"invalid input"
);
begin_
+=
size
;
}
void
on_arg_id
()
{
if
(
next_arg_id_
>=
args_
.
size
)
on_error
(
"argument index out of range"
);
arg_
=
args_
.
data
[
next_arg_id_
++
];
}
void
on_arg_id
(
unsigned
)
{
on_error
(
"invalid format"
);
}
void
on_arg_id
(
string_view
)
{
on_error
(
"invalid format"
);
}
void
on_replacement_field
(
const
char
*
)
{
switch
(
arg_
.
arg_type
)
{
case
scan_type
:
:
int_type
:
*
arg_
.
int_value
=
read_int
();
break
;
case
scan_type
:
:
uint_type
:
*
arg_
.
uint_value
=
read_uint
();
break
;
case
scan_type
:
:
long_long_type
:
*
arg_
.
long_long_value
=
read_int
<
long
long
>
();
break
;
case
scan_type
:
:
ulong_long_type
:
*
arg_
.
ulong_long_value
=
read_uint
<
unsigned
long
long
>
();
break
;
case
scan_type
:
:
string_type
:
while
(
begin_
!=
end_
&&
*
begin_
!=
' '
)
arg_
.
string
->
push_back
(
*
begin_
++
);
break
;
case
scan_type
:
:
string_view_type
:
{
auto
s
=
begin_
;
while
(
begin_
!=
end_
&&
*
begin_
!=
' '
)
++
begin_
;
*
arg_
.
string_view
=
fmt
::
string_view
(
s
,
begin_
-
s
);
break
;
}
default:
assert
(
false
);
}
}
const
char
*
on_format_specs
(
const
char
*
begin
,
const
char
*
)
{
return
begin
;
}
};
}
// namespace internal
template
<
typename
...
Args
>
std
::
array
<
internal
::
scan_arg
,
sizeof
...(
Args
)
>
make_scan_args
(
Args
&
...
args
)
{
return
std
::
array
<
internal
::
scan_arg
,
sizeof
...(
Args
)
>
{
args
...};
}
string_view
::
iterator
vscan
(
string_view
input
,
string_view
format_str
,
scan_args
args
)
{
internal
::
scan_handler
h
(
input
,
args
);
internal
::
parse_format_string
<
false
>
(
format_str
,
h
);
return
input
.
begin
()
+
(
h
.
pos
()
-
&*
input
.
begin
());
}
template
<
typename
...
Args
>
string_view
::
iterator
scan
(
string_view
input
,
string_view
format_str
,
Args
&
...
args
)
{
return
vscan
(
input
,
format_str
,
make_scan_args
(
args
...));
}
FMT_END_NAMESPACE
#include "scan.h"
TEST
(
ScanTest
,
ReadText
)
{
fmt
::
string_view
s
=
"foo"
;
...
...
@@ -219,6 +63,41 @@ TEST(ScanTest, ReadStringView) {
EXPECT_EQ
(
s
,
"foo"
);
}
#ifndef _WIN32
namespace
fmt
{
template
<
>
struct
scanner
<
tm
>
{
std
::
string
format
;
scan_parse_context
::
iterator
parse
(
scan_parse_context
&
ctx
)
{
auto
it
=
ctx
.
begin
();
if
(
it
!=
ctx
.
end
()
&&
*
it
==
':'
)
++
it
;
auto
end
=
it
;
while
(
end
!=
ctx
.
end
()
&&
*
end
!=
'}'
)
++
end
;
format
.
reserve
(
internal
::
to_unsigned
(
end
-
it
+
1
));
format
.
append
(
it
,
end
);
format
.
push_back
(
'\0'
);
return
end
;
}
template
<
class
ScanContext
>
typename
ScanContext
::
iterator
scan
(
tm
&
t
,
ScanContext
&
ctx
)
{
auto
result
=
strptime
(
ctx
.
begin
(),
format
.
c_str
(),
&
t
);
if
(
!
result
)
throw
format_error
(
"failed to parse time"
);
return
result
;
}
};
}
// namespace fmt
TEST
(
ScanTest
,
ReadCustom
)
{
const
char
*
input
=
"Date: 1985-10-25"
;
auto
t
=
tm
();
fmt
::
scan
(
input
,
"Date: {0:%Y-%m-%d}"
,
t
);
EXPECT_EQ
(
t
.
tm_year
,
85
);
EXPECT_EQ
(
t
.
tm_mon
,
9
);
EXPECT_EQ
(
t
.
tm_mday
,
25
);
}
#endif
TEST
(
ScanTest
,
InvalidFormat
)
{
EXPECT_THROW_MSG
(
fmt
::
scan
(
""
,
"{}"
),
fmt
::
format_error
,
"argument index out of range"
);
...
...
test/scan.h
0 → 100644
View file @
4faadff0
// Formatting library for C++ - scanning API proof of concept
//
// Copyright (c) 2019 - present, Victor Zverovich
// All rights reserved.
//
// For the license information refer to format.h.
#include <array>
#include "fmt/format.h"
FMT_BEGIN_NAMESPACE
template
<
typename
T
,
typename
Char
=
char
>
struct
scanner
{
// A deleted default constructor indicates a disabled scanner.
scanner
()
=
delete
;
};
class
scan_parse_context
{
private:
string_view
format_
;
public:
using
iterator
=
string_view
::
iterator
;
explicit
FMT_CONSTEXPR
scan_parse_context
(
string_view
format
)
:
format_
(
format
)
{}
FMT_CONSTEXPR
iterator
begin
()
const
{
return
format_
.
begin
();
}
FMT_CONSTEXPR
iterator
end
()
const
{
return
format_
.
end
();
}
void
advance_to
(
iterator
it
)
{
format_
.
remove_prefix
(
internal
::
to_unsigned
(
it
-
begin
()));
}
};
struct
scan_context
{
private:
string_view
input_
;
public:
using
iterator
=
const
char
*
;
explicit
scan_context
(
string_view
input
)
:
input_
(
input
)
{}
iterator
begin
()
const
{
return
input_
.
data
();
}
iterator
end
()
const
{
return
begin
()
+
input_
.
size
();
}
void
advance_to
(
iterator
it
)
{
input_
.
remove_prefix
(
internal
::
to_unsigned
(
it
-
begin
()));
}
};
namespace
internal
{
enum
class
scan_type
{
none_type
,
int_type
,
uint_type
,
long_long_type
,
ulong_long_type
,
string_type
,
string_view_type
,
custom_type
};
struct
custom_scan_arg
{
void
*
value
;
void
(
*
scan
)(
void
*
arg
,
scan_parse_context
&
parse_ctx
,
scan_context
&
ctx
);
};
class
scan_arg
{
public:
scan_type
type
;
union
{
int
*
int_value
;
unsigned
*
uint_value
;
long
long
*
long_long_value
;
unsigned
long
long
*
ulong_long_value
;
std
::
string
*
string
;
fmt
::
string_view
*
string_view
;
custom_scan_arg
custom
;
// TODO: more types
};
scan_arg
()
:
type
(
scan_type
::
none_type
)
{}
scan_arg
(
int
&
value
)
:
type
(
scan_type
::
int_type
),
int_value
(
&
value
)
{}
scan_arg
(
unsigned
&
value
)
:
type
(
scan_type
::
uint_type
),
uint_value
(
&
value
)
{}
scan_arg
(
long
long
&
value
)
:
type
(
scan_type
::
long_long_type
),
long_long_value
(
&
value
)
{}
scan_arg
(
unsigned
long
long
&
value
)
:
type
(
scan_type
::
ulong_long_type
),
ulong_long_value
(
&
value
)
{}
scan_arg
(
std
::
string
&
value
)
:
type
(
scan_type
::
string_type
),
string
(
&
value
)
{}
scan_arg
(
fmt
::
string_view
&
value
)
:
type
(
scan_type
::
string_view_type
),
string_view
(
&
value
)
{}
template
<
typename
T
>
scan_arg
(
T
&
value
)
:
type
(
scan_type
::
custom_type
)
{
custom
.
value
=
&
value
;
custom
.
scan
=
scan_custom_arg
<
T
>
;
}
private:
template
<
typename
T
>
static
void
scan_custom_arg
(
void
*
arg
,
scan_parse_context
&
parse_ctx
,
scan_context
&
ctx
)
{
scanner
<
T
>
s
;
parse_ctx
.
advance_to
(
s
.
parse
(
parse_ctx
));
ctx
.
advance_to
(
s
.
scan
(
*
static_cast
<
T
*>
(
arg
),
ctx
));
}
};
}
// namespace internal
struct
scan_args
{
int
size
;
const
internal
::
scan_arg
*
data
;
template
<
size_t
N
>
scan_args
(
const
std
::
array
<
internal
::
scan_arg
,
N
>&
store
)
:
size
(
N
),
data
(
store
.
data
())
{
static_assert
(
N
<
INT_MAX
,
"too many arguments"
);
}
};
namespace
internal
{
struct
scan_handler
:
error_handler
{
private:
scan_parse_context
parse_ctx_
;
scan_context
scan_ctx_
;
scan_args
args_
;
int
next_arg_id_
;
scan_arg
arg_
;
template
<
typename
T
=
unsigned
>
T
read_uint
()
{
T
value
=
0
;
auto
it
=
scan_ctx_
.
begin
(),
end
=
scan_ctx_
.
end
();
while
(
it
!=
end
)
{
char
c
=
*
it
++
;
if
(
c
<
'0'
||
c
>
'9'
)
on_error
(
"invalid input"
);
// TODO: check overflow
value
=
value
*
10
+
(
c
-
'0'
);
}
scan_ctx_
.
advance_to
(
it
);
return
value
;
}
template
<
typename
T
=
int
>
T
read_int
()
{
T
value
=
0
;
auto
it
=
scan_ctx_
.
begin
(),
end
=
scan_ctx_
.
end
();
bool
negative
=
it
!=
end
&&
*
it
==
'-'
;
if
(
negative
)
++
it
;
scan_ctx_
.
advance_to
(
it
);
value
=
read_uint
<
typename
std
::
make_unsigned
<
T
>::
type
>
();
if
(
negative
)
value
=
-
value
;
return
value
;
}
public:
scan_handler
(
string_view
format
,
string_view
input
,
scan_args
args
)
:
parse_ctx_
(
format
),
scan_ctx_
(
input
),
args_
(
args
),
next_arg_id_
(
0
)
{}
const
char
*
pos
()
const
{
return
scan_ctx_
.
begin
();
}
void
on_text
(
const
char
*
begin
,
const
char
*
end
)
{
auto
size
=
end
-
begin
;
auto
it
=
scan_ctx_
.
begin
();
if
(
it
+
size
>
scan_ctx_
.
end
()
||
!
std
::
equal
(
begin
,
end
,
it
))
on_error
(
"invalid input"
);
scan_ctx_
.
advance_to
(
it
+
size
);
}
void
on_arg_id
()
{
on_arg_id
(
next_arg_id_
++
);
}
void
on_arg_id
(
unsigned
id
)
{
if
(
id
>=
args_
.
size
)
on_error
(
"argument index out of range"
);
arg_
=
args_
.
data
[
id
];
}
void
on_arg_id
(
string_view
)
{
on_error
(
"invalid format"
);
}
void
on_replacement_field
(
const
char
*
)
{
auto
it
=
scan_ctx_
.
begin
(),
end
=
scan_ctx_
.
end
();
switch
(
arg_
.
type
)
{
case
scan_type
:
:
int_type
:
*
arg_
.
int_value
=
read_int
();
break
;
case
scan_type
:
:
uint_type
:
*
arg_
.
uint_value
=
read_uint
();
break
;
case
scan_type
:
:
long_long_type
:
*
arg_
.
long_long_value
=
read_int
<
long
long
>
();
break
;
case
scan_type
:
:
ulong_long_type
:
*
arg_
.
ulong_long_value
=
read_uint
<
unsigned
long
long
>
();
break
;
case
scan_type
:
:
string_type
:
while
(
it
!=
end
&&
*
it
!=
' '
)
arg_
.
string
->
push_back
(
*
it
++
);
scan_ctx_
.
advance_to
(
it
);
break
;
case
scan_type
:
:
string_view_type
:
{
auto
s
=
it
;
while
(
it
!=
end
&&
*
it
!=
' '
)
++
it
;
*
arg_
.
string_view
=
fmt
::
string_view
(
s
,
it
-
s
);
scan_ctx_
.
advance_to
(
it
);
break
;
}
default:
assert
(
false
);
}
}
const
char
*
on_format_specs
(
const
char
*
begin
,
const
char
*
)
{
if
(
arg_
.
type
!=
scan_type
::
custom_type
)
return
begin
;
parse_ctx_
.
advance_to
(
begin
);
arg_
.
custom
.
scan
(
arg_
.
custom
.
value
,
parse_ctx_
,
scan_ctx_
);
return
parse_ctx_
.
begin
();
}
};
}
// namespace internal
template
<
typename
...
Args
>
std
::
array
<
internal
::
scan_arg
,
sizeof
...(
Args
)
>
make_scan_args
(
Args
&
...
args
)
{
return
std
::
array
<
internal
::
scan_arg
,
sizeof
...(
Args
)
>
{
args
...};
}
string_view
::
iterator
vscan
(
string_view
input
,
string_view
format_str
,
scan_args
args
)
{
internal
::
scan_handler
h
(
format_str
,
input
,
args
);
internal
::
parse_format_string
<
false
>
(
format_str
,
h
);
return
input
.
begin
()
+
(
h
.
pos
()
-
&*
input
.
begin
());
}
template
<
typename
...
Args
>
string_view
::
iterator
scan
(
string_view
input
,
string_view
format_str
,
Args
&
...
args
)
{
return
vscan
(
input
,
format_str
,
make_scan_args
(
args
...));
}
FMT_END_NAMESPACE
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