Skip to content
Projects
Groups
Snippets
Help
Loading...
Help
Support
Keyboard shortcuts
?
Submit feedback
Contribute to GitLab
Sign in / Register
Toggle navigation
A
Actor Framework
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
Operations
Operations
Metrics
Environments
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
cpp-libs
Actor Framework
Commits
70af2bb9
Commit
70af2bb9
authored
Jan 10, 2021
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Email Patches
Plain Diff
Improve parsing of int/float from config values
parent
1a35b53f
Changes
3
Show whitespace changes
Inline
Side-by-side
Showing
3 changed files
with
98 additions
and
16 deletions
+98
-16
libcaf_core/src/config_value.cpp
libcaf_core/src/config_value.cpp
+62
-4
libcaf_core/src/config_value_reader.cpp
libcaf_core/src/config_value_reader.cpp
+7
-12
libcaf_core/test/config_value.cpp
libcaf_core/test/config_value.cpp
+29
-0
No files found.
libcaf_core/src/config_value.cpp
View file @
70af2bb9
...
@@ -240,8 +240,7 @@ expected<bool> config_value::to_boolean() const {
...
@@ -240,8 +240,7 @@ expected<bool> config_value::to_boolean() const {
expected
<
config_value
::
integer
>
config_value
::
to_integer
()
const
{
expected
<
config_value
::
integer
>
config_value
::
to_integer
()
const
{
using
result_type
=
expected
<
integer
>
;
using
result_type
=
expected
<
integer
>
;
auto
f
=
detail
::
make_overload
(
auto
f
=
detail
::
make_overload
(
no_conversions
<
integer
,
none_t
,
bool
,
timespan
,
uri
,
config_value
::
list
,
no_conversions
<
integer
,
none_t
,
bool
,
timespan
,
uri
,
config_value
::
list
>
(),
config_value
::
dictionary
>
(),
[](
integer
x
)
{
return
result_type
{
x
};
},
[](
integer
x
)
{
return
result_type
{
x
};
},
[](
real
x
)
{
[](
real
x
)
{
using
limits
=
std
::
numeric_limits
<
config_value
::
integer
>
;
using
limits
=
std
::
numeric_limits
<
config_value
::
integer
>
;
...
@@ -269,6 +268,37 @@ expected<config_value::integer> config_value::to_integer() const {
...
@@ -269,6 +268,37 @@ expected<config_value::integer> config_value::to_integer() const {
detail
::
print_escaped
(
msg
,
x
);
detail
::
print_escaped
(
msg
,
x
);
msg
+=
" to an integer"
;
msg
+=
" to an integer"
;
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
},
[](
const
dictionary
&
x
)
{
if
(
auto
i
=
x
.
find
(
"@type"
);
i
!=
x
.
end
()
&&
holds_alternative
<
std
::
string
>
(
i
->
second
))
{
const
auto
&
tn
=
get
<
std
::
string
>
(
i
->
second
);
string_view
valid_types
[]
=
{
type_name_v
<
int16_t
>
,
type_name_v
<
int32_t
>
,
type_name_v
<
int64_t
>
,
type_name_v
<
int8_t
>
,
type_name_v
<
uint16_t
>
,
type_name_v
<
uint32_t
>
,
type_name_v
<
uint64_t
>
,
type_name_v
<
uint8_t
>
};
auto
eq
=
[
&
tn
](
string_view
x
)
{
return
x
==
tn
;
};
if
(
std
::
any_of
(
std
::
begin
(
valid_types
),
std
::
end
(
valid_types
),
eq
))
{
if
(
auto
j
=
x
.
find
(
"value"
);
j
!=
x
.
end
())
{
return
j
->
second
.
to_integer
();
}
else
{
std
::
string
msg
=
"missing value for object of type "
;
msg
+=
tn
;
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
}
}
else
{
std
::
string
msg
=
"cannot convert "
;
msg
+=
tn
;
msg
+=
" to an integer"
;
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
}
}
else
{
std
::
string
msg
=
"cannot convert a dictionary to an integer"
;
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
}
});
});
return
visit
(
f
,
data_
);
return
visit
(
f
,
data_
);
}
}
...
@@ -276,8 +306,7 @@ expected<config_value::integer> config_value::to_integer() const {
...
@@ -276,8 +306,7 @@ expected<config_value::integer> config_value::to_integer() const {
expected
<
config_value
::
real
>
config_value
::
to_real
()
const
{
expected
<
config_value
::
real
>
config_value
::
to_real
()
const
{
using
result_type
=
expected
<
real
>
;
using
result_type
=
expected
<
real
>
;
auto
f
=
detail
::
make_overload
(
auto
f
=
detail
::
make_overload
(
no_conversions
<
real
,
none_t
,
bool
,
timespan
,
uri
,
config_value
::
list
,
no_conversions
<
real
,
none_t
,
bool
,
timespan
,
uri
,
config_value
::
list
>
(),
config_value
::
dictionary
>
(),
[](
integer
x
)
{
[](
integer
x
)
{
// This cast may lose precision on the value. We could try and check that,
// This cast may lose precision on the value. We could try and check that,
// but refusing to convert on loss of precision could also be unexpected
// but refusing to convert on loss of precision could also be unexpected
...
@@ -293,6 +322,35 @@ expected<config_value::real> config_value::to_real() const {
...
@@ -293,6 +322,35 @@ expected<config_value::real> config_value::to_real() const {
detail
::
print_escaped
(
msg
,
x
);
detail
::
print_escaped
(
msg
,
x
);
msg
+=
" to a floating point number"
;
msg
+=
" to a floating point number"
;
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
},
[](
const
dictionary
&
x
)
{
if
(
auto
i
=
x
.
find
(
"@type"
);
i
!=
x
.
end
()
&&
holds_alternative
<
std
::
string
>
(
i
->
second
))
{
const
auto
&
tn
=
get
<
std
::
string
>
(
i
->
second
);
string_view
valid_types
[]
=
{
type_name_v
<
float
>
,
type_name_v
<
double
>
,
type_name_v
<
long
double
>
};
auto
eq
=
[
&
tn
](
string_view
x
)
{
return
x
==
tn
;
};
if
(
std
::
any_of
(
std
::
begin
(
valid_types
),
std
::
end
(
valid_types
),
eq
))
{
if
(
auto
j
=
x
.
find
(
"value"
);
j
!=
x
.
end
())
{
return
j
->
second
.
to_real
();
}
else
{
std
::
string
msg
=
"missing value for object of type "
;
msg
+=
tn
;
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
}
}
else
{
std
::
string
msg
=
"cannot convert "
;
msg
+=
tn
;
msg
+=
" to a floating point number"
;
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
}
}
else
{
std
::
string
msg
=
"cannot convert a dictionary to a floating point number"
;
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
}
});
});
return
visit
(
f
,
data_
);
return
visit
(
f
,
data_
);
}
}
...
...
libcaf_core/src/config_value_reader.cpp
View file @
70af2bb9
...
@@ -381,7 +381,7 @@ bool config_value_reader::begin_associative_array(size_t& size) {
...
@@ -381,7 +381,7 @@ bool config_value_reader::begin_associative_array(size_t& size) {
st_
.
top
()
=
associative_array
{
dict
->
begin
(),
dict
->
end
()};
st_
.
top
()
=
associative_array
{
dict
->
begin
(),
dict
->
end
()};
return
true
;
return
true
;
}
}
std
::
string
msg
=
"expected a dictionary, got a "
;
std
::
string
msg
=
"
begin_associative_array:
expected a dictionary, got a "
;
msg
+=
top
->
type_name
();
msg
+=
top
->
type_name
();
emplace_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
));
emplace_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
));
return
false
;
return
false
;
...
@@ -419,13 +419,10 @@ bool pull(config_value_reader& reader, T& x) {
...
@@ -419,13 +419,10 @@ bool pull(config_value_reader& reader, T& x) {
reader
.
pop
();
reader
.
pop
();
return
true
;
return
true
;
}
else
{
}
else
{
std
::
string
msg
=
"expected a dictionary, got a "
;
reader
.
set_error
(
std
::
move
(
val
.
error
()));
msg
+=
to_string
(
type_name_v
<
T
>
);
reader
.
emplace_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
));
return
false
;
return
false
;
}
}
}
}
else
if
(
holds_alternative
<
config_value_reader
::
sequence
>
(
top
))
{
if
(
holds_alternative
<
config_value_reader
::
sequence
>
(
top
))
{
auto
&
seq
=
get
<
config_value_reader
::
sequence
>
(
top
);
auto
&
seq
=
get
<
config_value_reader
::
sequence
>
(
top
);
if
(
seq
.
at_end
())
{
if
(
seq
.
at_end
())
{
reader
.
emplace_error
(
sec
::
runtime_error
,
"value: sequence out of bounds"
);
reader
.
emplace_error
(
sec
::
runtime_error
,
"value: sequence out of bounds"
);
...
@@ -437,13 +434,10 @@ bool pull(config_value_reader& reader, T& x) {
...
@@ -437,13 +434,10 @@ bool pull(config_value_reader& reader, T& x) {
seq
.
advance
();
seq
.
advance
();
return
true
;
return
true
;
}
else
{
}
else
{
std
::
string
msg
=
"expected a dictionary, got a "
;
reader
.
set_error
(
std
::
move
(
val
.
error
()));
msg
+=
to_string
(
type_name_v
<
T
>
);
reader
.
emplace_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
));
return
false
;
return
false
;
}
}
}
}
else
if
(
holds_alternative
<
config_value_reader
::
key_ptr
>
(
top
))
{
if
(
holds_alternative
<
config_value_reader
::
key_ptr
>
(
top
))
{
auto
ptr
=
get
<
config_value_reader
::
key_ptr
>
(
top
);
auto
ptr
=
get
<
config_value_reader
::
key_ptr
>
(
top
);
if
constexpr
(
std
::
is_same
<
std
::
string
,
T
>::
value
)
{
if
constexpr
(
std
::
is_same
<
std
::
string
,
T
>::
value
)
{
x
=
*
ptr
;
x
=
*
ptr
;
...
@@ -453,10 +447,11 @@ bool pull(config_value_reader& reader, T& x) {
...
@@ -453,10 +447,11 @@ bool pull(config_value_reader& reader, T& x) {
if
(
auto
err
=
detail
::
parse
(
*
ptr
,
x
))
{
if
(
auto
err
=
detail
::
parse
(
*
ptr
,
x
))
{
reader
.
set_error
(
std
::
move
(
err
));
reader
.
set_error
(
std
::
move
(
err
));
return
false
;
return
false
;
}
}
else
{
return
true
;
return
true
;
}
}
}
}
}
reader
.
emplace_error
(
sec
::
conversion_failed
,
reader
.
emplace_error
(
sec
::
conversion_failed
,
"expected a value, sequence, or key"
);
"expected a value, sequence, or key"
);
return
false
;
return
false
;
...
...
libcaf_core/test/config_value.cpp
View file @
70af2bb9
...
@@ -161,6 +161,23 @@ SCENARIO("get_as can convert config values to integers") {
...
@@ -161,6 +161,23 @@ SCENARIO("get_as can convert config values to integers") {
}
}
}
}
}
}
GIVEN
(
"a config value x with type annotation 'int32_t' and the value 50"
)
{
config_value
x
;
x
.
as_dictionary
().
emplace
(
"@type"
,
"int32_t"
);
x
.
as_dictionary
().
emplace
(
"value"
,
50
);
WHEN
(
"using get_as with integer types"
)
{
THEN
(
"CAF parses the integer and performs a bound check"
)
{
CHECK_EQ
(
get_as
<
uint64_t
>
(
x
),
50u
);
CHECK_EQ
(
get_as
<
int64_t
>
(
x
),
50
);
CHECK_EQ
(
get_as
<
uint32_t
>
(
x
),
50u
);
CHECK_EQ
(
get_as
<
int32_t
>
(
x
),
50
);
CHECK_EQ
(
get_as
<
uint16_t
>
(
x
),
50u
);
CHECK_EQ
(
get_as
<
int16_t
>
(
x
),
50
);
CHECK_EQ
(
get_as
<
uint8_t
>
(
x
),
50u
);
CHECK_EQ
(
get_as
<
int8_t
>
(
x
),
50
);
}
}
}
GIVEN
(
"a config value x with value 50.0"
)
{
GIVEN
(
"a config value x with value 50.0"
)
{
auto
x
=
config_value
{
50.0
};
auto
x
=
config_value
{
50.0
};
WHEN
(
"using get_as with integer types"
)
{
WHEN
(
"using get_as with integer types"
)
{
...
@@ -278,6 +295,18 @@ SCENARIO("get_as can convert config values to floating point numbers") {
...
@@ -278,6 +295,18 @@ SCENARIO("get_as can convert config values to floating point numbers") {
}
}
}
}
}
}
GIVEN
(
"a config value x with type annotation 'float' and the value 50"
)
{
config_value
x
;
x
.
as_dictionary
().
emplace
(
"@type"
,
"float"
);
x
.
as_dictionary
().
emplace
(
"value"
,
123.0
);
WHEN
(
"using get_as with floating point types"
)
{
THEN
(
"CAF parses the value and performs a bound check"
)
{
CHECK_EQ
(
get_as
<
long
double
>
(
x
),
123.0
);
CHECK_EQ
(
get_as
<
double
>
(
x
),
123.0
);
CHECK_EQ
(
get_as
<
float
>
(
x
),
123.
f
);
}
}
}
GIVEN
(
"config_values of null, URI, boolean, list or dictionary"
)
{
GIVEN
(
"config_values of null, URI, boolean, list or dictionary"
)
{
WHEN
(
"using get_as with integer types"
)
{
WHEN
(
"using get_as with integer types"
)
{
THEN
(
"conversion fails"
)
{
THEN
(
"conversion fails"
)
{
...
...
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