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
3f59e8d4
Unverified
Commit
3f59e8d4
authored
Dec 22, 2020
by
Noir
Committed by
GitHub
Dec 22, 2020
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1192
Add new get_as and get_or utility functions
parents
f5c450a6
3e1ae2ba
Changes
41
Hide whitespace changes
Inline
Side-by-side
Showing
41 changed files
with
1778 additions
and
320 deletions
+1778
-320
libcaf_core/caf/binary_deserializer.hpp
libcaf_core/caf/binary_deserializer.hpp
+3
-2
libcaf_core/caf/binary_serializer.hpp
libcaf_core/caf/binary_serializer.hpp
+5
-4
libcaf_core/caf/config_value.hpp
libcaf_core/caf/config_value.hpp
+352
-15
libcaf_core/caf/config_value_reader.hpp
libcaf_core/caf/config_value_reader.hpp
+11
-1
libcaf_core/caf/config_value_writer.hpp
libcaf_core/caf/config_value_writer.hpp
+1
-4
libcaf_core/caf/deserializer.hpp
libcaf_core/caf/deserializer.hpp
+7
-5
libcaf_core/caf/detail/bounds_checker.hpp
libcaf_core/caf/detail/bounds_checker.hpp
+7
-0
libcaf_core/caf/detail/parse.hpp
libcaf_core/caf/detail/parse.hpp
+32
-14
libcaf_core/caf/detail/print.hpp
libcaf_core/caf/detail/print.hpp
+8
-0
libcaf_core/caf/detail/serialized_size.hpp
libcaf_core/caf/detail/serialized_size.hpp
+1
-3
libcaf_core/caf/detail/stringification_inspector.hpp
libcaf_core/caf/detail/stringification_inspector.hpp
+1
-1
libcaf_core/caf/dictionary.hpp
libcaf_core/caf/dictionary.hpp
+0
-14
libcaf_core/caf/hash/fnv.hpp
libcaf_core/caf/hash/fnv.hpp
+1
-1
libcaf_core/caf/hash/sha1.hpp
libcaf_core/caf/hash/sha1.hpp
+1
-1
libcaf_core/caf/load_inspector.hpp
libcaf_core/caf/load_inspector.hpp
+9
-4
libcaf_core/caf/load_inspector_base.hpp
libcaf_core/caf/load_inspector_base.hpp
+2
-1
libcaf_core/caf/save_inspector.hpp
libcaf_core/caf/save_inspector.hpp
+8
-3
libcaf_core/caf/save_inspector_base.hpp
libcaf_core/caf/save_inspector_base.hpp
+2
-1
libcaf_core/caf/sec.hpp
libcaf_core/caf/sec.hpp
+6
-0
libcaf_core/caf/serializer.hpp
libcaf_core/caf/serializer.hpp
+4
-9
libcaf_core/caf/settings.hpp
libcaf_core/caf/settings.hpp
+3
-0
libcaf_core/caf/type_id.hpp
libcaf_core/caf/type_id.hpp
+9
-0
libcaf_core/caf/type_id_list.hpp
libcaf_core/caf/type_id_list.hpp
+23
-0
libcaf_core/caf/typed_actor.hpp
libcaf_core/caf/typed_actor.hpp
+5
-0
libcaf_core/src/binary_deserializer.cpp
libcaf_core/src/binary_deserializer.cpp
+14
-7
libcaf_core/src/binary_serializer.cpp
libcaf_core/src/binary_serializer.cpp
+0
-4
libcaf_core/src/config_value.cpp
libcaf_core/src/config_value.cpp
+388
-43
libcaf_core/src/config_value_reader.cpp
libcaf_core/src/config_value_reader.cpp
+49
-19
libcaf_core/src/config_value_writer.cpp
libcaf_core/src/config_value_writer.cpp
+3
-16
libcaf_core/src/detail/serialized_size.cpp
libcaf_core/src/detail/serialized_size.cpp
+1
-5
libcaf_core/src/detail/stringification_inspector.cpp
libcaf_core/src/detail/stringification_inspector.cpp
+3
-5
libcaf_core/src/message.cpp
libcaf_core/src/message.cpp
+18
-30
libcaf_core/src/sec_strings.cpp
libcaf_core/src/sec_strings.cpp
+12
-0
libcaf_core/src/settings.cpp
libcaf_core/src/settings.cpp
+2
-0
libcaf_core/src/tracing_data.cpp
libcaf_core/src/tracing_data.cpp
+8
-8
libcaf_core/src/type_id_list.cpp
libcaf_core/src/type_id_list.cpp
+9
-0
libcaf_core/test/config_value.cpp
libcaf_core/test/config_value.cpp
+747
-70
libcaf_core/test/core-test.hpp
libcaf_core/test/core-test.hpp
+12
-4
libcaf_core/test/detail/parser/read_config.cpp
libcaf_core/test/detail/parser/read_config.cpp
+4
-4
libcaf_core/test/load_inspector.cpp
libcaf_core/test/load_inspector.cpp
+1
-1
libcaf_core/test/save_inspector.cpp
libcaf_core/test/save_inspector.cpp
+6
-21
No files found.
libcaf_core/caf/binary_deserializer.hpp
View file @
3f59e8d4
...
...
@@ -35,7 +35,8 @@
namespace
caf
{
/// Deserializes objects from sequence of bytes.
/// Deserializes C++ objects from sequence of bytes. Does not perform run-time
/// type checks.
class
CAF_CORE_EXPORT
binary_deserializer
:
public
load_inspector_base
<
binary_deserializer
>
{
public:
...
...
@@ -112,7 +113,7 @@ public:
bool
fetch_next_object_type
(
type_id_t
&
type
)
noexcept
;
constexpr
bool
begin_object
(
string_view
)
noexcept
{
constexpr
bool
begin_object
(
type_id_t
,
string_view
)
noexcept
{
return
true
;
}
...
...
libcaf_core/caf/binary_serializer.hpp
View file @
3f59e8d4
...
...
@@ -33,7 +33,10 @@
namespace
caf
{
/// Serializes objects into a sequence of bytes.
/// Serializes C++ objects into a sequence of bytes.
/// @note The binary data format may change between CAF versions and does not
/// perform any type checking at run-time. Thus the output of this
/// serializer is unsuitable for persistence layers.
class
CAF_CORE_EXPORT
binary_serializer
:
public
save_inspector_base
<
binary_serializer
>
{
public:
...
...
@@ -95,9 +98,7 @@ public:
// -- interface functions ----------------------------------------------------
bool
inject_next_object_type
(
type_id_t
type
);
constexpr
bool
begin_object
(
string_view
)
{
constexpr
bool
begin_object
(
type_id_t
,
string_view
)
noexcept
{
return
true
;
}
...
...
libcaf_core/caf/config_value.hpp
View file @
3f59e8d4
...
...
@@ -19,6 +19,8 @@
#pragma once
#include <chrono>
#include <cmath>
#include <cstddef>
#include <cstdint>
#include <iosfwd>
#include <iterator>
...
...
@@ -36,8 +38,10 @@
#include "caf/detail/parse.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/dictionary.hpp"
#include "caf/expected.hpp"
#include "caf/fwd.hpp"
#include "caf/inspector_access.hpp"
#include "caf/inspector_access_type.hpp"
#include "caf/optional.hpp"
#include "caf/raise_error.hpp"
#include "caf/string_algorithms.hpp"
...
...
@@ -45,6 +49,7 @@
#include "caf/sum_type.hpp"
#include "caf/sum_type_access.hpp"
#include "caf/sum_type_token.hpp"
#include "caf/timespan.hpp"
#include "caf/timestamp.hpp"
#include "caf/uri.hpp"
#include "caf/variant.hpp"
...
...
@@ -56,6 +61,11 @@ namespace caf {
/// contain lists of themselves.
class
CAF_CORE_EXPORT
config_value
{
public:
// -- friends ----------------------------------------------------------------
template
<
class
T
>
friend
expected
<
T
>
get_as
(
const
config_value
&
value
);
// -- member types -----------------------------------------------------------
using
integer
=
int64_t
;
...
...
@@ -64,16 +74,14 @@ public:
using
real
=
double
;
using
timespan
=
caf
::
timespan
;
using
string
=
std
::
string
;
using
list
=
std
::
vector
<
config_value
>
;
using
dictionary
=
caf
::
dictionary
<
config_value
>
;
using
types
=
detail
::
type_list
<
integer
,
boolean
,
real
,
timespan
,
uri
,
string
,
list
,
dictionary
>
;
using
types
=
detail
::
type_list
<
none_t
,
integer
,
boolean
,
real
,
timespan
,
uri
,
string
,
list
,
dictionary
>
;
using
variant_type
=
detail
::
tl_apply_t
<
types
,
variant
>
;
...
...
@@ -106,23 +114,34 @@ public:
// -- parsing ----------------------------------------------------------------
/// Tries to parse a value from
`str`
.
/// Tries to parse a value from
given characters
.
static
expected
<
config_value
>
parse
(
string_view
::
iterator
first
,
string_view
::
iterator
last
);
/// Tries to parse a value from `str`.
static
expected
<
config_value
>
parse
(
string_view
str
);
/// Tries to parse a config value (list) from `str` and to convert it to an
/// allowed input message type for `Handle`.
template
<
class
Handle
>
static
optional
<
message
>
parse_msg
(
string_view
str
,
const
Handle
&
)
{
auto
allowed
=
Handle
::
allowed_inputs
();
return
parse_msg_impl
(
str
,
allowed
);
}
// -- properties -------------------------------------------------------------
/// Converts the value to a list with one element
. Does nothing if the
value
/// already is a list.
/// Converts the value to a list with one element
(unless the config
value
///
holds `nullptr`). Does nothing if the value
already is a list.
void
convert_to_list
();
/// Returns the value as a list, converting it to one if needed.
list
&
as_list
();
/// Returns the value as a dictionary, converting it to one if needed.
/// Returns the value as a dictionary, converting it to one if needed. The
/// only data structure that CAF can convert to a dictionary is a list of
/// lists, where each nested list contains exactly two elements (key and
/// value). In all other cases, the conversion results in an empty dictionary.
dictionary
&
as_dictionary
();
/// Appends `x` to a list. Converts this config value to a list first by
...
...
@@ -152,13 +171,80 @@ public:
return
&
data_
;
}
/// Checks whether this config value is not null.
explicit
operator
bool
()
const
noexcept
{
return
data_
.
index
()
!=
0
;
}
/// Checks whether this config value is null.
bool
operator
!
()
const
noexcept
{
return
data_
.
index
()
==
0
;
}
// -- utility ----------------------------------------------------------------
/// @private
type_id_t
type_id
()
const
noexcept
;
/// @private
error_code
<
sec
>
default_construct
(
type_id_t
);
/// @private
expected
<
bool
>
to_boolean
()
const
;
/// @private
expected
<
integer
>
to_integer
()
const
;
/// @private
expected
<
real
>
to_real
()
const
;
/// @private
expected
<
timespan
>
to_timespan
()
const
;
/// @private
expected
<
list
>
to_list
()
const
;
/// @private
expected
<
dictionary
>
to_dictionary
()
const
;
/// @private
bool
can_convert_to_dictionary
()
const
;
/// @private
template
<
class
T
,
class
Token
>
expected
<
T
>
convert_to
(
Token
token
)
const
{
auto
tmp
=
T
{};
config_value_reader
reader
{
this
};
if
(
detail
::
load
(
reader
,
tmp
,
token
))
return
{
std
::
move
(
tmp
)};
else
return
{
reader
.
move_error
()};
}
/// @experimental
template
<
class
T
>
error
assign
(
const
T
&
x
)
{
config_value_writer
writer
{
this
};
if
(
detail
::
save
(
writer
,
x
))
return
{};
else
return
{
writer
.
move_error
()};
}
private:
// -- properties -------------------------------------------------------------
static
const
char
*
type_name_at_index
(
size_t
index
)
noexcept
;
static
optional
<
message
>
parse_msg_impl
(
string_view
str
,
span
<
const
type_id_list
>
allowed_types
);
// -- auto conversion of related types ---------------------------------------
void
set
(
none_t
)
{
data_
=
none
;
}
void
set
(
bool
x
)
{
data_
=
x
;
}
...
...
@@ -217,6 +303,9 @@ private:
variant_type
data_
;
};
/// @relates config_value
CAF_CORE_EXPORT
std
::
string
to_string
(
const
config_value
&
x
);
// -- convenience constants ----------------------------------------------------
/// Type of the `top_level_cli_parsing` constant.
...
...
@@ -233,6 +322,231 @@ using nested_cli_parsing_t = std::true_type;
/// `parse_cli`.
constexpr
auto
nested_cli_parsing
=
nested_cli_parsing_t
{};
// -- conversion via get_as ----------------------------------------------------
template
<
class
T
>
expected
<
T
>
get_as
(
const
config_value
&
value
);
template
<
class
T
>
expected
<
T
>
get_as
(
const
config_value
&
,
inspector_access_type
::
none
)
{
static_assert
(
detail
::
always_false_v
<
T
>
,
"cannot convert to T: found no a suitable inspect overload"
);
}
template
<
class
T
>
expected
<
T
>
get_as
(
const
config_value
&
,
inspector_access_type
::
unsafe
)
{
static_assert
(
detail
::
always_false_v
<
T
>
,
"cannot convert types that are tagged as unsafe"
);
}
template
<
class
T
>
expected
<
T
>
get_as
(
const
config_value
&
x
,
inspector_access_type
::
specialization
token
)
{
return
x
.
convert_to
<
T
>
(
token
);
}
template
<
class
T
>
expected
<
T
>
get_as
(
const
config_value
&
x
,
inspector_access_type
::
inspect
token
)
{
return
x
.
convert_to
<
T
>
(
token
);
}
template
<
class
T
>
expected
<
T
>
get_as
(
const
config_value
&
x
,
inspector_access_type
::
builtin_inspect
token
)
{
return
x
.
convert_to
<
T
>
(
token
);
}
template
<
class
T
>
expected
<
T
>
get_as
(
const
config_value
&
x
,
inspector_access_type
::
builtin
)
{
if
constexpr
(
std
::
is_same
<
T
,
std
::
string
>::
value
)
{
return
to_string
(
x
);
}
else
if
constexpr
(
std
::
is_same
<
T
,
bool
>::
value
)
{
return
x
.
to_boolean
();
}
else
if
constexpr
(
std
::
is_integral
<
T
>::
value
)
{
if
(
auto
result
=
x
.
to_integer
())
{
if
(
detail
::
bounds_checker
<
T
>::
check
(
*
result
))
return
*
result
;
else
return
make_error
(
sec
::
conversion_failed
,
"narrowing error"
);
}
else
{
return
std
::
move
(
result
.
error
());
}
}
else
if
constexpr
(
std
::
is_floating_point
<
T
>::
value
)
{
if
(
auto
result
=
x
.
to_real
())
{
if
constexpr
(
sizeof
(
T
)
>=
sizeof
(
config_value
::
real
))
{
return
*
result
;
}
else
{
auto
narrowed
=
static_cast
<
T
>
(
*
result
);
if
(
!
std
::
isfinite
(
*
result
)
||
std
::
isfinite
(
narrowed
))
{
return
narrowed
;
}
else
{
return
make_error
(
sec
::
conversion_failed
,
"narrowing error"
);
}
}
}
else
{
return
std
::
move
(
result
.
error
());
}
}
else
{
static_assert
(
detail
::
always_false_v
<
T
>
,
"sorry, this conversion is not implemented yet"
);
}
}
template
<
class
T
>
expected
<
T
>
get_as
(
const
config_value
&
x
,
inspector_access_type
::
empty
)
{
// Technically, we could always simply return T{} here. However,
// *semantically* it only makes sense to converts dictionaries to objects. So
// at least we check for this condition here.
if
(
x
.
can_convert_to_dictionary
())
return
T
{};
else
return
make_error
(
sec
::
conversion_failed
,
"invalid element type: expected a dictionary"
);
}
template
<
class
T
,
size_t
...
Is
>
expected
<
T
>
get_as_tuple
(
const
config_value
::
list
&
x
,
std
::
index_sequence
<
Is
...
>
)
{
auto
boxed
=
std
::
make_tuple
(
get_as
<
std
::
tuple_element_t
<
Is
,
T
>>
(
x
[
Is
])...);
if
((
get
<
Is
>
(
boxed
)
&&
...))
return
T
{
std
::
move
(
*
get
<
Is
>
(
boxed
))...};
else
return
make_error
(
sec
::
conversion_failed
,
"invalid element types"
);
}
template
<
class
T
>
expected
<
T
>
get_as
(
const
config_value
&
x
,
inspector_access_type
::
tuple
)
{
static_assert
(
!
std
::
is_array
<
T
>::
value
,
"cannot return an array from a function"
);
if
(
auto
wrapped_values
=
x
.
to_list
())
{
static
constexpr
size_t
n
=
std
::
tuple_size
<
T
>::
value
;
if
(
wrapped_values
->
size
()
==
n
)
return
get_as_tuple
<
T
>
(
*
wrapped_values
,
std
::
make_index_sequence
<
n
>
{});
else
return
make_error
(
sec
::
conversion_failed
,
"wrong number of arguments"
);
}
else
{
return
{
std
::
move
(
wrapped_values
.
error
())};
}
}
template
<
class
T
>
expected
<
T
>
get_as
(
const
config_value
&
x
,
inspector_access_type
::
map
)
{
using
key_type
=
typename
T
::
key_type
;
using
mapped_type
=
typename
T
::
mapped_type
;
T
result
;
if
(
auto
dict
=
x
.
to_dictionary
())
{
for
(
auto
&&
[
string_key
,
wrapped_value
]
:
*
dict
)
{
config_value
wrapped_key
{
std
::
move
(
string_key
)};
if
(
auto
key
=
get_as
<
key_type
>
(
wrapped_key
))
{
if
(
auto
val
=
get_as
<
mapped_type
>
(
wrapped_value
))
{
if
(
!
result
.
emplace
(
std
::
move
(
*
key
),
std
::
move
(
*
val
)).
second
)
{
return
make_error
(
sec
::
conversion_failed
,
"ambiguous mapping of keys to key_type"
);
}
}
else
{
return
make_error
(
sec
::
conversion_failed
,
"failed to convert values to mapped_type"
);
}
}
else
{
return
make_error
(
sec
::
conversion_failed
,
"failed to convert keys to key_type"
);
}
}
return
{
std
::
move
(
result
)};
}
else
{
return
{
std
::
move
(
dict
.
error
())};
}
}
template
<
class
T
>
expected
<
T
>
get_as
(
const
config_value
&
x
,
inspector_access_type
::
list
)
{
if
(
auto
wrapped_values
=
x
.
to_list
())
{
using
value_type
=
typename
T
::
value_type
;
T
result
;
result
.
reserve
(
wrapped_values
->
size
());
for
(
const
auto
&
wrapped_value
:
*
wrapped_values
)
if
(
auto
maybe_value
=
get_as
<
value_type
>
(
wrapped_value
))
result
.
emplace_back
(
std
::
move
(
*
maybe_value
));
else
return
{
std
::
move
(
maybe_value
.
error
())};
return
{
std
::
move
(
result
)};
}
else
{
return
{
std
::
move
(
wrapped_values
.
error
())};
}
}
/// Converts a @ref config_value to builtin types or user-defined types that
/// opted into the type inspection API.
/// @relates config_value
template
<
class
T
>
expected
<
T
>
get_as
(
const
config_value
&
value
)
{
if
constexpr
(
std
::
is_same
<
T
,
timespan
>::
value
)
{
return
value
.
to_timespan
();
}
else
if
constexpr
(
std
::
is_same
<
T
,
config_value
::
list
>::
value
)
{
return
value
.
to_list
();
}
else
if
constexpr
(
std
::
is_same
<
T
,
config_value
::
dictionary
>::
value
)
{
return
value
.
to_dictionary
();
}
else
{
auto
token
=
inspect_access_type
<
config_value_reader
,
T
>
();
return
get_as
<
T
>
(
value
,
token
);
}
}
// -- conversion via get_or ----------------------------------------------------
/// Customization point for configuring automatic mappings from default value
/// types to deduced types. For example, `get_or(value, "foo"sv)` must return a
/// `string` rather than a `string_view`. However, user-defined overloads *must
/// not* specialize this class for any type from the namespaces `std` or `caf`.
template
<
class
T
>
struct
get_or_deduction_guide
{
using
value_type
=
T
;
template
<
class
V
>
static
decltype
(
auto
)
convert
(
V
&&
x
)
{
return
std
::
forward
<
V
>
(
x
);
}
};
template
<
>
struct
get_or_deduction_guide
<
string_view
>
{
using
value_type
=
std
::
string
;
static
value_type
convert
(
string_view
str
)
{
return
{
str
.
begin
(),
str
.
end
()};
}
};
template
<
class
T
>
struct
get_or_deduction_guide
<
span
<
T
>>
{
using
value_type
=
std
::
vector
<
T
>
;
static
value_type
convert
(
span
<
T
>
buf
)
{
return
{
buf
.
begin
(),
buf
.
end
()};
}
};
/// Configures @ref get_or to uses the @ref get_or_deduction_guide.
struct
get_or_auto_deduce
{};
/// Converts a @ref config_value to `To` or returns `fallback` if the conversion
/// fails.
/// @relates config_value
template
<
class
To
=
get_or_auto_deduce
,
class
Fallback
>
auto
get_or
(
const
config_value
&
x
,
Fallback
&&
fallback
)
{
if
constexpr
(
std
::
is_same
<
To
,
get_or_auto_deduce
>::
value
)
{
using
guide
=
get_or_deduction_guide
<
std
::
decay_t
<
Fallback
>>
;
using
value_type
=
typename
guide
::
value_type
;
if
(
auto
val
=
get_as
<
value_type
>
(
x
))
return
std
::
move
(
*
val
);
else
return
guide
::
convert
(
std
::
forward
<
Fallback
>
(
fallback
));
}
else
{
if
(
auto
val
=
get_as
<
To
>
(
x
))
return
std
::
move
(
*
val
);
else
return
To
{
std
::
forward
<
Fallback
>
(
fallback
)};
}
}
// -- SumType-like access ------------------------------------------------------
template
<
class
T
>
...
...
@@ -900,6 +1214,26 @@ struct sum_type_access<config_value> {
}
};
/// @relates config_value
inline
bool
operator
==
(
const
config_value
&
x
,
std
::
nullptr_t
)
noexcept
{
return
x
.
get_data
().
index
()
==
0
;
}
/// @relates config_value
inline
bool
operator
==
(
std
::
nullptr_t
,
const
config_value
&
x
)
noexcept
{
return
x
.
get_data
().
index
()
==
0
;
}
/// @relates config_value
inline
bool
operator
!=
(
const
config_value
&
x
,
std
::
nullptr_t
)
noexcept
{
return
x
.
get_data
().
index
()
!=
0
;
}
/// @relates config_value
inline
bool
operator
!=
(
std
::
nullptr_t
,
const
config_value
&
x
)
noexcept
{
return
x
.
get_data
().
index
()
!=
0
;
}
/// @relates config_value
CAF_CORE_EXPORT
bool
operator
<
(
const
config_value
&
x
,
const
config_value
&
y
);
...
...
@@ -916,9 +1250,6 @@ inline bool operator!=(const config_value& x, const config_value& y) {
return
!
(
x
==
y
);
}
/// @relates config_value
CAF_CORE_EXPORT
std
::
string
to_string
(
const
config_value
&
x
);
/// @relates config_value
CAF_CORE_EXPORT
std
::
ostream
&
operator
<<
(
std
::
ostream
&
out
,
const
config_value
&
x
);
...
...
@@ -998,10 +1329,11 @@ struct variant_inspector_traits<config_value> {
using
value_type
=
config_value
;
static
constexpr
type_id_t
allowed_types
[]
=
{
type_id_v
<
none_t
>
,
type_id_v
<
config_value
::
integer
>
,
type_id_v
<
config_value
::
boolean
>
,
type_id_v
<
config_value
::
real
>
,
type_id_v
<
config_value
::
timespan
>
,
type_id_v
<
timespan
>
,
type_id_v
<
uri
>
,
type_id_v
<
config_value
::
string
>
,
type_id_v
<
config_value
::
list
>
,
...
...
@@ -1019,7 +1351,7 @@ struct variant_inspector_traits<config_value> {
template
<
class
U
>
static
void
assign
(
value_type
&
x
,
U
&&
value
)
{
x
.
get_data
()
=
std
::
move
(
value
);
x
=
std
::
move
(
value
);
}
template
<
class
F
>
...
...
@@ -1027,6 +1359,11 @@ struct variant_inspector_traits<config_value> {
switch
(
type
)
{
default:
return
false
;
case
type_id_v
<
none_t
>
:
{
auto
tmp
=
config_value
{};
continuation
(
tmp
);
return
true
;
}
case
type_id_v
<
config_value
:
:
integer
>:
{
auto
tmp
=
config_value
::
integer
{};
continuation
(
tmp
);
...
...
@@ -1042,8 +1379,8 @@ struct variant_inspector_traits<config_value> {
continuation
(
tmp
);
return
true
;
}
case
type_id_v
<
config_value
:
:
timespan
>:
{
auto
tmp
=
config_value
::
timespan
{};
case
type_id_v
<
timespan
>
:
{
auto
tmp
=
timespan
{};
continuation
(
tmp
);
return
true
;
}
...
...
libcaf_core/caf/config_value_reader.hpp
View file @
3f59e8d4
...
...
@@ -23,6 +23,7 @@
#include "caf/dictionary.hpp"
#include "caf/fwd.hpp"
#include <memory>
#include <stack>
#include <vector>
...
...
@@ -85,6 +86,10 @@ public:
~
config_value_reader
()
override
;
config_value_reader
(
const
config_value_reader
&
)
=
delete
;
config_value_reader
&
operator
=
(
const
config_value_reader
&
)
=
delete
;
// -- stack access -----------------------------------------------------------
value_type
&
top
()
{
...
...
@@ -99,7 +104,7 @@ public:
bool
fetch_next_object_type
(
type_id_t
&
type
)
override
;
bool
begin_object
(
string_view
name
)
override
;
bool
begin_object
(
type_id_t
type
,
string_view
name
)
override
;
bool
end_object
()
override
;
...
...
@@ -166,9 +171,14 @@ public:
bool
value
(
span
<
byte
>
x
)
override
;
private:
// Sets `type` according to the `@type` field in `obj` or to the type ID of
// `settings` as fallback if no such field exists.
bool
fetch_object_type
(
const
settings
*
obj
,
type_id_t
&
type
);
stack_type
st_
;
// Stores on-the-fly converted values.
std
::
vector
<
std
::
unique_ptr
<
config_value
>>
scratch_space_
;
};
}
// namespace caf
libcaf_core/caf/config_value_writer.hpp
View file @
3f59e8d4
...
...
@@ -68,9 +68,7 @@ public:
// -- interface functions ----------------------------------------------------
bool
inject_next_object_type
(
type_id_t
type
)
override
;
bool
begin_object
(
string_view
name
)
override
;
bool
begin_object
(
type_id_t
type
,
string_view
name
)
override
;
bool
end_object
()
override
;
...
...
@@ -140,7 +138,6 @@ private:
bool
push
(
config_value
&&
x
);
stack_type
st_
;
string_view
type_hint_
;
};
}
// namespace caf
libcaf_core/caf/deserializer.hpp
View file @
3f59e8d4
...
...
@@ -62,13 +62,15 @@ public:
// -- interface functions ----------------------------------------------------
/// Reads run-time-type information for the next object. Requires that the
/// @ref serializer provided this information via
/// @ref serializer::inject_next_object_type.
/// Reads run-time-type information for the next object if available.
virtual
bool
fetch_next_object_type
(
type_id_t
&
type
)
=
0
;
/// Begins processing of an object.
virtual
bool
begin_object
(
string_view
type
)
=
0
;
/// Begins processing of an object, may perform a type check depending on the
/// data format.
/// @param type 16-bit ID for known types, @ref invalid_type_id otherwise.
/// @param pretty_class_name Either the output of @ref type_name_or_anonymous
/// or the optionally defined pretty name.
virtual
bool
begin_object
(
type_id_t
type
,
string_view
pretty_class_name
)
=
0
;
/// Ends processing of an object.
virtual
bool
end_object
()
=
0
;
...
...
libcaf_core/caf/detail/bounds_checker.hpp
View file @
3f59e8d4
...
...
@@ -33,6 +33,13 @@ struct bounds_checker {
}
};
template
<
>
struct
bounds_checker
<
int64_t
,
false
>
{
static
constexpr
bool
check
(
int64_t
)
noexcept
{
return
true
;
}
};
template
<
class
To
>
struct
bounds_checker
<
To
,
true
>
{
static
constexpr
bool
check
(
int64_t
x
)
noexcept
{
...
...
libcaf_core/caf/detail/parse.hpp
View file @
3f59e8d4
...
...
@@ -222,8 +222,15 @@ template <class First, class Second, size_t N>
void
parse_element
(
string_parser_state
&
ps
,
std
::
pair
<
First
,
Second
>&
kvp
,
const
char
(
&
char_blacklist
)[
N
]);
template
<
class
T
>
enable_if_tt
<
is_iterable
<
T
>>
parse
(
string_parser_state
&
ps
,
T
&
xs
)
{
struct
require_opening_char_t
{};
constexpr
auto
require_opening_char
=
require_opening_char_t
{};
struct
allow_omitting_opening_char_t
{};
constexpr
auto
allow_omitting_opening_char
=
allow_omitting_opening_char_t
{};
template
<
class
T
,
class
Policy
=
allow_omitting_opening_char_t
>
enable_if_tt
<
is_iterable
<
T
>>
parse
(
string_parser_state
&
ps
,
T
&
xs
,
Policy
=
{})
{
using
value_type
=
deconst_kvp_t
<
typename
T
::
value_type
>
;
static
constexpr
auto
is_map_type
=
is_pair
<
value_type
>::
value
;
static
constexpr
auto
opening_char
=
is_map_type
?
'{'
:
'['
;
...
...
@@ -252,19 +259,23 @@ enable_if_tt<is_iterable<T>> parse(string_parser_state& ps, T& xs) {
}
return
;
}
// An empty string simply results in an empty list/map.
if
(
ps
.
at_end
())
return
;
// List/map without [] or {}.
do
{
char
char_blacklist
[]
=
{
','
,
'\0'
};
value_type
tmp
;
parse_element
(
ps
,
tmp
,
char_blacklist
);
if
(
ps
.
code
>
pec
::
trailing_character
)
if
constexpr
(
std
::
is_same
<
Policy
,
require_opening_char_t
>::
value
)
{
ps
.
code
=
pec
::
unexpected_character
;
}
else
{
// An empty string simply results in an empty list/map.
if
(
ps
.
at_end
())
return
;
*
out
++
=
std
::
move
(
tmp
);
}
while
(
ps
.
consume
(
','
));
ps
.
code
=
ps
.
at_end
()
?
pec
::
success
:
pec
::
trailing_character
;
// List/map without [] or {}.
do
{
char
char_blacklist
[]
=
{
','
,
'\0'
};
value_type
tmp
;
parse_element
(
ps
,
tmp
,
char_blacklist
);
if
(
ps
.
code
>
pec
::
trailing_character
)
return
;
*
out
++
=
std
::
move
(
tmp
);
}
while
(
ps
.
consume
(
','
));
ps
.
code
=
ps
.
at_end
()
?
pec
::
success
:
pec
::
trailing_character
;
}
}
template
<
class
T
>
...
...
@@ -306,4 +317,11 @@ auto parse(string_view str, T& x) {
return
parse_result
(
ps
,
str
);
}
template
<
class
T
,
class
Policy
>
auto
parse
(
string_view
str
,
T
&
x
,
Policy
policy
)
{
string_parser_state
ps
{
str
.
begin
(),
str
.
end
()};
parse
(
ps
,
x
,
policy
);
return
parse_result
(
ps
,
str
);
}
}
// namespace caf::detail
libcaf_core/caf/detail/print.hpp
View file @
3f59e8d4
...
...
@@ -18,6 +18,7 @@
#pragma once
#include "caf/none.hpp"
#include "caf/string_view.hpp"
#include <chrono>
...
...
@@ -60,6 +61,13 @@ void print_escaped(Buffer& buf, string_view str) {
buf
.
push_back
(
'"'
);
}
template
<
class
Buffer
>
void
print
(
Buffer
&
buf
,
none_t
)
{
using
namespace
caf
::
literals
;
auto
str
=
"null"
_sv
;
buf
.
insert
(
buf
.
end
(),
str
.
begin
(),
str
.
end
());
}
template
<
class
Buffer
>
void
print
(
Buffer
&
buf
,
bool
x
)
{
using
namespace
caf
::
literals
;
...
...
libcaf_core/caf/detail/serialized_size.hpp
View file @
3f59e8d4
...
...
@@ -32,9 +32,7 @@ public:
size_t
result
=
0
;
bool
inject_next_object_type
(
type_id_t
type
)
override
;
bool
begin_object
(
string_view
)
override
;
bool
begin_object
(
type_id_t
,
string_view
)
override
;
bool
end_object
()
override
;
...
...
libcaf_core/caf/detail/stringification_inspector.hpp
View file @
3f59e8d4
...
...
@@ -58,7 +58,7 @@ public:
// -- serializer interface ---------------------------------------------------
bool
begin_object
(
string_view
name
);
bool
begin_object
(
type_id_t
,
string_view
name
);
bool
end_object
();
...
...
libcaf_core/caf/dictionary.hpp
View file @
3f59e8d4
...
...
@@ -330,14 +330,6 @@ private:
map_type
xs_
;
};
// -- free functions -----------------------------------------------------------
// @relates dictionary
template
<
class
T
>
std
::
string
to_string
(
const
dictionary
<
T
>&
xs
)
{
return
deep_to_string
(
xs
.
container
());
}
// -- operators ----------------------------------------------------------------
// @relates dictionary
...
...
@@ -376,10 +368,4 @@ bool operator>=(const dictionary<T>& xs, const dictionary<T>& ys) {
return
xs
.
container
()
>=
ys
.
container
();
}
// @relates dictionary
template
<
class
T
>
std
::
ostream
&
operator
<<
(
std
::
ostream
&
out
,
const
dictionary
<
T
>&
xs
)
{
return
out
<<
to_string
(
xs
);
}
}
// namespace caf
libcaf_core/caf/hash/fnv.hpp
View file @
3f59e8d4
...
...
@@ -53,7 +53,7 @@ public:
return
false
;
}
constexpr
bool
begin_object
(
string_view
)
{
constexpr
bool
begin_object
(
type_id_t
,
string_view
)
{
return
true
;
}
...
...
libcaf_core/caf/hash/sha1.hpp
View file @
3f59e8d4
...
...
@@ -49,7 +49,7 @@ public:
return
false
;
}
constexpr
bool
begin_object
(
string_view
)
{
constexpr
bool
begin_object
(
type_id_t
,
string_view
)
{
return
true
;
}
...
...
libcaf_core/caf/load_inspector.hpp
View file @
3f59e8d4
...
...
@@ -275,6 +275,7 @@ public:
template
<
class
Inspector
,
class
LoadCallback
>
struct
object_with_load_callback_t
{
type_id_t
object_type
;
string_view
object_name
;
Inspector
*
f
;
LoadCallback
load_callback
;
...
...
@@ -282,7 +283,7 @@ public:
template
<
class
...
Fields
>
bool
fields
(
Fields
&&
...
fs
)
{
using
load_callback_result
=
decltype
(
load_callback
());
if
(
!
(
f
->
begin_object
(
object_name
)
&&
(
fs
(
*
f
)
&&
...)))
if
(
!
(
f
->
begin_object
(
object_
type
,
object_
name
)
&&
(
fs
(
*
f
)
&&
...)))
return
false
;
if
constexpr
(
std
::
is_same
<
load_callback_result
,
bool
>::
value
)
{
if
(
!
load_callback
())
{
...
...
@@ -299,7 +300,7 @@ public:
}
auto
pretty_name
(
string_view
name
)
&&
{
return
object_t
{
name
,
f
};
return
object_t
{
object_type
,
name
,
f
};
}
template
<
class
F
>
...
...
@@ -310,16 +311,19 @@ public:
template
<
class
Inspector
>
struct
object_t
{
type_id_t
object_type
;
string_view
object_name
;
Inspector
*
f
;
template
<
class
...
Fields
>
bool
fields
(
Fields
&&
...
fs
)
{
return
f
->
begin_object
(
object_name
)
&&
(
fs
(
*
f
)
&&
...)
&&
f
->
end_object
();
return
f
->
begin_object
(
object_type
,
object_name
)
//
&&
(
fs
(
*
f
)
&&
...)
//
&&
f
->
end_object
();
}
auto
pretty_name
(
string_view
name
)
&&
{
return
object_t
{
name
,
f
};
return
object_t
{
object_type
,
name
,
f
};
}
template
<
class
F
>
...
...
@@ -330,6 +334,7 @@ public:
template
<
class
F
>
auto
on_load
(
F
fun
)
&&
{
return
object_with_load_callback_t
<
Inspector
,
F
>
{
object_type
,
object_name
,
f
,
std
::
move
(
fun
),
...
...
libcaf_core/caf/load_inspector_base.hpp
View file @
3f59e8d4
...
...
@@ -38,7 +38,8 @@ public:
template
<
class
T
>
constexpr
auto
object
(
T
&
)
noexcept
{
return
super
::
object_t
<
Subtype
>
{
type_name_or_anonymous
<
T
>
(),
dptr
()};
return
super
::
object_t
<
Subtype
>
{
type_id_or_invalid
<
T
>
(),
type_name_or_anonymous
<
T
>
(),
dptr
()};
}
template
<
class
T
>
...
...
libcaf_core/caf/save_inspector.hpp
View file @
3f59e8d4
...
...
@@ -171,6 +171,7 @@ public:
template
<
class
Inspector
,
class
SaveCallback
>
struct
object_with_save_callback_t
{
type_id_t
object_type
;
string_view
object_name
;
Inspector
*
f
;
SaveCallback
save_callback
;
...
...
@@ -178,7 +179,7 @@ public:
template
<
class
...
Fields
>
bool
fields
(
Fields
&&
...
fs
)
{
using
save_callback_result
=
decltype
(
save_callback
());
if
(
!
(
f
->
begin_object
(
object_name
)
&&
(
fs
(
*
f
)
&&
...)))
if
(
!
(
f
->
begin_object
(
object_
type
,
object_
name
)
&&
(
fs
(
*
f
)
&&
...)))
return
false
;
if
constexpr
(
std
::
is_same
<
save_callback_result
,
bool
>::
value
)
{
if
(
!
save_callback
())
{
...
...
@@ -206,16 +207,19 @@ public:
template
<
class
Inspector
>
struct
object_t
{
type_id_t
object_type
;
string_view
object_name
;
Inspector
*
f
;
template
<
class
...
Fields
>
bool
fields
(
Fields
&&
...
fs
)
{
return
f
->
begin_object
(
object_name
)
&&
(
fs
(
*
f
)
&&
...)
&&
f
->
end_object
();
return
f
->
begin_object
(
object_type
,
object_name
)
//
&&
(
fs
(
*
f
)
&&
...)
//
&&
f
->
end_object
();
}
auto
pretty_name
(
string_view
name
)
&&
{
return
object_t
{
name
,
f
};
return
object_t
{
object_type
,
name
,
f
};
}
template
<
class
F
>
...
...
@@ -226,6 +230,7 @@ public:
template
<
class
F
>
auto
on_save
(
F
fun
)
&&
{
return
object_with_save_callback_t
<
Inspector
,
F
>
{
object_type
,
object_name
,
f
,
std
::
move
(
fun
),
...
...
libcaf_core/caf/save_inspector_base.hpp
View file @
3f59e8d4
...
...
@@ -34,7 +34,8 @@ public:
template
<
class
T
>
constexpr
auto
object
(
T
&
)
noexcept
{
return
super
::
object_t
<
Subtype
>
{
type_name_or_anonymous
<
T
>
(),
dptr
()};
return
super
::
object_t
<
Subtype
>
{
type_id_or_invalid
<
T
>
(),
type_name_or_anonymous
<
T
>
(),
dptr
()};
}
template
<
class
T
>
...
...
libcaf_core/caf/sec.hpp
View file @
3f59e8d4
...
...
@@ -164,6 +164,12 @@ enum class sec : uint8_t {
conversion_failed
,
/// A network connection was closed by the remote side.
connection_closed
,
/// An operation failed because run-time type information diverged from the
/// expected type.
type_clash
,
/// An operation failed because the callee does not implement this
/// functionality.
unsupported_operation
,
};
/// @relates sec
...
...
libcaf_core/caf/serializer.hpp
View file @
3f59e8d4
...
...
@@ -66,15 +66,10 @@ public:
// -- interface functions ----------------------------------------------------
/// Injects run-time-type information for the *next* object, i.e., causes the
/// next call to `begin_object` to write additional meta information. Allows a
/// @ref deserializer to retrieve the type for the next object via
/// @ref deserializer::fetch_next_object_type.
virtual
bool
inject_next_object_type
(
type_id_t
type
)
=
0
;
/// Begins processing of an object. Saves the type information
/// to the underlying storage.
virtual
bool
begin_object
(
string_view
name
)
=
0
;
/// Begins processing of an object. May save the type information to the
/// underlying storage to allow a @ref deserializer to retrieve and check the
/// type information for data formats that provide deserialization.
virtual
bool
begin_object
(
type_id_t
type
,
string_view
name
)
=
0
;
/// Ends processing of an object.
virtual
bool
end_object
()
=
0
;
...
...
libcaf_core/caf/settings.hpp
View file @
3f59e8d4
...
...
@@ -33,6 +33,9 @@ namespace caf {
/// @relates config_value
using
settings
=
dictionary
<
config_value
>
;
/// @relates config_value
CAF_CORE_EXPORT
std
::
string
to_string
(
const
settings
&
xs
);
/// Tries to retrieve the value associated to `name` from `xs`.
/// @relates config_value
CAF_CORE_EXPORT
const
config_value
*
...
...
libcaf_core/caf/type_id.hpp
View file @
3f59e8d4
...
...
@@ -108,6 +108,15 @@ string_view type_name_or_anonymous() {
return
"anonymous"
;
}
/// Returns `type_id_v<T>` if available, `invalid_type_id` otherwise.
template
<
class
T
>
type_id_t
type_id_or_invalid
()
{
if
constexpr
(
detail
::
is_complete
<
type_id
<
T
>>
)
return
type_id
<
T
>::
value
;
else
return
invalid_type_id
;
}
/// Returns the type name of given `type` or an empty string if `type` is an
/// invalid ID.
CAF_CORE_EXPORT
string_view
query_type_name
(
type_id_t
type
);
...
...
libcaf_core/caf/type_id_list.hpp
View file @
3f59e8d4
...
...
@@ -88,6 +88,10 @@ public:
return
begin
()
+
size
();
}
/// Returns the number of bytes that a buffer needs to allocate for storing a
/// type-erased tuple for the element types stored in this list.
size_t
data_size
()
const
noexcept
;
private:
pointer
data_
;
};
...
...
@@ -110,3 +114,22 @@ constexpr type_id_list make_type_id_list() {
CAF_CORE_EXPORT
std
::
string
to_string
(
type_id_list
xs
);
}
// namespace caf
namespace
caf
::
detail
{
template
<
class
F
>
struct
argument_type_id_list_factory
;
template
<
class
R
,
class
...
Ts
>
struct
argument_type_id_list_factory
<
R
(
Ts
...)
>
{
static
type_id_list
make
()
{
return
make_type_id_list
<
Ts
...
>
();
}
};
template
<
class
F
>
type_id_list
make_argument_type_id_list
()
{
return
argument_type_id_list_factory
<
F
>::
make
();
}
}
// namespace caf::detail
libcaf_core/caf/typed_actor.hpp
View file @
3f59e8d4
...
...
@@ -31,6 +31,7 @@
#include "caf/make_actor.hpp"
#include "caf/replies_to.hpp"
#include "caf/stateful_actor.hpp"
#include "caf/type_id_list.hpp"
#include "caf/typed_actor_view_base.hpp"
#include "caf/typed_behavior.hpp"
#include "caf/typed_response_promise.hpp"
...
...
@@ -246,6 +247,10 @@ public:
x
.
ptr_
.
reset
();
}
static
std
::
array
<
type_id_list
,
sizeof
...(
Sigs
)
>
allowed_inputs
()
{
return
{{
detail
::
make_argument_type_id_list
<
Sigs
>
()...}};
}
/// @endcond
private:
...
...
libcaf_core/src/binary_deserializer.cpp
View file @
3f59e8d4
...
...
@@ -35,19 +35,23 @@ namespace {
template
<
class
T
>
bool
int_value
(
binary_deserializer
&
source
,
T
&
x
)
{
auto
tmp
=
std
::
make_unsigned_t
<
T
>
{};
if
(
!
source
.
value
(
as_writable_bytes
(
make_span
(
&
tmp
,
1
))))
if
(
source
.
value
(
as_writable_bytes
(
make_span
(
&
tmp
,
1
))))
{
x
=
static_cast
<
T
>
(
detail
::
from_network_order
(
tmp
));
return
true
;
}
else
{
return
false
;
x
=
static_cast
<
T
>
(
detail
::
from_network_order
(
tmp
));
return
true
;
}
}
template
<
class
T
>
bool
float_value
(
binary_deserializer
&
source
,
T
&
x
)
{
auto
tmp
=
typename
detail
::
ieee_754_trait
<
T
>::
packed_type
{};
if
(
!
int_value
(
source
,
tmp
))
if
(
int_value
(
source
,
tmp
))
{
x
=
detail
::
unpack754
(
tmp
);
return
true
;
}
else
{
return
false
;
x
=
detail
::
unpack754
(
tmp
);
return
true
;
}
}
// Does not perform any range checks.
...
...
@@ -67,7 +71,10 @@ binary_deserializer::binary_deserializer(actor_system& sys) noexcept
}
bool
binary_deserializer
::
fetch_next_object_type
(
type_id_t
&
type
)
noexcept
{
return
value
(
type
);
type
=
invalid_type_id
;
emplace_error
(
sec
::
unsupported_operation
,
"the default binary format does not embed type information"
);
return
false
;
}
bool
binary_deserializer
::
begin_sequence
(
size_t
&
list_size
)
noexcept
{
...
...
libcaf_core/src/binary_serializer.cpp
View file @
3f59e8d4
...
...
@@ -51,10 +51,6 @@ void binary_serializer::skip(size_t num_bytes) {
write_pos_
+=
num_bytes
;
}
bool
binary_serializer
::
inject_next_object_type
(
type_id_t
type
)
{
return
value
(
type
);
}
bool
binary_serializer
::
begin_sequence
(
size_t
list_size
)
{
// Use varbyte encoding to compress sequence size on the wire.
// For 64-bit values, the encoded representation cannot get larger than 10
...
...
libcaf_core/src/config_value.cpp
View file @
3f59e8d4
...
...
@@ -19,31 +19,48 @@
#include "caf/config_value.hpp"
#include <cctype>
#include <cmath>
#include <cstdlib>
#include <ostream>
#include "caf/deep_to_string.hpp"
#include "caf/detail/config_consumer.hpp"
#include "caf/detail/meta_object.hpp"
#include "caf/detail/overload.hpp"
#include "caf/detail/parse.hpp"
#include "caf/detail/parser/read_config.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/expected.hpp"
#include "caf/parser_state.hpp"
#include "caf/pec.hpp"
#include "caf/settings.hpp"
#include "caf/string_view.hpp"
namespace
caf
{
namespace
{
const
char
*
type_names
[]
{
"integer"
,
"boolean"
,
"real"
,
"timespan"
,
"uri"
,
"string"
,
"list"
,
"dictionary"
,
};
const
char
*
type_names
[]
=
{
"none"
,
"integer"
,
"boolean"
,
"real"
,
"timespan"
,
"uri"
,
"string"
,
"list"
,
"dictionary"
};
template
<
class
To
,
class
From
>
auto
no_conversion
()
{
return
[](
const
From
&
)
{
std
::
string
msg
=
"cannot convert "
;
msg
+=
type_names
[
detail
::
tl_index_of
<
config_value
::
types
,
From
>::
value
];
msg
+=
" to "
;
msg
+=
type_names
[
detail
::
tl_index_of
<
config_value
::
types
,
To
>::
value
];
auto
err
=
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
));
return
expected
<
To
>
{
std
::
move
(
err
)};
};
}
template
<
class
To
,
class
...
From
>
auto
no_conversions
()
{
return
detail
::
make_overload
(
no_conversion
<
To
,
From
>
()...);
}
}
// namespace
...
...
@@ -94,12 +111,16 @@ expected<config_value> config_value::parse(string_view str) {
// -- properties ---------------------------------------------------------------
void
config_value
::
convert_to_list
()
{
if
(
holds_alternative
<
list
>
(
data_
))
return
;
using
std
::
swap
;
config_value
tmp
;
swap
(
*
this
,
tmp
);
data_
=
std
::
vector
<
config_value
>
{
std
::
move
(
tmp
)};
if
(
holds_alternative
<
list
>
(
data_
))
{
;
// nop
}
else
if
(
holds_alternative
<
none_t
>
(
data_
))
{
data_
=
config_value
::
list
{};
}
else
{
using
std
::
swap
;
config_value
tmp
;
swap
(
*
this
,
tmp
);
data_
=
config_value
::
list
{
std
::
move
(
tmp
)};
}
}
config_value
::
list
&
config_value
::
as_list
()
{
...
...
@@ -108,9 +129,15 @@ config_value::list& config_value::as_list() {
}
config_value
::
dictionary
&
config_value
::
as_dictionary
()
{
if
(
!
holds_alternative
<
dictionary
>
(
*
this
))
*
this
=
dictionary
{};
return
get
<
dictionary
>
(
*
this
);
if
(
auto
dict
=
get_if
<
config_value
::
dictionary
>
(
&
data_
))
{
return
*
dict
;
}
else
if
(
auto
lifted
=
to_dictionary
())
{
data_
=
std
::
move
(
*
lifted
);
return
get
<
config_value
::
dictionary
>
(
data_
);
}
else
{
data_
=
config_value
::
dictionary
{};
return
get
<
config_value
::
dictionary
>
(
data_
);
}
}
void
config_value
::
append
(
config_value
x
)
{
...
...
@@ -126,6 +153,304 @@ const char* config_value::type_name_at_index(size_t index) noexcept {
return
type_names
[
index
];
}
// -- utility ------------------------------------------------------------------
type_id_t
config_value
::
type_id
()
const
noexcept
{
static
constexpr
type_id_t
allowed_types
[]
=
{
type_id_v
<
none_t
>
,
type_id_v
<
config_value
::
integer
>
,
type_id_v
<
config_value
::
boolean
>
,
type_id_v
<
config_value
::
real
>
,
type_id_v
<
timespan
>
,
type_id_v
<
uri
>
,
type_id_v
<
config_value
::
string
>
,
type_id_v
<
config_value
::
list
>
,
type_id_v
<
config_value
::
dictionary
>
,
};
return
allowed_types
[
data_
.
index
()];
}
error_code
<
sec
>
config_value
::
default_construct
(
type_id_t
id
)
{
switch
(
id
)
{
case
type_id_v
<
bool
>
:
set
(
false
);
return
sec
::
none
;
case
type_id_v
<
double
>
:
case
type_id_v
<
float
>
:
case
type_id_v
<
long
double
>
:
set
(
0.0
);
return
sec
::
none
;
case
type_id_v
<
int16_t
>
:
case
type_id_v
<
int32_t
>
:
case
type_id_v
<
int64_t
>
:
case
type_id_v
<
int8_t
>
:
case
type_id_v
<
uint16_t
>
:
case
type_id_v
<
uint32_t
>
:
case
type_id_v
<
uint64_t
>
:
case
type_id_v
<
uint8_t
>
:
set
(
0
);
return
sec
::
none
;
case
type_id_v
<
std
:
:
string
>:
set
(
std
::
string
{});
return
sec
::
none
;
case
type_id_v
<
timespan
>
:
set
(
timespan
{});
return
sec
::
none
;
case
type_id_v
<
uri
>
:
set
(
uri
{});
return
sec
::
none
;
default:
if
(
auto
meta
=
detail
::
global_meta_object
(
id
))
{
auto
ptr
=
malloc
(
meta
->
padded_size
);
auto
free_guard
=
detail
::
make_scope_guard
([
ptr
]
{
free
(
ptr
);
});
meta
->
default_construct
(
ptr
);
auto
destroy_guard
=
detail
::
make_scope_guard
([
=
]
{
meta
->
destroy
(
ptr
);
});
config_value_writer
writer
{
this
};
if
(
meta
->
save
(
writer
,
ptr
))
{
return
sec
::
none
;
}
else
{
auto
&
err
=
writer
.
get_error
();
if
(
err
.
category
()
==
type_id_v
<
sec
>
)
return
static_cast
<
sec
>
(
err
.
code
());
else
return
sec
::
conversion_failed
;
}
}
else
{
return
sec
::
unknown_type
;
}
}
}
expected
<
bool
>
config_value
::
to_boolean
()
const
{
using
result_type
=
expected
<
bool
>
;
auto
f
=
detail
::
make_overload
(
no_conversions
<
bool
,
none_t
,
integer
,
real
,
timespan
,
uri
,
config_value
::
list
,
config_value
::
dictionary
>
(),
[](
boolean
x
)
{
return
result_type
{
x
};
},
[](
const
std
::
string
&
x
)
{
if
(
x
==
"true"
)
{
return
result_type
{
true
};
}
else
if
(
x
==
"false"
)
{
return
result_type
{
false
};
}
else
{
std
::
string
msg
=
"cannot convert "
;
detail
::
print_escaped
(
msg
,
x
);
msg
+=
" to a boolean"
;
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
}
});
return
visit
(
f
,
data_
);
}
expected
<
config_value
::
integer
>
config_value
::
to_integer
()
const
{
using
result_type
=
expected
<
integer
>
;
auto
f
=
detail
::
make_overload
(
no_conversions
<
integer
,
none_t
,
bool
,
timespan
,
uri
,
config_value
::
list
,
config_value
::
dictionary
>
(),
[](
integer
x
)
{
return
result_type
{
x
};
},
[](
real
x
)
{
using
limits
=
std
::
numeric_limits
<
config_value
::
integer
>
;
if
(
std
::
isfinite
(
x
)
// never convert NaN & friends
&&
std
::
fmod
(
x
,
1.0
)
==
0.0
// only convert whole numbers
&&
x
<=
static_cast
<
config_value
::
real
>
(
limits
::
max
())
&&
x
>=
static_cast
<
config_value
::
real
>
(
limits
::
min
()))
{
return
result_type
{
static_cast
<
config_value
::
integer
>
(
x
)};
}
else
{
auto
err
=
make_error
(
sec
::
conversion_failed
,
"cannot convert decimal or out-of-bounds real number to an integer"
);
return
result_type
{
std
::
move
(
err
)};
}
},
[](
const
std
::
string
&
x
)
{
auto
tmp_int
=
config_value
::
integer
{
0
};
if
(
detail
::
parse
(
x
,
tmp_int
)
==
none
)
return
result_type
{
tmp_int
};
auto
tmp_real
=
0.0
;
if
(
detail
::
parse
(
x
,
tmp_real
)
==
none
)
if
(
auto
ival
=
config_value
{
tmp_real
}.
to_integer
())
return
result_type
{
*
ival
};
std
::
string
msg
=
"cannot convert "
;
detail
::
print_escaped
(
msg
,
x
);
msg
+=
" to an integer"
;
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
});
return
visit
(
f
,
data_
);
}
expected
<
config_value
::
real
>
config_value
::
to_real
()
const
{
using
result_type
=
expected
<
real
>
;
auto
f
=
detail
::
make_overload
(
no_conversions
<
real
,
none_t
,
bool
,
timespan
,
uri
,
config_value
::
list
,
config_value
::
dictionary
>
(),
[](
integer
x
)
{
// 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
// behavior. So we rather always convert, even if it costs precision.
return
result_type
{
static_cast
<
real
>
(
x
)};
},
[](
real
x
)
{
return
result_type
{
x
};
},
[](
const
std
::
string
&
x
)
{
auto
tmp
=
0.0
;
if
(
detail
::
parse
(
x
,
tmp
)
==
none
)
return
result_type
{
tmp
};
std
::
string
msg
=
"cannot convert "
;
detail
::
print_escaped
(
msg
,
x
);
msg
+=
" to a floating point number"
;
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
});
return
visit
(
f
,
data_
);
}
expected
<
timespan
>
config_value
::
to_timespan
()
const
{
using
result_type
=
expected
<
timespan
>
;
auto
f
=
detail
::
make_overload
(
no_conversions
<
timespan
,
none_t
,
bool
,
integer
,
real
,
uri
,
config_value
::
list
,
config_value
::
dictionary
>
(),
[](
timespan
x
)
{
return
result_type
{
x
};
},
[](
const
std
::
string
&
x
)
{
auto
tmp
=
timespan
{};
if
(
detail
::
parse
(
x
,
tmp
)
==
none
)
return
result_type
{
tmp
};
std
::
string
msg
=
"cannot convert "
;
detail
::
print_escaped
(
msg
,
x
);
msg
+=
" to a timespan"
;
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
});
return
visit
(
f
,
data_
);
}
expected
<
config_value
::
list
>
config_value
::
to_list
()
const
{
using
result_type
=
expected
<
list
>
;
auto
dict_to_list
=
[](
const
dictionary
&
dict
,
list
&
result
)
{
for
(
const
auto
&
[
key
,
val
]
:
dict
)
{
list
kvp
;
kvp
.
reserve
(
2
);
kvp
.
emplace_back
(
key
);
kvp
.
emplace_back
(
val
);
result
.
emplace_back
(
std
::
move
(
kvp
));
}
};
auto
f
=
detail
::
make_overload
(
no_conversions
<
list
,
none_t
,
bool
,
integer
,
real
,
timespan
,
uri
>
(),
[
dict_to_list
](
const
std
::
string
&
x
)
{
// Check whether we can parse the string as a list. If that fails, try
// whether we can parse it as a dictionary instead (and then convert that
// to a list).
config_value
::
list
tmp
;
if
(
detail
::
parse
(
x
,
tmp
,
detail
::
require_opening_char
)
==
none
)
return
result_type
{
std
::
move
(
tmp
)};
config_value
::
dictionary
dict
;
if
(
detail
::
parse
(
x
,
dict
,
detail
::
require_opening_char
)
==
none
)
{
tmp
.
clear
();
dict_to_list
(
dict
,
tmp
);
return
result_type
{
std
::
move
(
tmp
)};
}
std
::
string
msg
=
"cannot convert "
;
detail
::
print_escaped
(
msg
,
x
);
msg
+=
" to a list"
;
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
},
[](
const
list
&
x
)
{
return
result_type
{
x
};
},
[
dict_to_list
](
const
dictionary
&
x
)
{
list
tmp
;
dict_to_list
(
x
,
tmp
);
return
result_type
{
std
::
move
(
tmp
)};
});
return
visit
(
f
,
data_
);
}
expected
<
config_value
::
dictionary
>
config_value
::
to_dictionary
()
const
{
using
result_type
=
expected
<
dictionary
>
;
auto
f
=
detail
::
make_overload
(
no_conversions
<
dictionary
,
none_t
,
bool
,
integer
,
timespan
,
real
,
uri
>
(),
[](
const
list
&
x
)
{
dictionary
tmp
;
auto
lift
=
[
&
tmp
](
const
config_value
&
element
)
{
auto
ls
=
element
.
to_list
();
if
(
ls
&&
ls
->
size
()
==
2
)
return
tmp
.
emplace
(
to_string
((
*
ls
)[
0
]),
std
::
move
((
*
ls
)[
1
])).
second
;
else
return
false
;
};
if
(
std
::
all_of
(
x
.
begin
(),
x
.
end
(),
lift
))
{
return
result_type
{
std
::
move
(
tmp
)};
}
else
{
auto
err
=
make_error
(
sec
::
conversion_failed
,
"cannot convert list to dictionary unless each "
"element in the list is a key-value pair"
);
return
result_type
{
std
::
move
(
err
)};
}
},
[](
const
std
::
string
&
x
)
{
if
(
dictionary
tmp
;
detail
::
parse
(
x
,
tmp
)
==
none
)
{
return
result_type
{
std
::
move
(
tmp
)};
}
if
(
list
tmp
;
detail
::
parse
(
x
,
tmp
)
==
none
)
{
config_value
ls
{
std
::
move
(
tmp
)};
if
(
auto
res
=
ls
.
to_dictionary
())
return
res
;
}
std
::
string
msg
=
"cannot convert "
;
detail
::
print_escaped
(
msg
,
x
);
msg
+=
" to a dictionary"
;
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
},
[](
const
dictionary
&
x
)
{
return
result_type
{
x
};
});
return
visit
(
f
,
data_
);
}
bool
config_value
::
can_convert_to_dictionary
()
const
{
auto
f
=
detail
::
make_overload
(
//
[](
const
auto
&
)
{
return
false
;
},
[
this
](
const
std
::
string
&
)
{
// TODO: implement some dry-run mode and use it here to avoid creating an
// actual dictionary only to throw it away.
auto
maybe_dict
=
to_dictionary
();
return
static_cast
<
bool
>
(
maybe_dict
);
},
[](
const
dictionary
&
)
{
return
true
;
});
return
visit
(
f
,
data_
);
}
optional
<
message
>
config_value
::
parse_msg_impl
(
string_view
str
,
span
<
const
type_id_list
>
allowed_types
)
{
if
(
auto
val
=
parse
(
str
))
{
auto
ls_size
=
val
->
as_list
().
size
();
message
result
;
auto
converts
=
[
&
val
,
&
result
,
ls_size
](
type_id_list
ls
)
{
if
(
ls
.
size
()
!=
ls_size
)
return
false
;
config_value_reader
reader
{
std
::
addressof
(
*
val
)};
auto
unused
=
size_t
{
0
};
reader
.
begin_sequence
(
unused
);
CAF_ASSERT
(
unused
==
ls_size
);
intrusive_ptr
<
detail
::
message_data
>
ptr
;
if
(
auto
vptr
=
malloc
(
sizeof
(
detail
::
message_data
)
+
ls
.
data_size
()))
ptr
.
reset
(
new
(
vptr
)
detail
::
message_data
(
ls
),
false
);
else
return
false
;
auto
pos
=
ptr
->
storage
();
for
(
auto
type
:
ls
)
{
auto
meta
=
detail
::
global_meta_object
(
type
);
CAF_ASSERT
(
meta
!=
nullptr
);
meta
->
default_construct
(
pos
);
ptr
->
inc_constructed_elements
();
if
(
!
meta
->
load
(
reader
,
pos
))
return
false
;
pos
+=
meta
->
padded_size
;
}
result
.
reset
(
ptr
.
release
(),
false
);
return
reader
.
end_sequence
();
};
if
(
std
::
any_of
(
allowed_types
.
begin
(),
allowed_types
.
end
(),
converts
))
return
{
std
::
move
(
result
)};
}
return
{};
}
// -- related free functions ---------------------------------------------------
bool
operator
<
(
const
config_value
&
x
,
const
config_value
&
y
)
{
...
...
@@ -143,10 +468,17 @@ void to_string_impl(std::string& str, const config_value& x);
struct
to_string_visitor
{
std
::
string
&
str
;
void
operator
()(
const
std
::
string
&
x
)
{
detail
::
print_escaped
(
str
,
x
);
}
template
<
class
T
>
void
operator
()(
const
T
&
x
)
{
detail
::
stringification_inspector
f
{
str
};
f
.
value
(
x
);
detail
::
print
(
str
,
x
);
}
void
operator
()(
none_t
)
{
str
+=
"null"
;
}
void
operator
()(
const
uri
&
x
)
{
...
...
@@ -155,36 +487,38 @@ struct to_string_visitor {
}
void
operator
()(
const
config_value
::
list
&
xs
)
{
if
(
xs
.
empty
())
{
str
+=
"[]"
;
return
;
}
str
+=
'['
;
auto
i
=
xs
.
begin
();
to_string_impl
(
str
,
*
i
);
for
(
++
i
;
i
!=
xs
.
end
();
++
i
)
{
str
+=
", "
;
if
(
!
xs
.
empty
())
{
auto
i
=
xs
.
begin
();
to_string_impl
(
str
,
*
i
);
for
(
++
i
;
i
!=
xs
.
end
();
++
i
)
{
str
+=
", "
;
to_string_impl
(
str
,
*
i
);
}
}
str
+=
']'
;
}
void
append_key
(
const
std
::
string
&
key
)
{
if
(
std
::
all_of
(
key
.
begin
(),
key
.
end
(),
::
isalnum
))
str
.
append
(
key
.
begin
(),
key
.
end
());
else
(
*
this
)(
key
);
}
void
operator
()(
const
config_value
::
dictionary
&
xs
)
{
if
(
xs
.
empty
())
{
str
+=
"{}"
;
return
;
}
detail
::
stringification_inspector
f
{
str
};
str
+=
'{'
;
auto
i
=
xs
.
begin
();
f
.
value
(
i
->
first
);
str
+=
" = "
;
to_string_impl
(
str
,
i
->
second
);
for
(
++
i
;
i
!=
xs
.
end
();
++
i
)
{
str
+=
", "
;
f
.
value
(
i
->
first
);
if
(
!
xs
.
empty
())
{
auto
i
=
xs
.
begin
();
append_key
(
i
->
first
);
str
+=
" = "
;
to_string_impl
(
str
,
i
->
second
);
for
(
++
i
;
i
!=
xs
.
end
();
++
i
)
{
str
+=
", "
;
append_key
(
i
->
first
);
str
+=
" = "
;
to_string_impl
(
str
,
i
->
second
);
}
}
str
+=
'}'
;
}
...
...
@@ -198,8 +532,19 @@ void to_string_impl(std::string& str, const config_value& x) {
}
// namespace
std
::
string
to_string
(
const
config_value
&
x
)
{
if
(
auto
str
=
get_if
<
std
::
string
>
(
std
::
addressof
(
x
.
get_data
())))
{
return
*
str
;
}
else
{
std
::
string
result
;
to_string_impl
(
result
,
x
);
return
result
;
}
}
std
::
string
to_string
(
const
settings
&
xs
)
{
std
::
string
result
;
to_string_impl
(
result
,
x
);
to_string_visitor
f
{
result
};
f
(
xs
);
return
result
;
}
...
...
libcaf_core/src/config_value_reader.cpp
View file @
3f59e8d4
...
...
@@ -122,11 +122,12 @@ bool config_value_reader::fetch_next_object_type(type_id_t& type) {
return
false
;
},
[
this
,
&
type
](
const
config_value
*
val
)
{
if
(
auto
obj
=
get_if
<
settings
>
(
val
);
obj
==
nullptr
)
{
emplace_error
(
sec
::
conversion_failed
,
"cannot read input as object"
);
return
false
;
auto
tid
=
val
->
type_id
();
if
(
tid
!=
type_id_v
<
config_value
::
dictionary
>
)
{
type
=
tid
;
return
true
;
}
else
{
return
fetch_object_type
(
obj
,
type
);
return
fetch_object_type
(
get_if
<
settings
>
(
val
)
,
type
);
}
},
[
this
](
key_ptr
)
{
...
...
@@ -146,11 +147,13 @@ bool config_value_reader::fetch_next_object_type(type_id_t& type) {
emplace_error
(
sec
::
runtime_error
,
"list index out of bounds"
);
return
false
;
}
if
(
auto
obj
=
get_if
<
settings
>
(
std
::
addressof
(
seq
.
current
()));
!
obj
)
{
emplace_error
(
sec
::
conversion_failed
,
"cannot read input as object"
);
return
false
;
auto
&
val
=
seq
.
current
();
auto
tid
=
val
.
type_id
();
if
(
tid
!=
type_id_v
<
config_value
::
dictionary
>
)
{
type
=
tid
;
return
true
;
}
else
{
return
fetch_object_type
(
obj
,
type
);
return
fetch_object_type
(
get_if
<
settings
>
(
&
val
)
,
type
);
}
},
[
this
](
associative_array
&
)
{
...
...
@@ -162,7 +165,7 @@ bool config_value_reader::fetch_next_object_type(type_id_t& type) {
}
}
bool
config_value_reader
::
begin_object
(
string_view
)
{
bool
config_value_reader
::
begin_object
(
type_id_t
type
,
string_view
)
{
if
(
st_
.
empty
())
{
emplace_error
(
sec
::
runtime_error
,
"tried to read multiple objects from the root object"
);
...
...
@@ -176,10 +179,17 @@ bool config_value_reader::begin_object(string_view) {
},
[
this
](
const
config_value
*
val
)
{
if
(
auto
obj
=
get_if
<
settings
>
(
val
))
{
// Morph into an object. This value gets "consumed" by
// begin_object/end_object.
// Unbox the dictionary.
st_
.
top
()
=
obj
;
return
true
;
}
else
if
(
auto
dict
=
val
->
to_dictionary
())
{
// Replace the actual config value on the stack with the on-the-fly
// converted dictionary.
auto
ptr
=
std
::
make_unique
<
config_value
>
(
std
::
move
(
*
dict
));
const
settings
*
unboxed
=
std
::
addressof
(
get
<
settings
>
(
*
ptr
));
st_
.
top
()
=
unboxed
;
scratch_space_
.
emplace_back
(
std
::
move
(
ptr
));
return
true
;
}
else
{
emplace_error
(
sec
::
conversion_failed
,
"cannot read input as object"
);
return
false
;
...
...
@@ -216,7 +226,27 @@ bool config_value_reader::begin_object(string_view) {
"fetch_next_object_type called inside associative array"
);
return
false
;
});
return
visit
(
f
,
st_
.
top
());
if
(
visit
(
f
,
st_
.
top
()))
{
// Perform a type check if type is a valid ID and the object contains an
// "@type" field.
if
(
type
!=
invalid_type_id
)
{
CAF_ASSERT
(
holds_alternative
<
const
settings
*>
(
st_
.
top
()));
auto
obj
=
get
<
const
settings
*>
(
st_
.
top
());
auto
want
=
query_type_name
(
type
);
if
(
auto
i
=
obj
->
find
(
"@type"
);
i
!=
obj
->
end
())
{
if
(
auto
got
=
get_if
<
std
::
string
>
(
std
::
addressof
(
i
->
second
)))
{
if
(
want
!=
*
got
)
{
emplace_error
(
sec
::
type_clash
,
"expected type: "
+
to_string
(
want
),
"found type: "
+
*
got
);
return
false
;
}
}
}
}
return
true
;
}
else
{
return
false
;
}
}
bool
config_value_reader
::
end_object
()
{
...
...
@@ -563,15 +593,15 @@ bool config_value_reader::value(span<byte> bytes) {
bool
config_value_reader
::
fetch_object_type
(
const
settings
*
obj
,
type_id_t
&
type
)
{
if
(
auto
str
=
get_if
<
std
::
string
>
(
obj
,
"@type"
);
str
==
nullptr
)
{
emplace_error
(
sec
::
runtime_error
,
"cannot fetch object type: no '@type' entry found"
);
return
false
;
}
else
if
(
auto
id
=
query_type_id
(
*
str
);
id
==
invalid_type_id
)
{
emplace_error
(
sec
::
runtime_error
,
"no such type: "
+
*
str
);
return
false
;
}
else
{
// fetch_next_object_type only calls this function
type
=
type_id_v
<
config_value
::
dictionary
>
;
return
true
;
}
else
if
(
auto
id
=
query_type_id
(
*
str
);
id
!=
invalid_type_id
)
{
type
=
id
;
return
true
;
}
else
{
emplace_error
(
sec
::
runtime_error
,
"unknown type: "
+
*
str
);
return
false
;
}
}
...
...
libcaf_core/src/config_value_writer.cpp
View file @
3f59e8d4
...
...
@@ -66,18 +66,7 @@ config_value_writer::~config_value_writer() {
// -- interface functions ------------------------------------------------------
bool
config_value_writer
::
inject_next_object_type
(
type_id_t
type
)
{
CHECK_NOT_EMPTY
();
type_hint_
=
query_type_name
(
type
);
if
(
type_hint_
.
empty
())
{
emplace_error
(
sec
::
runtime_error
,
"query_type_name returned an empty string for type ID"
);
return
false
;
}
return
true
;
}
bool
config_value_writer
::
begin_object
(
string_view
)
{
bool
config_value_writer
::
begin_object
(
type_id_t
type
,
string_view
)
{
CHECK_NOT_EMPTY
();
auto
f
=
detail
::
make_overload
(
[
this
](
config_value
*
x
)
{
...
...
@@ -118,10 +107,8 @@ bool config_value_writer::begin_object(string_view) {
});
if
(
!
visit
(
f
,
st_
.
top
()))
return
false
;
if
(
!
type_hint_
.
empty
())
{
put
(
*
get
<
settings
*>
(
st_
.
top
()),
"@type"
,
type_hint_
);
type_hint_
=
string_view
{};
}
if
(
type
!=
invalid_type_id
)
put
(
*
get
<
settings
*>
(
st_
.
top
()),
"@type"
,
query_type_name
(
type
));
return
true
;
}
...
...
libcaf_core/src/detail/serialized_size.cpp
View file @
3f59e8d4
...
...
@@ -26,11 +26,7 @@
namespace
caf
::
detail
{
bool
serialized_size_inspector
::
inject_next_object_type
(
type_id_t
type
)
{
return
value
(
type
);
}
bool
serialized_size_inspector
::
begin_object
(
string_view
)
{
bool
serialized_size_inspector
::
begin_object
(
type_id_t
,
string_view
)
{
return
true
;
}
...
...
libcaf_core/src/detail/stringification_inspector.cpp
View file @
3f59e8d4
...
...
@@ -49,7 +49,7 @@ void escape(std::string& result, char c) {
namespace
caf
::
detail
{
bool
stringification_inspector
::
begin_object
(
string_view
name
)
{
bool
stringification_inspector
::
begin_object
(
type_id_t
,
string_view
name
)
{
sep
();
if
(
name
!=
"std::string"
)
{
result_
.
insert
(
result_
.
end
(),
name
.
begin
(),
name
.
end
());
...
...
@@ -130,15 +130,13 @@ bool stringification_inspector::value(float x) {
bool
stringification_inspector
::
value
(
double
x
)
{
sep
();
auto
str
=
std
::
to_string
(
x
);
result_
+=
str
;
detail
::
print
(
result_
,
x
);
return
true
;
}
bool
stringification_inspector
::
value
(
long
double
x
)
{
sep
();
auto
str
=
std
::
to_string
(
x
);
result_
+=
str
;
detail
::
print
(
result_
,
x
);
return
true
;
}
...
...
libcaf_core/src/message.cpp
View file @
3f59e8d4
...
...
@@ -59,7 +59,7 @@ template <class Deserializer>
bool
load_data
(
Deserializer
&
source
,
message
::
data_ptr
&
data
)
{
// For machine-to-machine data formats, we prefix the type information.
if
(
!
source
.
has_human_readable_format
())
{
GUARDED
(
source
.
begin_object
(
"message"
));
GUARDED
(
source
.
begin_object
(
type_id_v
<
message
>
,
"message"
));
GUARDED
(
source
.
begin_field
(
"types"
));
size_t
msg_size
=
0
;
GUARDED
(
source
.
begin_sequence
(
msg_size
));
...
...
@@ -159,8 +159,6 @@ bool load_data(Deserializer& source, message::data_ptr& data) {
using
unique_void_ptr
=
std
::
unique_ptr
<
void
,
free_t
>
;
auto
msg_size
=
size_t
{
0
};
std
::
vector
<
object_ptr
>
objects
;
GUARDED
(
source
.
begin_object
(
"message"
));
GUARDED
(
source
.
begin_field
(
"values"
));
GUARDED
(
source
.
begin_sequence
(
msg_size
));
if
(
msg_size
>
0
)
{
// Deserialize message elements individually.
...
...
@@ -185,7 +183,7 @@ bool load_data(Deserializer& source, message::data_ptr& data) {
STOP
(
sec
::
unknown_type
);
}
}
GUARDED
(
source
.
end_
field
()
&&
source
.
end_
sequence
());
GUARDED
(
source
.
end_sequence
());
// Merge elements into a single message data object.
intrusive_ptr
<
detail
::
message_data
>
ptr
;
if
(
auto
vptr
=
malloc
(
sizeof
(
detail
::
message_data
)
+
data_size
))
{
...
...
@@ -203,13 +201,11 @@ bool load_data(Deserializer& source, message::data_ptr& data) {
pos
+=
x
.
meta
->
padded_size
;
}
data
.
reset
(
ptr
.
release
(),
false
);
return
source
.
end_object
()
;
return
true
;
}
else
{
data
.
reset
();
return
source
.
end_sequence
();
}
return
source
.
end_sequence
()
//
&&
source
.
end_field
()
//
&&
source
.
end_object
();
}
}
// namespace
...
...
@@ -242,18 +238,18 @@ save_data(Serializer& sink, const message::data_ptr& data) {
if
(
!
sink
.
has_human_readable_format
())
{
if
(
data
==
nullptr
)
{
// Short-circuit empty tuples.
return
sink
.
begin_object
(
"message"
)
//
&&
sink
.
begin_field
(
"types"
)
//
&&
sink
.
begin_sequence
(
0
)
//
&&
sink
.
end_sequence
()
//
&&
sink
.
end_field
()
//
&&
sink
.
begin_field
(
"values"
)
//
&&
sink
.
begin_tuple
(
0
)
//
&&
sink
.
end_tuple
()
//
&&
sink
.
end_field
()
//
return
sink
.
begin_object
(
type_id_v
<
message
>
,
"message"
)
//
&&
sink
.
begin_field
(
"types"
)
//
&&
sink
.
begin_sequence
(
0
)
//
&&
sink
.
end_sequence
()
//
&&
sink
.
end_field
()
//
&&
sink
.
begin_field
(
"values"
)
//
&&
sink
.
begin_tuple
(
0
)
//
&&
sink
.
end_tuple
()
//
&&
sink
.
end_field
()
//
&&
sink
.
end_object
();
}
GUARDED
(
sink
.
begin_object
(
"message"
));
GUARDED
(
sink
.
begin_object
(
type_id_v
<
message
>
,
"message"
));
auto
type_ids
=
data
->
types
();
// Write type information.
GUARDED
(
sink
.
begin_field
(
"types"
)
&&
sink
.
begin_sequence
(
type_ids
.
size
()));
...
...
@@ -274,25 +270,17 @@ save_data(Serializer& sink, const message::data_ptr& data) {
// dynamically-typed objects.
if
(
data
==
nullptr
)
{
// Short-circuit empty tuples.
return
sink
.
begin_object
(
"message"
)
//
&&
sink
.
begin_field
(
"values"
)
//
&&
sink
.
begin_sequence
(
0
)
//
&&
sink
.
end_sequence
()
//
&&
sink
.
end_field
()
//
&&
sink
.
end_object
();
return
sink
.
begin_sequence
(
0
)
&&
sink
.
end_sequence
();
}
auto
type_ids
=
data
->
types
();
GUARDED
(
sink
.
begin_object
(
"message"
)
//
&&
sink
.
begin_field
(
"values"
)
//
&&
sink
.
begin_sequence
(
type_ids
.
size
()));
GUARDED
(
sink
.
begin_sequence
(
type_ids
.
size
()));
auto
storage
=
data
->
storage
();
for
(
auto
id
:
type_ids
)
{
auto
&
meta
=
gmos
[
id
];
GUARDED
(
sink
.
inject_next_object_type
(
id
)
//
&&
save
(
meta
,
sink
,
storage
));
GUARDED
(
save
(
meta
,
sink
,
storage
));
storage
+=
meta
.
padded_size
;
}
return
sink
.
end_sequence
()
&&
sink
.
end_field
()
&&
sink
.
end_object
()
;
return
sink
.
end_sequence
();
}
}
// namespace
...
...
libcaf_core/src/sec_strings.cpp
View file @
3f59e8d4
...
...
@@ -142,6 +142,10 @@ std::string to_string(sec x) {
return
"conversion_failed"
;
case
sec
:
:
connection_closed
:
return
"connection_closed"
;
case
sec
:
:
type_clash
:
return
"type_clash"
;
case
sec
:
:
unsupported_operation
:
return
"unsupported_operation"
;
};
}
...
...
@@ -335,6 +339,12 @@ bool from_string(string_view in, sec& out) {
}
else
if
(
in
==
"connection_closed"
)
{
out
=
sec
::
connection_closed
;
return
true
;
}
else
if
(
in
==
"type_clash"
)
{
out
=
sec
::
type_clash
;
return
true
;
}
else
if
(
in
==
"unsupported_operation"
)
{
out
=
sec
::
unsupported_operation
;
return
true
;
}
else
{
return
false
;
}
...
...
@@ -409,6 +419,8 @@ bool from_integer(std::underlying_type_t<sec> in,
case
sec
:
:
load_callback_failed
:
case
sec
:
:
conversion_failed
:
case
sec
:
:
connection_closed
:
case
sec
:
:
type_clash
:
case
sec
:
:
unsupported_operation
:
out
=
result
;
return
true
;
};
...
...
libcaf_core/src/settings.cpp
View file @
3f59e8d4
...
...
@@ -22,6 +22,8 @@
namespace
caf
{
// note: to_string is implemented in config_value.cpp
const
config_value
*
get_if
(
const
settings
*
xs
,
string_view
name
)
{
// Access the key directly unless the user specified a dot-separated path.
auto
pos
=
name
.
find
(
'.'
);
...
...
libcaf_core/src/tracing_data.cpp
View file @
3f59e8d4
...
...
@@ -41,22 +41,22 @@ namespace {
template
<
class
Serializer
>
bool
serialize_impl
(
Serializer
&
sink
,
const
tracing_data_ptr
&
x
)
{
if
(
!
x
)
{
return
sink
.
begin_object
(
"tracing_data"
)
//
&&
sink
.
begin_field
(
"value"
,
false
)
//
&&
sink
.
end_field
()
//
return
sink
.
begin_object
(
invalid_type_id
,
"tracing_data"
)
//
&&
sink
.
begin_field
(
"value"
,
false
)
//
&&
sink
.
end_field
()
//
&&
sink
.
end_object
();
}
return
sink
.
begin_object
(
"tracing_data"
)
//
&&
sink
.
begin_field
(
"value"
,
true
)
//
&&
x
->
serialize
(
sink
)
//
&&
sink
.
end_field
()
//
return
sink
.
begin_object
(
invalid_type_id
,
"tracing_data"
)
//
&&
sink
.
begin_field
(
"value"
,
true
)
//
&&
x
->
serialize
(
sink
)
//
&&
sink
.
end_field
()
//
&&
sink
.
end_object
();
}
template
<
class
Deserializer
>
bool
deserialize_impl
(
Deserializer
&
source
,
tracing_data_ptr
&
x
)
{
bool
is_present
=
false
;
if
(
!
source
.
begin_object
(
"tracing_data"
)
if
(
!
source
.
begin_object
(
invalid_type_id
,
"tracing_data"
)
||
!
source
.
begin_field
(
"value"
,
is_present
))
return
false
;
if
(
!
is_present
)
...
...
libcaf_core/src/type_id_list.cpp
View file @
3f59e8d4
...
...
@@ -22,6 +22,15 @@
namespace
caf
{
size_t
type_id_list
::
data_size
()
const
noexcept
{
auto
result
=
size_t
{
0
};
for
(
auto
type
:
*
this
)
{
auto
meta_obj
=
detail
::
global_meta_object
(
type
);
result
+=
meta_obj
->
padded_size
;
}
return
result
;
}
std
::
string
to_string
(
type_id_list
xs
)
{
if
(
!
xs
||
xs
.
size
()
==
0
)
return
"[]"
;
...
...
libcaf_core/test/config_value.cpp
View file @
3f59e8d4
...
...
@@ -23,6 +23,7 @@
#include "core-test.hpp"
#include "nasty.hpp"
#include <cmath>
#include <list>
#include <map>
#include <set>
...
...
@@ -41,6 +42,10 @@
#include "caf/string_view.hpp"
#include "caf/variant.hpp"
#include "caf/detail/bounds_checker.hpp"
#include "caf/detail/overload.hpp"
#include "caf/detail/parse.hpp"
using
std
::
string
;
using
namespace
caf
;
...
...
@@ -81,13 +86,751 @@ config_value cfg_lst(Ts&&... xs) {
return
config_value
{
std
::
move
(
lst
)};
}
struct
fixture
{
config_value
cv_null
;
config_value
cv_true
;
config_value
cv_false
;
config_value
cv_empty_uri
;
config_value
cv_empty_list
;
config_value
cv_empty_dict
;
config_value
cv_caf_uri
;
fixture
()
:
cv_true
(
true
),
cv_false
(
false
),
cv_empty_uri
(
uri
{}),
cv_empty_list
(
config_value
::
list
{}),
cv_empty_dict
(
config_value
::
dictionary
{})
{
cv_caf_uri
=
unbox
(
make_uri
(
"https://actor-framework.org"
));
}
};
}
// namespace
CAF_TEST_FIXTURE_SCOPE
(
config_value_tests
,
fixture
)
SCENARIO
(
"get_as can convert config values to boolean"
)
{
GIVEN
(
"a config value x with value true or false"
)
{
WHEN
(
"using get_as with bool"
)
{
THEN
(
"conversion succeeds"
)
{
CHECK_EQ
(
get_as
<
bool
>
(
cv_true
),
true
);
CHECK_EQ
(
get_as
<
bool
>
(
cv_false
),
false
);
}
}
}
GIVEN
(
"a config value x with value
\"
true
\"
or
\"
false
\"
"
)
{
WHEN
(
"using get_as with bool"
)
{
THEN
(
"conversion succeeds"
)
{
CHECK_EQ
(
get_as
<
bool
>
(
config_value
{
"true"
s
}),
true
);
CHECK_EQ
(
get_as
<
bool
>
(
config_value
{
"false"
s
}),
false
);
}
}
}
GIVEN
(
"non-boolean config_values"
)
{
WHEN
(
"using get_as with bool"
)
{
THEN
(
"conversion fails"
)
{
CHECK_EQ
(
get_as
<
bool
>
(
cv_null
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
bool
>
(
cv_empty_uri
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
bool
>
(
cv_empty_list
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
bool
>
(
cv_empty_dict
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
bool
>
(
config_value
{
0
}),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
bool
>
(
config_value
{
1
}),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
bool
>
(
config_value
{
0.
f
}),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
bool
>
(
config_value
{
1.
f
}),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
bool
>
(
config_value
{
""
s
}),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
bool
>
(
config_value
{
"1"
s
}),
sec
::
conversion_failed
);
}
}
}
}
SCENARIO
(
"get_as can convert config values to integers"
)
{
GIVEN
(
"a config value x with value 32,768"
)
{
auto
x
=
config_value
{
32'768
};
WHEN
(
"using get_as with integer types"
)
{
THEN
(
"conversion fails if bounds checks fail"
)
{
CHECK_EQ
(
get_as
<
uint64_t
>
(
x
),
32'768u
);
CHECK_EQ
(
get_as
<
int64_t
>
(
x
),
32'768
);
CHECK_EQ
(
get_as
<
uint32_t
>
(
x
),
32'768u
);
CHECK_EQ
(
get_as
<
int32_t
>
(
x
),
32'768
);
CHECK_EQ
(
get_as
<
uint16_t
>
(
x
),
32'768u
);
CHECK_EQ
(
get_as
<
int16_t
>
(
x
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
uint8_t
>
(
x
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
int8_t
>
(
x
),
sec
::
conversion_failed
);
}
}
}
GIVEN
(
"a config value x with value -5"
)
{
auto
x
=
config_value
{
-
5
};
WHEN
(
"using get_as with integer types"
)
{
THEN
(
"conversion fails for all unsigned types"
)
{
CAF_CHECK_EQUAL
(
get_as
<
uint64_t
>
(
x
),
sec
::
conversion_failed
);
CAF_CHECK_EQUAL
(
get_as
<
int64_t
>
(
x
),
-
5
);
CAF_CHECK_EQUAL
(
get_as
<
uint32_t
>
(
x
),
sec
::
conversion_failed
);
CAF_CHECK_EQUAL
(
get_as
<
int32_t
>
(
x
),
-
5
);
CAF_CHECK_EQUAL
(
get_as
<
uint16_t
>
(
x
),
sec
::
conversion_failed
);
CAF_CHECK_EQUAL
(
get_as
<
int16_t
>
(
x
),
-
5
);
CAF_CHECK_EQUAL
(
get_as
<
uint8_t
>
(
x
),
sec
::
conversion_failed
);
CAF_CHECK_EQUAL
(
get_as
<
int8_t
>
(
x
),
-
5
);
}
}
}
GIVEN
(
"a config value x with value
\"
50000
\"
"
)
{
auto
x
=
config_value
{
"50000"
s
};
WHEN
(
"using get_as with integer types"
)
{
THEN
(
"CAF parses the string and performs a bound check"
)
{
CAF_CHECK_EQUAL
(
get_as
<
uint64_t
>
(
x
),
50'000u
);
CAF_CHECK_EQUAL
(
get_as
<
int64_t
>
(
x
),
50'000
);
CAF_CHECK_EQUAL
(
get_as
<
uint32_t
>
(
x
),
50'000u
);
CAF_CHECK_EQUAL
(
get_as
<
int32_t
>
(
x
),
50'000
);
CAF_CHECK_EQUAL
(
get_as
<
uint16_t
>
(
x
),
50'000u
);
CAF_CHECK_EQUAL
(
get_as
<
int16_t
>
(
x
),
sec
::
conversion_failed
);
CAF_CHECK_EQUAL
(
get_as
<
uint8_t
>
(
x
),
sec
::
conversion_failed
);
CAF_CHECK_EQUAL
(
get_as
<
int8_t
>
(
x
),
sec
::
conversion_failed
);
}
}
}
GIVEN
(
"a config value x with value 50.0"
)
{
auto
x
=
config_value
{
50.0
};
WHEN
(
"using get_as with integer types"
)
{
THEN
(
"CAF parses the string 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.05"
)
{
auto
x
=
config_value
{
50.05
};
WHEN
(
"using get_as with integer types"
)
{
THEN
(
"CAF fails to convert the real to an integer"
)
{
CHECK_EQ
(
get_as
<
uint64_t
>
(
x
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
int64_t
>
(
x
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
uint32_t
>
(
x
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
int32_t
>
(
x
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
uint16_t
>
(
x
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
int16_t
>
(
x
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
uint8_t
>
(
x
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
int8_t
>
(
x
),
sec
::
conversion_failed
);
}
}
}
GIVEN
(
"a config value x with value
\"
50.000
\"
"
)
{
auto
x
=
config_value
{
"50.000"
s
};
WHEN
(
"using get_as with integer types"
)
{
THEN
(
"CAF parses the string 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.05
\"
"
)
{
auto
x
=
config_value
{
"50.05"
s
};
WHEN
(
"using get_as with integer types"
)
{
THEN
(
"CAF fails to convert the real to an integer"
)
{
CHECK_EQ
(
get_as
<
uint64_t
>
(
x
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
int64_t
>
(
x
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
uint32_t
>
(
x
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
int32_t
>
(
x
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
uint16_t
>
(
x
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
int16_t
>
(
x
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
uint8_t
>
(
x
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
int8_t
>
(
x
),
sec
::
conversion_failed
);
}
}
}
GIVEN
(
"config_values of null, URI, boolean, list or dictionary"
)
{
WHEN
(
"using get_as with floating point types"
)
{
THEN
(
"conversion fails"
)
{
CHECK_EQ
(
get_as
<
float
>
(
cv_null
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
float
>
(
cv_true
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
float
>
(
cv_false
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
float
>
(
cv_empty_uri
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
float
>
(
cv_empty_list
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
float
>
(
cv_empty_dict
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
double
>
(
cv_null
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
double
>
(
cv_true
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
double
>
(
cv_false
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
double
>
(
cv_empty_uri
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
double
>
(
cv_empty_list
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
double
>
(
cv_empty_dict
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
long
double
>
(
cv_null
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
long
double
>
(
cv_true
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
long
double
>
(
cv_false
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
long
double
>
(
cv_empty_uri
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
long
double
>
(
cv_empty_list
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
long
double
>
(
cv_empty_dict
),
sec
::
conversion_failed
);
}
}
}
}
SCENARIO
(
"get_as can convert config values to floating point numbers"
)
{
GIVEN
(
"a config value x with value 1.79769e+308"
)
{
auto
x
=
config_value
{
1.79769e+308
};
WHEN
(
"using get_as with floating point types"
)
{
THEN
(
"conversion fails if bounds checks fail"
)
{
CHECK_EQ
(
get_as
<
long
double
>
(
x
),
1.79769e+308
);
CHECK_EQ
(
get_as
<
double
>
(
x
),
1.79769e+308
);
CHECK_EQ
(
get_as
<
float
>
(
x
),
sec
::
conversion_failed
);
}
}
}
GIVEN
(
"a config value x with value
\"
3e7
\"
"
)
{
auto
x
=
config_value
{
"3e7"
s
};
WHEN
(
"using get_as with floating point types"
)
{
THEN
(
"CAF parses the string and converts the value"
)
{
CHECK_EQ
(
get_as
<
long
double
>
(
x
),
3e7
);
CHECK_EQ
(
get_as
<
double
>
(
x
),
3e7
);
CHECK_EQ
(
get_as
<
float
>
(
x
),
3e7
f
);
}
}
}
GIVEN
(
"a config value x with value 123"
)
{
auto
x
=
config_value
{
123
};
WHEN
(
"using get_as with floating point types"
)
{
THEN
(
"CAF converts the value"
)
{
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"
)
{
WHEN
(
"using get_as with integer types"
)
{
THEN
(
"conversion fails"
)
{
CHECK_EQ
(
get_as
<
int64_t
>
(
cv_null
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
int64_t
>
(
cv_true
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
int64_t
>
(
cv_false
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
int64_t
>
(
cv_empty_uri
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
int64_t
>
(
cv_empty_list
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
int64_t
>
(
cv_empty_dict
),
sec
::
conversion_failed
);
}
}
}
}
SCENARIO
(
"get_as can convert config values to timespans"
)
{
using
namespace
std
::
chrono_literals
;
GIVEN
(
"a config value with value 42s"
)
{
auto
x
=
config_value
{
timespan
{
42s
}};
WHEN
(
"using get_as with timespan"
)
{
THEN
(
"conversion succeeds"
)
{
CHECK_EQ
(
get_as
<
timespan
>
(
x
),
timespan
{
42s
});
CHECK_EQ
(
get_as
<
std
::
string
>
(
x
),
"42s"
);
}
}
WHEN
(
"using get_as with type other than timespan or string"
)
{
THEN
(
"conversion fails"
)
{
CHECK_EQ
(
get_as
<
int64_t
>
(
x
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
double
>
(
x
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
uri
>
(
x
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
config_value
::
list
>
(
x
),
sec
::
conversion_failed
);
CHECK_EQ
(
get_as
<
config_value
::
dictionary
>
(
x
),
sec
::
conversion_failed
);
}
}
}
}
SCENARIO
(
"get_as can convert config values to strings"
)
{
using
string
=
std
::
string
;
GIVEN
(
"any config value"
)
{
WHEN
(
"using get_as with string"
)
{
THEN
(
"CAF renders the value as string"
)
{
CHECK_EQ
(
get_as
<
string
>
(
cv_null
),
"null"
);
CHECK_EQ
(
get_as
<
string
>
(
cv_true
),
"true"
);
CHECK_EQ
(
get_as
<
string
>
(
cv_false
),
"false"
);
CHECK_EQ
(
get_as
<
string
>
(
cv_empty_list
),
"[]"
);
CHECK_EQ
(
get_as
<
string
>
(
cv_empty_dict
),
"{}"
);
CHECK_EQ
(
get_as
<
string
>
(
config_value
{
42
}),
"42"
);
CHECK_EQ
(
get_as
<
string
>
(
config_value
{
4.2
}),
"4.2"
);
CHECK_EQ
(
get_as
<
string
>
(
config_value
{
timespan
{
4
}}),
"4ns"
);
CHECK_EQ
(
get_as
<
string
>
(
cv_caf_uri
),
"https://actor-framework.org"
);
}
}
}
}
SCENARIO
(
"get_as can convert config values to lists"
)
{
using
list
=
config_value
::
list
;
GIVEN
(
"a config value with value [1, 2, 3]"
)
{
auto
x
=
make_config_value_list
(
1
,
2
,
3
);
WHEN
(
"using get_as with config_value::list"
)
{
THEN
(
"conversion succeeds"
)
{
auto
maybe_res
=
get_as
<
list
>
(
x
);
if
(
CHECK
(
maybe_res
)
&&
CHECK_EQ
(
maybe_res
->
size
(),
3u
))
{
auto
&
res
=
*
maybe_res
;
CHECK_EQ
(
get_as
<
int
>
(
res
[
0
]),
1
);
CHECK_EQ
(
get_as
<
int
>
(
res
[
1
]),
2
);
CHECK_EQ
(
get_as
<
int
>
(
res
[
2
]),
3
);
}
}
}
WHEN
(
"using get_as with vector<int>"
)
{
THEN
(
"conversion succeeds"
)
{
auto
maybe_res
=
get_as
<
std
::
vector
<
int
>>
(
x
);
if
(
CHECK
(
maybe_res
)
&&
CHECK_EQ
(
maybe_res
->
size
(),
3u
))
{
auto
&
res
=
*
maybe_res
;
CHECK_EQ
(
res
[
0
],
1
);
CHECK_EQ
(
res
[
1
],
2
);
CHECK_EQ
(
res
[
2
],
3
);
}
}
}
}
GIVEN
(
"a config value with value
\"
[1, 2, 3]
\"
"
)
{
auto
x
=
config_value
(
"[1, 2, 3]"
s
);
WHEN
(
"using get_as with list"
)
{
THEN
(
"conversion succeeds"
)
{
auto
maybe_res
=
get_as
<
list
>
(
x
);
if
(
CHECK
(
maybe_res
)
&&
CHECK_EQ
(
maybe_res
->
size
(),
3u
))
{
auto
&
res
=
*
maybe_res
;
CHECK_EQ
(
get_as
<
int
>
(
res
[
0
]),
1
);
CHECK_EQ
(
get_as
<
int
>
(
res
[
1
]),
2
);
CHECK_EQ
(
get_as
<
int
>
(
res
[
2
]),
3
);
}
}
}
WHEN
(
"using get_as with vector<int>"
)
{
THEN
(
"conversion succeeds"
)
{
auto
maybe_res
=
get_as
<
std
::
vector
<
int
>>
(
x
);
if
(
CHECK
(
maybe_res
)
&&
CHECK_EQ
(
maybe_res
->
size
(),
3u
))
{
auto
&
res
=
*
maybe_res
;
CHECK_EQ
(
res
[
0
],
1
);
CHECK_EQ
(
res
[
1
],
2
);
CHECK_EQ
(
res
[
2
],
3
);
}
}
}
}
}
SCENARIO
(
"get_as can convert config values to dictionaries"
)
{
using
dictionary
=
config_value
::
dictionary
;
auto
dict
=
config_value
::
dictionary
{
{
"a"
,
config_value
{
1
}},
{
"b"
,
config_value
{
2
}},
{
"c"
,
config_value
{
3
}},
};
std
::
vector
<
config_value
>
given_values
;
given_values
.
emplace_back
(
std
::
move
(
dict
));
given_values
.
emplace_back
(
"{a = 1, b = 2, c = 3}"
s
);
for
(
auto
&
x
:
given_values
)
{
GIVEN
(
"the config value "
<<
x
)
{
WHEN
(
"using get_as with config_value::dictionary"
)
{
THEN
(
"conversion succeeds"
)
{
auto
maybe_res
=
get_as
<
dictionary
>
(
x
);
if
(
CHECK
(
maybe_res
)
&&
CHECK_EQ
(
maybe_res
->
size
(),
3u
))
{
auto
&
res
=
*
maybe_res
;
CHECK_EQ
(
get_as
<
int
>
(
res
[
"a"
]),
1
);
CHECK_EQ
(
get_as
<
int
>
(
res
[
"b"
]),
2
);
CHECK_EQ
(
get_as
<
int
>
(
res
[
"c"
]),
3
);
}
}
}
WHEN
(
"using get_as with config_value::list"
)
{
THEN
(
"CAF converts the dictionary to a list of lists"
)
{
auto
maybe_res
=
get_as
<
list
>
(
x
);
if
(
CHECK
(
maybe_res
)
&&
CHECK_EQ
(
maybe_res
->
size
(),
3u
))
{
auto
&
res
=
*
maybe_res
;
if
(
auto
kvp
=
unbox
(
get_as
<
list
>
(
res
[
0
]));
CHECK_EQ
(
kvp
.
size
(),
2u
))
{
CHECK_EQ
(
get_as
<
std
::
string
>
(
kvp
[
0
]),
"a"
);
CHECK_EQ
(
get_as
<
int
>
(
kvp
[
1
]),
1
);
}
if
(
auto
kvp
=
unbox
(
get_as
<
list
>
(
res
[
1
]));
CHECK_EQ
(
kvp
.
size
(),
2u
))
{
CHECK_EQ
(
get_as
<
std
::
string
>
(
kvp
[
0
]),
"b"
);
CHECK_EQ
(
get_as
<
int
>
(
kvp
[
1
]),
2
);
}
if
(
auto
kvp
=
unbox
(
get_as
<
list
>
(
res
[
2
]));
CHECK_EQ
(
kvp
.
size
(),
2u
))
{
CHECK_EQ
(
get_as
<
std
::
string
>
(
kvp
[
0
]),
"c"
);
CHECK_EQ
(
get_as
<
int
>
(
kvp
[
1
]),
3
);
}
}
}
}
WHEN
(
"using get_as with vector<tuple<string, int>>"
)
{
THEN
(
"CAF converts the dictionary to a list of tuples"
)
{
using
kvp_t
=
std
::
tuple
<
std
::
string
,
int
>
;
auto
maybe_res
=
get_as
<
std
::
vector
<
kvp_t
>>
(
x
);
MESSAGE
(
"maybe_res: "
<<
maybe_res
);
if
(
CHECK
(
maybe_res
)
&&
CHECK_EQ
(
maybe_res
->
size
(),
3u
))
{
auto
&
res
=
*
maybe_res
;
CHECK_EQ
(
res
[
0
],
kvp_t
(
"a"
,
1
));
CHECK_EQ
(
res
[
1
],
kvp_t
(
"b"
,
2
));
CHECK_EQ
(
res
[
2
],
kvp_t
(
"c"
,
3
));
}
}
}
}
}
}
SCENARIO
(
"get_as can convert config values to maps"
)
{
auto
dict
=
config_value
::
dictionary
{
{
"1"
,
config_value
{
1
}},
{
"2"
,
config_value
{
4
}},
{
"3"
,
config_value
{
9
}},
};
std
::
vector
<
config_value
>
given_values
;
given_values
.
emplace_back
(
std
::
move
(
dict
));
given_values
.
emplace_back
(
"{1 = 1, 2 = 4, 3 = 9}"
s
);
for
(
auto
&
x
:
given_values
)
{
GIVEN
(
"the config value "
<<
x
)
{
WHEN
(
"using get_as with map<string, int>"
)
{
THEN
(
"conversion succeeds"
)
{
auto
maybe_res
=
get_as
<
std
::
map
<
std
::
string
,
int
>>
(
x
);
if
(
CHECK
(
maybe_res
)
&&
CHECK_EQ
(
maybe_res
->
size
(),
3u
))
{
auto
&
res
=
*
maybe_res
;
CHECK_EQ
(
res
[
"1"
],
1
);
CHECK_EQ
(
res
[
"2"
],
4
);
CHECK_EQ
(
res
[
"3"
],
9
);
}
}
}
WHEN
(
"using get_as with unordered_map<string, int>"
)
{
THEN
(
"conversion succeeds"
)
{
auto
maybe_res
=
get_as
<
std
::
unordered_map
<
std
::
string
,
int
>>
(
x
);
if
(
CHECK
(
maybe_res
)
&&
CHECK_EQ
(
maybe_res
->
size
(),
3u
))
{
auto
&
res
=
*
maybe_res
;
CHECK_EQ
(
res
[
"1"
],
1
);
CHECK_EQ
(
res
[
"2"
],
4
);
CHECK_EQ
(
res
[
"3"
],
9
);
}
}
}
WHEN
(
"using get_as with map<int, int>"
)
{
THEN
(
"conversion succeeds"
)
{
auto
maybe_res
=
get_as
<
std
::
map
<
int
,
int
>>
(
x
);
if
(
CHECK
(
maybe_res
)
&&
CHECK_EQ
(
maybe_res
->
size
(),
3u
))
{
auto
&
res
=
*
maybe_res
;
CHECK_EQ
(
res
[
1
],
1
);
CHECK_EQ
(
res
[
2
],
4
);
CHECK_EQ
(
res
[
3
],
9
);
}
}
}
WHEN
(
"using get_as with unordered_map<int, int>"
)
{
THEN
(
"conversion succeeds"
)
{
auto
maybe_res
=
get_as
<
std
::
unordered_map
<
int
,
int
>>
(
x
);
if
(
CHECK
(
maybe_res
)
&&
CHECK_EQ
(
maybe_res
->
size
(),
3u
))
{
auto
&
res
=
*
maybe_res
;
CHECK_EQ
(
res
[
1
],
1
);
CHECK_EQ
(
res
[
2
],
4
);
CHECK_EQ
(
res
[
3
],
9
);
}
}
}
}
}
}
SCENARIO
(
"get_as can convert config values to custom types"
)
{
std
::
vector
<
std
::
pair
<
weekday
,
std
::
string
>>
weekday_values
{
{
weekday
::
monday
,
"monday"
s
},
{
weekday
::
tuesday
,
"tuesday"
s
},
{
weekday
::
wednesday
,
"wednesday"
s
},
{
weekday
::
thursday
,
"thursday"
s
},
{
weekday
::
friday
,
"friday"
s
},
{
weekday
::
saturday
,
"saturday"
s
},
{
weekday
::
sunday
,
"sunday"
s
}};
for
(
const
auto
&
[
enum_val
,
str_val
]
:
weekday_values
)
{
config_value
x
{
str_val
};
GIVEN
(
"the config value "
<<
x
)
{
WHEN
(
"using get_as with weekday"
)
{
THEN
(
"CAF picks up the custom inspect_value overload for conversion"
)
{
auto
maybe_res
=
get_as
<
weekday
>
(
x
);
if
(
CHECK
(
maybe_res
))
CHECK_EQ
(
*
maybe_res
,
enum_val
);
}
}
}
}
config_value
::
dictionary
my_request_dict
;
my_request_dict
[
"a"
]
=
config_value
{
10
};
my_request_dict
[
"b"
]
=
config_value
{
20
};
auto
my_request_val
=
config_value
{
my_request_dict
};
GIVEN
(
"the config value "
<<
my_request_val
)
{
WHEN
(
"using get_as with my_request"
)
{
THEN
(
"CAF picks up the custom inspect overload for conversion"
)
{
auto
maybe_res
=
get_as
<
my_request
>
(
my_request_val
);
if
(
CHECK
(
maybe_res
))
CHECK_EQ
(
*
maybe_res
,
my_request
(
10
,
20
));
}
}
}
std
::
vector
<
config_value
>
obj_vals
{
config_value
{
my_request_val
},
config_value
{
config_value
::
dictionary
{}},
config_value
{
"{}"
s
}};
for
(
auto
&
x
:
obj_vals
)
{
GIVEN
(
"the config value "
<<
x
)
{
WHEN
(
"using get_as with dummy_tag_type"
)
{
THEN
(
"CAF only checks whether the config value is dictionary-ish"
)
{
CHECK
(
get_as
<
dummy_tag_type
>
(
my_request_val
));
}
}
}
}
std
::
vector
<
config_value
>
non_obj_vals
{
config_value
{},
config_value
{
42
},
config_value
{
"[1,2,3]"
s
}};
for
(
auto
&
x
:
non_obj_vals
)
{
GIVEN
(
"the config value "
<<
x
)
{
WHEN
(
"using get_as with dummy_tag_type"
)
{
THEN
(
"conversion fails"
)
{
CHECK_EQ
(
get_as
<
dummy_tag_type
>
(
x
),
sec
::
conversion_failed
);
}
}
}
}
}
SCENARIO
(
"get_or converts or returns a fallback value"
)
{
using
namespace
caf
::
literals
;
GIVEN
(
"the config value 42"
)
{
auto
x
=
config_value
{
42
};
WHEN
(
"using get_or with type int"
)
{
THEN
(
"CAF ignores the default value"
)
{
CHECK_EQ
(
get_or
(
x
,
10
),
42
);
}
}
WHEN
(
"using get_or with type string"
)
{
THEN
(
"CAF ignores the default value"
)
{
CHECK_EQ
(
get_or
(
x
,
"foo"
s
),
"42"
s
);
}
}
WHEN
(
"using get_or with type bool"
)
{
THEN
(
"CAF returns the default value"
)
{
CHECK_EQ
(
get_or
(
x
,
false
),
false
);
}
}
WHEN
(
"using get_or with type span<int>"
)
{
int
fallback_arr
[]
=
{
10
,
20
,
30
};
auto
fallback
=
make_span
(
fallback_arr
);
THEN
(
"CAF returns the default value after converting it to vector<int>"
)
{
auto
result
=
get_or
(
x
,
fallback
);
static_assert
(
std
::
is_same
<
decltype
(
result
),
std
::
vector
<
int
>>::
value
);
CHECK_EQ
(
result
,
std
::
vector
<
int
>
({
10
,
20
,
30
}));
}
}
WHEN
(
"using get_or with type i64_wrapper"
)
{
THEN
(
"CAF returns i64_wrapper{42}"
)
{
auto
result
=
get_or
<
i64_wrapper
>
(
x
,
10
);
CHECK_EQ
(
result
.
value
,
42
);
}
}
}
GIVEN
(
"the config value 'hello world'"
)
{
auto
x
=
config_value
{
"hello world"
};
WHEN
(
"using get_or with type i64_wrapper"
)
{
THEN
(
"CAF returns the fallback value"
)
{
auto
result
=
get_or
<
i64_wrapper
>
(
x
,
10
);
CHECK_EQ
(
result
.
value
,
10
);
}
}
}
}
SCENARIO
(
"config values can default-construct all registered types"
)
{
auto
from
=
[](
type_id_t
id
)
{
config_value
result
;
if
(
auto
err
=
result
.
default_construct
(
id
))
CAF_FAIL
(
"default construction failed: "
<<
err
);
return
result
;
};
auto
keys
=
[](
const
auto
&
dict
)
{
std
::
vector
<
std
::
string
>
result
;
for
(
const
auto
&
kvp
:
dict
)
result
.
emplace_back
(
kvp
.
first
);
return
result
;
};
GIVEN
(
"a config value"
)
{
WHEN
(
"calling default_construct for any integral type"
)
{
THEN
(
"the config value becomes config_value::integer{0}"
)
{
CHECK_EQ
(
from
(
type_id_v
<
int8_t
>
),
config_value
{
0
});
CHECK_EQ
(
from
(
type_id_v
<
int16_t
>
),
config_value
{
0
});
CHECK_EQ
(
from
(
type_id_v
<
int32_t
>
),
config_value
{
0
});
CHECK_EQ
(
from
(
type_id_v
<
int64_t
>
),
config_value
{
0
});
CHECK_EQ
(
from
(
type_id_v
<
uint8_t
>
),
config_value
{
0
});
CHECK_EQ
(
from
(
type_id_v
<
uint16_t
>
),
config_value
{
0
});
CHECK_EQ
(
from
(
type_id_v
<
uint32_t
>
),
config_value
{
0
});
CHECK_EQ
(
from
(
type_id_v
<
uint64_t
>
),
config_value
{
0
});
}
}
WHEN
(
"calling default_construct for any floating point type"
)
{
THEN
(
"the config value becomes config_value::real{0}"
)
{
CHECK_EQ
(
from
(
type_id_v
<
float
>
),
config_value
{
0.0
});
CHECK_EQ
(
from
(
type_id_v
<
double
>
),
config_value
{
0.0
});
CHECK_EQ
(
from
(
type_id_v
<
long
double
>
),
config_value
{
0.0
});
}
}
WHEN
(
"calling default_construct for std::string"
)
{
THEN
(
"the config value becomes
\"\"
"
)
{
CHECK_EQ
(
from
(
type_id_v
<
std
::
string
>
),
config_value
{
std
::
string
{}});
}
}
WHEN
(
"calling default_construct for caf::timespan"
)
{
THEN
(
"the config value becomes 0s"
)
{
CHECK_EQ
(
from
(
type_id_v
<
timespan
>
),
config_value
{
timespan
{
0
}});
}
}
WHEN
(
"calling default_construct for caf::uri"
)
{
THEN
(
"the config value becomes an empty URI"
)
{
CHECK_EQ
(
from
(
type_id_v
<
uri
>
),
config_value
{
uri
{}});
}
}
WHEN
(
"calling default_construct for any list-like type"
)
{
THEN
(
"the config value becomes a config_value::list"
)
{
CHECK_EQ
(
from
(
type_id_v
<
std
::
vector
<
actor
>>
).
get_data
().
index
(),
7u
);
CHECK_EQ
(
from
(
type_id_v
<
std
::
vector
<
bool
>>
).
get_data
().
index
(),
7u
);
}
}
WHEN
(
"calling default_construct for any custom non-list type"
)
{
THEN
(
"the config value becomes a dictionary"
)
{
auto
val
=
from
(
type_id_v
<
my_request
>
);
CHECK_EQ
(
val
.
get_data
().
index
(),
8u
);
auto
&
dict
=
val
.
as_dictionary
();
CHECK_EQ
(
keys
(
dict
),
std
::
vector
<
std
::
string
>
({
"@type"
,
"a"
,
"b"
}));
CHECK_EQ
(
dict
[
"@type"
].
get_data
().
index
(),
6u
);
CHECK_EQ
(
get_as
<
std
::
string
>
(
dict
[
"@type"
]),
"my_request"
s
);
CHECK_EQ
(
dict
[
"a"
].
get_data
().
index
(),
1u
);
CHECK_EQ
(
get_as
<
int32_t
>
(
dict
[
"a"
]),
0
);
CHECK_EQ
(
dict
[
"b"
].
get_data
().
index
(),
1u
);
CHECK_EQ
(
get_as
<
int32_t
>
(
dict
[
"b"
]),
0
);
}
}
}
}
#define CHECK_ROUNDTRIP(init_val, expected_str) \
do { \
config_value x; \
if (auto assign_failed = x.assign(init_val); CHECK(!assign_failed)) { \
auto str = to_string(x); \
CHECK_EQ(str, expected_str); \
auto parsed = config_value::parse(str); \
using init_val_type = decltype(init_val); \
if (CHECK(parsed)) { \
if constexpr (!std::is_same<init_val_type, message>::value) \
CHECK_EQ(get_as<init_val_type>(*parsed), init_val); \
else \
CHECK_EQ(to_string(*parsed), str); \
} \
} \
} while (false)
SCENARIO
(
"config values can parse their own to_string output"
)
{
GIVEN
(
"a config value"
)
{
WHEN
(
"assigning a value and then calling to_string on it"
)
{
THEN
(
"then config_value::parse reconstitutes the original value"
)
{
CHECK_ROUNDTRIP
(
0
,
"0"
);
CHECK_ROUNDTRIP
(
"hello world"
s
,
"hello world"
);
CHECK_ROUNDTRIP
(
std
::
vector
<
int
>
({
1
,
2
,
3
}),
"[1, 2, 3]"
);
CHECK_ROUNDTRIP
(
my_request
(
1
,
2
),
R"_({"@type" = "my_request", a = 1, b = 2})_"
);
CHECK_ROUNDTRIP
(
std
::
make_tuple
(
add_atom_v
,
1
,
2
),
R"_([{"@type" = "caf::add_atom"}, 1, 2])_"
);
CHECK_ROUNDTRIP
(
make_message
(
add_atom_v
,
1
,
2
),
R"_([{"@type" = "caf::add_atom"}, 1, 2])_"
);
}
}
}
}
SCENARIO
(
"config values can convert lists of tuples to dictionaries"
)
{
GIVEN
(
"a config value containing a list of key-value pairs (lists)"
)
{
WHEN
(
"calling as_dictionary on the object"
)
{
THEN
(
"the config value lifts the key-value pair list to a dictionary"
)
{
auto
x
=
make_config_value_list
(
make_config_value_list
(
"one"
,
1
),
make_config_value_list
(
2
,
"two"
));
auto
&
dict
=
x
.
as_dictionary
();
CHECK_EQ
(
dict
.
size
(),
2u
);
CHECK_EQ
(
dict
[
"one"
],
1
);
CHECK_EQ
(
dict
[
"2"
],
"two"
s
);
}
}
}
GIVEN
(
"a config value containing a string representing a kvp list"
)
{
WHEN
(
"calling as_dictionary on the object"
)
{
THEN
(
"the config value lifts the key-value pair list to a dictionary"
)
{
auto
x
=
config_value
{
R"_([["one", 1], [2, "two"]])_"
};
auto
&
dict
=
x
.
as_dictionary
();
CHECK_EQ
(
dict
.
size
(),
2u
);
CHECK_EQ
(
dict
[
"one"
],
1
);
CHECK_EQ
(
dict
[
"2"
],
"two"
s
);
}
}
}
}
SCENARIO
(
"config values can parse messages"
)
{
using
testee_t
=
typed_actor
<
result
<
void
>
(
int16_t
),
//
result
<
void
>
(
int32_t
,
int32_t
),
//
result
<
void
>
(
my_request
),
//
result
<
void
>
(
add_atom
,
int32_t
,
int32_t
)
>
;
//
auto
parse
=
[](
string_view
str
)
{
testee_t
testee
;
return
config_value
::
parse_msg
(
str
,
testee
);
};
GIVEN
(
"a typed actor handle and valid input strings"
)
{
THEN
(
"config_value::parse_msg generates matching message types"
)
{
if
(
auto
msg
=
parse
(
"16000"
);
CHECK
(
msg
))
{
if
(
CHECK
((
msg
->
match_elements
<
int16_t
>
())))
{
CHECK_EQ
(
msg
->
get_as
<
int16_t
>
(
0
),
16000
);
}
}
if
(
auto
msg
=
parse
(
"[16000]"
);
CHECK
(
msg
))
{
if
(
CHECK
((
msg
->
match_elements
<
int16_t
>
())))
{
CHECK_EQ
(
msg
->
get_as
<
int16_t
>
(
0
),
16000
);
}
}
if
(
auto
msg
=
parse
(
"[1, 2]"
);
CHECK
(
msg
))
{
if
(
CHECK
((
msg
->
match_elements
<
int32_t
,
int32_t
>
())))
{
CHECK_EQ
(
msg
->
get_as
<
int32_t
>
(
0
),
1
);
CHECK_EQ
(
msg
->
get_as
<
int32_t
>
(
1
),
2
);
}
}
if
(
auto
msg
=
parse
(
"{a = 1, b = 2}"
);
CHECK
(
msg
))
{
if
(
CHECK
((
msg
->
match_elements
<
my_request
>
())))
{
CHECK_EQ
(
msg
->
get_as
<
my_request
>
(
0
),
my_request
(
1
,
2
));
}
}
if
(
auto
msg
=
parse
(
"[{a = 1, b = 2}]"
);
CHECK
(
msg
))
{
if
(
CHECK
((
msg
->
match_elements
<
my_request
>
())))
{
CHECK_EQ
(
msg
->
get_as
<
my_request
>
(
0
),
my_request
(
1
,
2
));
}
}
if
(
auto
msg
=
parse
(
R"_([{"@type" = "caf::add_atom"}, 1, 2])_"
);
CHECK
(
msg
))
{
if
(
CHECK
((
msg
->
match_elements
<
add_atom
,
int32_t
,
int32_t
>
())))
{
CHECK_EQ
(
msg
->
get_as
<
int32_t
>
(
1
),
1
);
CHECK_EQ
(
msg
->
get_as
<
int32_t
>
(
2
),
2
);
}
}
}
}
GIVEN
(
"a typed actor handle and invalid input strings"
)
{
THEN
(
"config_value::parse_msg returns nullopt"
)
{
CHECK
(
!
parse
(
"65000"
));
CHECK
(
!
parse
(
"[1, 2, 3]"
));
CHECK
(
!
parse
(
"[{a = 1.1, b = 2.2}]"
));
}
}
}
CAF_TEST
(
default_constructed
)
{
config_value
x
;
CAF_CHECK_EQUAL
(
holds_alternative
<
int64_t
>
(
x
),
true
);
CAF_CHECK_EQUAL
(
get
<
int64_t
>
(
x
),
0
);
CAF_CHECK_EQUAL
(
x
.
type_name
(),
"integer"
s
);
CAF_CHECK_EQUAL
(
holds_alternative
<
none_t
>
(
x
),
true
);
CAF_CHECK_EQUAL
(
x
.
type_name
(),
"none"
s
);
}
CAF_TEST
(
positive
integer
)
{
...
...
@@ -441,70 +1184,4 @@ CAF_TEST(conversion to std::unordered_multimap) {
CAF_CHECK_EQUAL
(
*
ys
,
map_type
({{
"a"
,
1
},
{
"b"
,
2
},
{
"c"
,
3
},
{
"d"
,
4
}}));
}
namespace
{
struct
point_3d
{
int32_t
x
;
int32_t
y
;
int32_t
z
;
};
[[
maybe_unused
]]
bool
operator
==
(
const
point_3d
&
x
,
const
point_3d
&
y
)
{
return
std
::
tie
(
x
.
x
,
x
.
y
,
x
.
z
)
==
std
::
tie
(
y
.
x
,
y
.
y
,
y
.
z
);
}
template
<
class
Inspector
>
bool
inspect
(
Inspector
&
f
,
point_3d
&
x
)
{
return
f
.
object
(
x
).
fields
(
f
.
field
(
"x"
,
x
.
x
),
f
.
field
(
"y"
,
x
.
y
),
f
.
field
(
"z"
,
x
.
z
));
}
struct
line
{
point_3d
p1
;
point_3d
p2
;
};
[[
maybe_unused
]]
bool
operator
==
(
const
line
&
x
,
const
line
&
y
)
{
return
std
::
tie
(
x
.
p1
,
x
.
p2
)
==
std
::
tie
(
y
.
p1
,
y
.
p2
);
}
template
<
class
Inspector
>
bool
inspect
(
Inspector
&
f
,
line
&
x
)
{
return
f
.
object
(
x
).
fields
(
f
.
field
(
"p1"
,
x
.
p1
),
f
.
field
(
"p2"
,
x
.
p2
));
}
}
// namespace
CAF_TEST
(
config
values
pick
up
user
defined
inspect
overloads
)
{
CAF_MESSAGE
(
"users can fill dictionaries with field contents"
);
{
config_value
x
;
auto
&
dict
=
x
.
as_dictionary
();
put
(
dict
,
"p1.x"
,
1
);
put
(
dict
,
"p1.y"
,
2
);
put
(
dict
,
"p1.z"
,
3
);
put
(
dict
,
"p2.x"
,
10
);
put
(
dict
,
"p2.y"
,
20
);
put
(
dict
,
"p2.z"
,
30
);
auto
l
=
get_if
<
line
>
(
&
x
);
if
(
CAF_CHECK_NOT_EQUAL
(
l
,
none
))
CAF_CHECK_EQUAL
(
*
l
,
(
line
{{
1
,
2
,
3
},
{
10
,
20
,
30
}}));
}
CAF_MESSAGE
(
"users can pass objects as dictionaries on the command line"
);
{
auto
val
=
config_value
::
parse
(
"{p1{x=1,y=2,z=3},p2{x=10,y=20,z=30}}"
);
CAF_CHECK
(
val
);
if
(
val
)
{
auto
l
=
get_if
<
line
>
(
std
::
addressof
(
*
val
));
if
(
CAF_CHECK_NOT_EQUAL
(
l
,
none
))
CAF_CHECK_EQUAL
(
*
l
,
(
line
{{
1
,
2
,
3
},
{
10
,
20
,
30
}}));
}
}
CAF_MESSAGE
(
"value readers appear as inspectors with human-readable format"
);
{
config_value
x
{
std
::
string
{
"saturday"
}};
auto
val
=
get_if
<
weekday
>
(
&
x
);
if
(
CAF_CHECK
(
val
))
CAF_CHECK_EQUAL
(
*
val
,
weekday
::
saturday
);
}
}
CAF_TEST_FIXTURE_SCOPE_END
()
libcaf_core/test/core-test.hpp
View file @
3f59e8d4
...
...
@@ -83,7 +83,7 @@ struct i32_wrapper {
template
<
class
Inspector
>
friend
bool
inspect
(
Inspector
&
f
,
i32_wrapper
&
x
)
{
return
f
.
object
(
x
).
fields
(
f
.
field
(
"value"
,
x
.
value
)
);
return
f
.
apply
(
x
.
value
);
}
};
...
...
@@ -97,19 +97,27 @@ struct i64_wrapper {
++
instances
;
}
explicit
i64_wrapper
(
int64_t
val
)
:
value
(
val
)
{
++
instances
;
}
~
i64_wrapper
()
{
--
instances
;
}
template
<
class
Inspector
>
friend
bool
inspect
(
Inspector
&
f
,
i64_wrapper
&
x
)
{
return
f
.
object
(
x
).
fields
(
f
.
field
(
"value"
,
x
.
value
)
);
return
f
.
apply
(
x
.
value
);
}
};
struct
my_request
{
int32_t
a
;
int32_t
b
;
int32_t
a
=
0
;
int32_t
b
=
0
;
my_request
()
=
default
;
my_request
(
int
a
,
int
b
)
:
a
(
a
),
b
(
b
)
{
// nop
}
};
[[
maybe_unused
]]
inline
bool
operator
==
(
const
my_request
&
x
,
...
...
libcaf_core/test/detail/parser/read_config.cpp
View file @
3f59e8d4
...
...
@@ -145,7 +145,7 @@ const auto conf0_log = make_log(
"key: foo=bar"
,
"{"
,
"key: foo"
,
"value (string):
\"
bar
\"
"
,
"value (string):
bar
"
,
"}"
,
"key: 1group"
,
"{"
,
...
...
@@ -162,7 +162,7 @@ const auto conf0_log = make_log(
"key: padding"
,
"value (integer): 10"
,
"key: file-name"
,
"value (string):
\"
foobar.ini
\"
"
,
"value (string):
foobar.ini
"
,
"}"
,
"key: scheduler"
,
"{"
,
...
...
@@ -184,7 +184,7 @@ const auto conf0_log = make_log(
"value (integer): 23"
,
"value (integer): 2"
,
"value (integer): 4"
,
"value (string):
\"
abc
\"
"
,
"value (string):
abc
"
,
"]"
,
"key: some-map"
,
"{"
,
...
...
@@ -193,7 +193,7 @@ const auto conf0_log = make_log(
"key: entry2"
,
"value (integer): 23"
,
"key: entry3"
,
"value (string):
\"
abc
\"
"
,
"value (string):
abc
"
,
"}"
,
"key: middleman"
,
"{"
,
...
...
libcaf_core/test/load_inspector.cpp
View file @
3f59e8d4
...
...
@@ -58,7 +58,7 @@ struct testee : deserializer {
return
false
;
}
bool
begin_object
(
string_view
object_name
)
override
{
bool
begin_object
(
type_id_t
,
string_view
object_name
)
override
{
new_line
();
indent
+=
2
;
log
+=
"begin object "
;
...
...
libcaf_core/test/save_inspector.cpp
View file @
3f59e8d4
...
...
@@ -49,15 +49,7 @@ struct testee : serializer {
log
.
insert
(
log
.
end
(),
indent
,
' '
);
}
bool
inject_next_object_type
(
type_id_t
type
)
override
{
new_line
();
log
+=
"next object type: "
;
auto
tn
=
detail
::
global_meta_object
(
type
)
->
type_name
;
log
.
insert
(
log
.
end
(),
tn
.
begin
(),
tn
.
end
());
return
true
;
}
bool
begin_object
(
string_view
object_name
)
override
{
bool
begin_object
(
type_id_t
,
string_view
object_name
)
override
{
new_line
();
indent
+=
2
;
log
+=
"begin object "
;
...
...
@@ -734,18 +726,11 @@ end object)_");
f
.
set_has_human_readable_format
(
true
);
CAF_CHECK
(
inspect
(
f
,
x
));
CAF_CHECK_EQUAL
(
f
.
log
,
R"_(
begin object message
begin field values
begin sequence of size 3
next object type: int32_t
int32_t value
next object type: std::string
std::string value
next object type: double
double value
end sequence
end field
end object)_"
);
begin sequence of size 3
int32_t value
std::string value
double value
end sequence)_"
);
}
CAF_TEST_FIXTURE_SCOPE_END
()
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