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
dbdf9b26
Unverified
Commit
dbdf9b26
authored
Jan 05, 2021
by
Noir
Committed by
GitHub
Jan 05, 2021
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1194
Re-implement config_option using get_as
parents
3f59e8d4
f4c56936
Changes
54
Expand all
Hide whitespace changes
Inline
Side-by-side
Showing
54 changed files
with
1208 additions
and
1903 deletions
+1208
-1903
CHANGELOG.md
CHANGELOG.md
+21
-3
libcaf_core/caf/actor_system_config.hpp
libcaf_core/caf/actor_system_config.hpp
+16
-18
libcaf_core/caf/config_option.hpp
libcaf_core/caf/config_option.hpp
+19
-19
libcaf_core/caf/config_option_adder.hpp
libcaf_core/caf/config_option_adder.hpp
+2
-6
libcaf_core/caf/config_value.hpp
libcaf_core/caf/config_value.hpp
+153
-795
libcaf_core/caf/detail/config_consumer.hpp
libcaf_core/caf/detail/config_consumer.hpp
+7
-3
libcaf_core/caf/detail/dispatch_parse_cli.hpp
libcaf_core/caf/detail/dispatch_parse_cli.hpp
+0
-61
libcaf_core/caf/detail/parse.hpp
libcaf_core/caf/detail/parse.hpp
+12
-104
libcaf_core/caf/detail/parser/read_config.hpp
libcaf_core/caf/detail/parser/read_config.hpp
+26
-0
libcaf_core/caf/detail/stringification_inspector.hpp
libcaf_core/caf/detail/stringification_inspector.hpp
+7
-0
libcaf_core/caf/detail/type_traits.hpp
libcaf_core/caf/detail/type_traits.hpp
+38
-0
libcaf_core/caf/make_config_option.hpp
libcaf_core/caf/make_config_option.hpp
+24
-46
libcaf_core/caf/parser_state.hpp
libcaf_core/caf/parser_state.hpp
+4
-0
libcaf_core/caf/sec.hpp
libcaf_core/caf/sec.hpp
+2
-0
libcaf_core/caf/settings.hpp
libcaf_core/caf/settings.hpp
+36
-22
libcaf_core/caf/telemetry/histogram.hpp
libcaf_core/caf/telemetry/histogram.hpp
+1
-1
libcaf_core/caf/telemetry/metric_registry.hpp
libcaf_core/caf/telemetry/metric_registry.hpp
+1
-1
libcaf_core/src/actor_system.cpp
libcaf_core/src/actor_system.cpp
+2
-2
libcaf_core/src/actor_system_config.cpp
libcaf_core/src/actor_system_config.cpp
+8
-8
libcaf_core/src/config_option.cpp
libcaf_core/src/config_option.cpp
+11
-10
libcaf_core/src/config_option_adder.cpp
libcaf_core/src/config_option_adder.cpp
+5
-0
libcaf_core/src/config_option_set.cpp
libcaf_core/src/config_option_set.cpp
+26
-16
libcaf_core/src/config_value.cpp
libcaf_core/src/config_value.cpp
+83
-14
libcaf_core/src/config_value_reader.cpp
libcaf_core/src/config_value_reader.cpp
+2
-2
libcaf_core/src/config_value_writer.cpp
libcaf_core/src/config_value_writer.cpp
+2
-1
libcaf_core/src/detail/config_consumer.cpp
libcaf_core/src/detail/config_consumer.cpp
+15
-26
libcaf_core/src/detail/parse.cpp
libcaf_core/src/detail/parse.cpp
+54
-9
libcaf_core/src/logger.cpp
libcaf_core/src/logger.cpp
+1
-1
libcaf_core/src/make_config_option.cpp
libcaf_core/src/make_config_option.cpp
+23
-57
libcaf_core/src/scheduler/abstract_coordinator.cpp
libcaf_core/src/scheduler/abstract_coordinator.cpp
+2
-4
libcaf_core/src/sec_strings.cpp
libcaf_core/src/sec_strings.cpp
+6
-0
libcaf_core/src/settings.cpp
libcaf_core/src/settings.cpp
+6
-6
libcaf_core/test/actor_system_config.cpp
libcaf_core/test/actor_system_config.cpp
+10
-2
libcaf_core/test/config_option.cpp
libcaf_core/test/config_option.cpp
+162
-12
libcaf_core/test/config_option_set.cpp
libcaf_core/test/config_option_set.cpp
+21
-33
libcaf_core/test/config_value.cpp
libcaf_core/test/config_value.cpp
+89
-378
libcaf_core/test/config_value_writer.cpp
libcaf_core/test/config_value_writer.cpp
+25
-32
libcaf_core/test/core-test.cpp
libcaf_core/test/core-test.cpp
+45
-0
libcaf_core/test/core-test.hpp
libcaf_core/test/core-test.hpp
+11
-15
libcaf_core/test/detail/config_consumer.cpp
libcaf_core/test/detail/config_consumer.cpp
+5
-6
libcaf_core/test/detail/parse.cpp
libcaf_core/test/detail/parse.cpp
+5
-32
libcaf_core/test/detail/parser/read_bool.cpp
libcaf_core/test/detail/parser/read_bool.cpp
+16
-15
libcaf_core/test/load_inspector.cpp
libcaf_core/test/load_inspector.cpp
+2
-1
libcaf_core/test/message.cpp
libcaf_core/test/message.cpp
+3
-3
libcaf_core/test/message_lifetime.cpp
libcaf_core/test/message_lifetime.cpp
+2
-1
libcaf_core/test/mixin/requester.cpp
libcaf_core/test/mixin/requester.cpp
+7
-7
libcaf_core/test/settings.cpp
libcaf_core/test/settings.cpp
+36
-70
libcaf_core/test/span.cpp
libcaf_core/test/span.cpp
+8
-8
libcaf_core/test/variant.cpp
libcaf_core/test/variant.cpp
+6
-12
libcaf_io/src/io/basp/instance.cpp
libcaf_io/src/io/basp/instance.cpp
+3
-3
libcaf_io/src/io/middleman.cpp
libcaf_io/src/io/middleman.cpp
+1
-1
libcaf_test/caf/test/bdd_dsl.hpp
libcaf_test/caf/test/bdd_dsl.hpp
+5
-0
libcaf_test/caf/test/unit_test.hpp
libcaf_test/caf/test/unit_test.hpp
+86
-37
libcaf_test/caf/test/unit_test_impl.hpp
libcaf_test/caf/test/unit_test_impl.hpp
+45
-0
No files found.
CHANGELOG.md
View file @
dbdf9b26
...
...
@@ -16,6 +16,18 @@ is based on [Keep a Changelog](https://keepachangelog.com).
and
`caf.middleman.serialization-time`
.
-
The macro
`CAF_ADD_TYPE_ID`
now accepts an optional third parameter for
allowing users to override the default type name.
-
The new function pair
`get_as`
and
`get_or`
model type conversions on a
`config_value`
. For example,
`get_as<int>(x)`
would convert the content of
`x`
to an
`int`
by either casting numeric values to
`int`
(with bound checks) or
trying to parse the input of
`x`
if it contains a string. The function
`get_or`
already existed for
`settings`
, but we have added new overloads for
generalizing the function to
`config_value`
as well.
### Deprecated
-
The new
`get_as`
and
`get_or`
function pair makes type conversions on a
`config_value`
via
`get`
,
`get_if`
, etc. obsolete. We will retain the
STL-style interface for treating a
`config_value`
as a
`variant`
-like type.
### Changed
...
...
@@ -23,9 +35,15 @@ is based on [Keep a Changelog](https://keepachangelog.com).
i.e.,
`caf-application.conf`
.
-
Simplify the type inspection API by removing the distinction between
`apply_object`
and
`apply_value`
. Instead, inspectors only offer
`apply`
and
users may now also call
`map`
,
`list`
,
`tuple`
and
`value`
for unboxing simple
wrapper types. Furthermore, CAF no longer automatically serializes enumeration
types using their underlying value because this is fundamentally unsafe.
users may now also call
`map`
,
`list`
, and
`tuple`
for unboxing simple wrapper
types. Furthermore, CAF no longer automatically serializes enumeration types
using their underlying value because this is fundamentally unsafe.
-
CAF no longer parses the input to string options on the command line. For
example,
`my_app '--msg="hello"'`
results in CAF storing
`"hello"`
(including
the quotes) for the config option
`msg`
. Previously, CAF tried to parse any
string input on the command-line that starts with quotes in the same way it
would parse strings from a config file, leading to very unintuitive results in
some cases (#1113).
### Fixed
...
...
libcaf_core/caf/actor_system_config.hpp
View file @
dbdf9b26
...
...
@@ -325,6 +325,13 @@ private:
/// Returns all user-provided configuration parameters.
CAF_CORE_EXPORT
const
settings
&
content
(
const
actor_system_config
&
cfg
);
/// Returns whether `xs` associates a value of type `T` to `name`.
/// @relates actor_system_config
template
<
class
T
>
bool
holds_alternative
(
const
actor_system_config
&
cfg
,
string_view
name
)
{
return
holds_alternative
<
T
>
(
content
(
cfg
),
name
);
}
/// Tries to retrieve the value associated to `name` from `cfg`.
/// @relates actor_system_config
template
<
class
T
>
...
...
@@ -339,29 +346,20 @@ T get(const actor_system_config& cfg, string_view name) {
return
get
<
T
>
(
content
(
cfg
),
name
);
}
/// Retrieves the value associated to `name` from `cfg` or returns
/// `default_value`.
/// Retrieves the value associated to `name` from `cfg` or returns `fallback`.
/// @relates actor_system_config
template
<
class
T
,
class
=
typename
std
::
enable_if
<
!
std
::
is_pointer
<
T
>
::
value
&&
!
std
::
is_convertible
<
T
,
string_view
>::
value
>::
type
>
T
get_or
(
const
actor_system_config
&
cfg
,
string_view
name
,
T
default_value
)
{
return
get_or
(
content
(
cfg
),
name
,
std
::
move
(
default_value
));
template
<
class
To
=
get_or_auto_deduce
,
class
Fallback
>
auto
get_or
(
const
actor_system_config
&
cfg
,
string_view
name
,
Fallback
&&
fallback
)
{
return
get_or
<
To
>
(
content
(
cfg
),
name
,
std
::
forward
<
Fallback
>
(
fallback
));
}
/// Retrieves the value associated to `name` from `cfg` or returns
/// `default_value`.
/// @relates actor_system_config
inline
std
::
string
get_or
(
const
actor_system_config
&
cfg
,
string_view
name
,
string_view
default_value
)
{
return
get_or
(
content
(
cfg
),
name
,
default_value
);
}
/// Returns whether `xs` associates a value of type `T` to `name`.
/// Tries to retrieve the value associated to `name` from `cfg` as an instance
/// of type `T`.
/// @relates actor_system_config
template
<
class
T
>
bool
holds_alternative
(
const
actor_system_config
&
cfg
,
string_view
name
)
{
return
holds_alternative
<
T
>
(
content
(
cfg
),
name
);
expected
<
T
>
get_as
(
const
actor_system_config
&
cfg
,
string_view
name
)
{
return
get_as
<
T
>
(
content
(
cfg
),
name
);
}
}
// namespace caf
libcaf_core/caf/config_option.hpp
View file @
dbdf9b26
...
...
@@ -36,22 +36,19 @@ public:
/// Custom vtable-like struct for delegating to type-specific functions and
/// storing type-specific information shared by several config options.
struct
meta_state
{
/// Checks whether a value matches the option's type.
error
(
*
check
)(
const
config_value
&
);
/// Stores a value in the given location.
void
(
*
store
)(
void
*
,
const
config_value
&
);
/// Tries to perform this sequence of steps:
/// - Convert the config value to the type of the config option.
/// - Assign the converted value back to the config value to synchronize
/// conversions back to the caller.
/// - Store the converted value in the pointer unless it is `nullptr`.
error
(
*
sync
)(
void
*
,
config_value
&
);
/// Tries to extract a value from the given location. Exists for backward
/// compatibility only and will get removed with CAF 0.17.
config_value
(
*
get
)(
const
void
*
);
/// Tries to parse an input string. Stores and returns the parsed value on
/// success, returns an error otherwise.
expected
<
config_value
>
(
*
parse
)(
void
*
,
string_view
);
/// Human-readable name of the option's type.
st
d
::
string
type_name
;
st
ring_view
type_name
;
};
// -- constructors, destructors, and assignment operators --------------------
...
...
@@ -89,12 +86,19 @@ public:
/// Returns the full name for this config option as "<category>.<long name>".
string_view
full_name
()
const
noexcept
;
/// Checks whether `x` holds a legal value for this option.
error
check
(
const
config_value
&
x
)
const
;
/// Synchronizes the value of this config option with `x` and vice versa.
///
/// Tries to perform this sequence of steps:
/// - Convert the config value to the type of the config option.
/// - Assign the converted value back to the config value to synchronize
/// conversions back to the caller.
/// - Store the converted value unless this option is stateless.
error
sync
(
config_value
&
x
)
const
;
/// Stores `x` in this option unless it is stateless.
/// @pre `check(x) == none`.
void
store
(
const
config_value
&
x
)
const
;
[[
deprecated
(
"use sync instead"
)]]
error
store
(
const
config_value
&
x
)
const
;
[[
deprecated
(
"use sync instead"
)]]
expected
<
config_value
>
parse
(
string_view
input
)
const
;
/// Returns a human-readable representation of this option's expected type.
string_view
type_name
()
const
noexcept
;
...
...
@@ -105,10 +109,6 @@ public:
/// Returns whether the category is optional for CLI options.
bool
has_flat_cli_name
()
const
noexcept
;
/// Tries to parse an input string. Stores and returns the parsed value on
/// success, returns an error otherwise.
expected
<
config_value
>
parse
(
string_view
input
)
const
;
/// @private
// TODO: remove with CAF 0.17
optional
<
config_value
>
get
()
const
;
...
...
libcaf_core/caf/config_option_adder.hpp
View file @
dbdf9b26
...
...
@@ -52,14 +52,10 @@ public:
config_option_adder
&
add_neg
(
bool
&
ref
,
string_view
name
,
string_view
description
);
/// For backward compatibility only. Do not use for new code!
/// @private
config_option_adder
&
[[
deprecated
(
"use timespan options instead"
)]]
config_option_adder
&
add_us
(
size_t
&
ref
,
string_view
name
,
string_view
description
);
/// For backward compatibility only. Do not use for new code!
/// @private
config_option_adder
&
[[
deprecated
(
"use timespan options instead"
)]]
config_option_adder
&
add_ms
(
size_t
&
ref
,
string_view
name
,
string_view
description
);
private:
...
...
libcaf_core/caf/config_value.hpp
View file @
dbdf9b26
This diff is collapsed.
Click to expand it.
libcaf_core/caf/detail/config_consumer.hpp
View file @
dbdf9b26
...
...
@@ -36,6 +36,8 @@ class CAF_CORE_EXPORT config_list_consumer {
public:
// -- constructors, destructors, and assignment operators --------------------
config_list_consumer
()
=
default
;
config_list_consumer
(
const
config_option_set
*
options
,
config_consumer
*
parent
);
...
...
@@ -60,18 +62,20 @@ public:
template
<
class
T
>
void
value
(
T
&&
x
)
{
xs_
.
emplace_back
(
std
::
forward
<
T
>
(
x
));
result
.
emplace_back
(
std
::
forward
<
T
>
(
x
));
}
config_value
::
list
result
;
std
::
string
qualified_key
();
private:
// -- member variables -------------------------------------------------------
const
config_option_set
*
options_
=
nullptr
;
variant
<
config_consumer
*
,
config_list_consumer
*
,
config_value_consumer
*>
variant
<
none_t
,
config_consumer
*
,
config_list_consumer
*
,
config_value_consumer
*>
parent_
;
config_value
::
list
xs_
;
};
/// Consumes a series of key-value pairs from an application configuration.
...
...
libcaf_core/caf/detail/dispatch_parse_cli.hpp
deleted
100644 → 0
View file @
3f59e8d4
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2019 Dominik Charousset *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#pragma once
#include <type_traits>
#include "caf/config_value.hpp"
#include "caf/detail/type_traits.hpp"
namespace
caf
::
detail
{
template
<
class
Trait
>
struct
dispatch_parse_cli_helper
{
template
<
class
...
Ts
>
auto
operator
()(
Ts
&&
...
xs
)
->
decltype
(
Trait
::
parse_cli
(
std
::
forward
<
Ts
>
(
xs
)...))
{
return
Trait
::
parse_cli
(
std
::
forward
<
Ts
>
(
xs
)...);
}
};
template
<
class
Access
,
class
T
>
void
dispatch_parse_cli
(
std
::
true_type
,
string_parser_state
&
ps
,
T
&
x
,
const
char
*
char_blacklist
)
{
Access
::
parse_cli
(
ps
,
x
,
char_blacklist
);
}
template
<
class
Access
,
class
T
>
void
dispatch_parse_cli
(
std
::
false_type
,
string_parser_state
&
ps
,
T
&
x
,
const
char
*
)
{
Access
::
parse_cli
(
ps
,
x
);
}
template
<
class
T
>
void
dispatch_parse_cli
(
string_parser_state
&
ps
,
T
&
x
,
const
char
*
char_blacklist
)
{
using
access
=
caf
::
select_config_value_access_t
<
T
>
;
using
helper_fun
=
dispatch_parse_cli_helper
<
access
>
;
using
token_type
=
bool_token
<
detail
::
is_callable_with
<
helper_fun
,
string_parser_state
&
,
T
&
,
const
char
*>::
value
>
;
token_type
token
;
dispatch_parse_cli
<
access
>
(
token
,
ps
,
x
,
char_blacklist
);
}
}
// namespace caf::detail
libcaf_core/caf/detail/parse.hpp
View file @
dbdf9b26
...
...
@@ -101,21 +101,25 @@ CAF_CORE_EXPORT void parse(string_parser_state& ps, long double& x);
// -- CAF types ----------------------------------------------------------------
CAF_CORE_EXPORT
void
parse
(
string_parser_state
&
ps
,
ipv4_address
&
x
);
CAF_CORE_EXPORT
void
parse
(
string_parser_state
&
,
ipv4_address
&
);
CAF_CORE_EXPORT
void
parse
(
string_parser_state
&
ps
,
ipv4_subnet
&
x
);
CAF_CORE_EXPORT
void
parse
(
string_parser_state
&
,
ipv4_subnet
&
);
CAF_CORE_EXPORT
void
parse
(
string_parser_state
&
ps
,
ipv4_endpoint
&
x
);
CAF_CORE_EXPORT
void
parse
(
string_parser_state
&
,
ipv4_endpoint
&
);
CAF_CORE_EXPORT
void
parse
(
string_parser_state
&
ps
,
ipv6_address
&
x
);
CAF_CORE_EXPORT
void
parse
(
string_parser_state
&
,
ipv6_address
&
);
CAF_CORE_EXPORT
void
parse
(
string_parser_state
&
ps
,
ipv6_subnet
&
x
);
CAF_CORE_EXPORT
void
parse
(
string_parser_state
&
,
ipv6_subnet
&
);
CAF_CORE_EXPORT
void
parse
(
string_parser_state
&
ps
,
ipv6_endpoint
&
x
);
CAF_CORE_EXPORT
void
parse
(
string_parser_state
&
,
ipv6_endpoint
&
);
CAF_CORE_EXPORT
void
parse
(
string_parser_state
&
ps
,
uri
&
x
);
CAF_CORE_EXPORT
void
parse
(
string_parser_state
&
,
uri
&
);
CAF_CORE_EXPORT
void
parse
(
string_parser_state
&
ps
,
config_value
&
x
);
CAF_CORE_EXPORT
void
parse
(
string_parser_state
&
,
config_value
&
);
CAF_CORE_EXPORT
void
parse
(
string_parser_state
&
,
std
::
vector
<
config_value
>&
);
CAF_CORE_EXPORT
void
parse
(
string_parser_state
&
,
dictionary
<
config_value
>&
);
// -- variadic utility ---------------------------------------------------------
...
...
@@ -209,102 +213,6 @@ void parse(string_parser_state& ps,
x
=
value_type
{
since_epoch
};
}
// -- container types ----------------------------------------------------------
CAF_CORE_EXPORT
void
parse_element
(
string_parser_state
&
ps
,
std
::
string
&
x
,
const
char
*
char_blacklist
);
template
<
class
T
>
enable_if_t
<!
is_pair
<
T
>::
value
>
parse_element
(
string_parser_state
&
ps
,
T
&
x
,
const
char
*
);
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
]);
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
?
'{'
:
'['
;
static
constexpr
auto
closing_char
=
is_map_type
?
'}'
:
']'
;
auto
out
=
std
::
inserter
(
xs
,
xs
.
end
());
// List/map using [] or {} notation.
if
(
ps
.
consume
(
opening_char
))
{
char
char_blacklist
[]
=
{
closing_char
,
','
,
'\0'
};
do
{
if
(
ps
.
consume
(
closing_char
))
{
ps
.
skip_whitespaces
();
ps
.
code
=
ps
.
at_end
()
?
pec
::
success
:
pec
::
trailing_character
;
return
;
}
value_type
tmp
;
parse_element
(
ps
,
tmp
,
char_blacklist
);
if
(
ps
.
code
>
pec
::
trailing_character
)
return
;
*
out
++
=
std
::
move
(
tmp
);
}
while
(
ps
.
consume
(
','
));
if
(
ps
.
consume
(
closing_char
))
{
ps
.
skip_whitespaces
();
ps
.
code
=
ps
.
at_end
()
?
pec
::
success
:
pec
::
trailing_character
;
}
else
{
ps
.
code
=
pec
::
unexpected_character
;
}
return
;
}
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
;
// 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
>
enable_if_t
<!
is_pair
<
T
>::
value
>
parse_element
(
string_parser_state
&
ps
,
T
&
x
,
const
char
*
)
{
parse
(
ps
,
x
);
}
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
])
{
static_assert
(
N
>
0
,
"empty array"
);
// TODO: consider to guard the blacklist computation with
// `if constexpr (is_same_v<First, string>)` when switching to C++17.
char
key_blacklist
[
N
+
1
];
if
(
N
>
1
)
memcpy
(
key_blacklist
,
char_blacklist
,
N
-
1
);
key_blacklist
[
N
-
1
]
=
'='
;
key_blacklist
[
N
]
=
'\0'
;
parse_element
(
ps
,
kvp
.
first
,
key_blacklist
);
if
(
ps
.
code
>
pec
::
trailing_character
)
return
;
if
(
!
ps
.
consume
(
'='
))
{
ps
.
code
=
pec
::
unexpected_character
;
return
;
}
parse_element
(
ps
,
kvp
.
second
,
char_blacklist
);
}
// -- convenience functions ----------------------------------------------------
CAF_CORE_EXPORT
...
...
libcaf_core/caf/detail/parser/read_config.hpp
View file @
dbdf9b26
...
...
@@ -102,6 +102,28 @@ void read_config_list(State& ps, Consumer&& consumer) {
// clang-format on
}
// Like read_config_list, but without surrounding '[]'.
template
<
class
State
,
class
Consumer
>
void
lift_config_list
(
State
&
ps
,
Consumer
&&
consumer
)
{
// clang-format off
start
();
state
(
init
)
{
epsilon
(
before_value
)
}
term_state
(
before_value
)
{
transition
(
before_value
,
"
\t\n
"
)
fsm_epsilon
(
read_config_comment
(
ps
,
consumer
),
before_value
,
'#'
)
fsm_epsilon
(
read_config_value
(
ps
,
consumer
,
std
::
true_type
{}),
after_value
)
}
term_state
(
after_value
)
{
transition
(
after_value
,
"
\t\n
"
)
transition
(
before_value
,
','
)
fsm_epsilon
(
read_config_comment
(
ps
,
consumer
),
after_value
,
'#'
)
}
fin
();
// clang-format on
}
template
<
bool
Nested
=
true
,
class
State
,
class
Consumer
>
void
read_config_map
(
State
&
ps
,
Consumer
&&
consumer
)
{
std
::
string
key
;
...
...
@@ -140,6 +162,7 @@ void read_config_map(State& ps, Consumer&& consumer) {
unstable_state
(
after_value
)
{
transition
(
after_value
,
"
\t
"
)
transition
(
had_newline
,
"
\n
"
)
transition_if
(
!
Nested
,
after_comma
,
','
)
transition
(
await_key_name
,
','
)
transition_if
(
Nested
,
done
,
'}'
,
consumer
.
end_map
())
fsm_epsilon
(
read_config_comment
(
ps
,
consumer
),
had_newline
,
'#'
)
...
...
@@ -157,6 +180,9 @@ void read_config_map(State& ps, Consumer&& consumer) {
epsilon_if
(
!
Nested
,
done
)
epsilon
(
unexpected_end_of_input
)
}
term_state
(
after_comma
)
{
epsilon
(
await_key_name
)
}
state
(
unexpected_end_of_input
)
{
// no transitions, only needed for the unstable states
}
...
...
libcaf_core/caf/detail/stringification_inspector.hpp
View file @
dbdf9b26
...
...
@@ -189,6 +189,13 @@ public:
return
true
;
}
else
if
constexpr
(
std
::
is_same
<
T
,
char
>::
value
)
{
return
value
(
string_view
{
x
,
strlen
(
x
)});
}
else
if
constexpr
(
std
::
is_same
<
T
,
void
>::
value
)
{
sep
();
result_
+=
"*<"
;
auto
addr
=
reinterpret_cast
<
intptr_t
>
(
x
);
result_
+=
std
::
to_string
(
addr
);
result_
+=
'>'
;
return
true
;
}
else
{
sep
();
result_
+=
'*'
;
...
...
libcaf_core/caf/detail/type_traits.hpp
View file @
dbdf9b26
...
...
@@ -685,6 +685,44 @@ public:
static
constexpr
bool
value
=
sfinae_type
::
value
;
};
template
<
class
T
>
struct
has_reserve
{
private:
template
<
class
List
>
static
auto
sfinae
(
List
*
l
)
->
decltype
(
l
->
reserve
(
10
),
std
::
true_type
());
template
<
class
U
>
static
auto
sfinae
(...)
->
std
::
false_type
;
using
sfinae_type
=
decltype
(
sfinae
<
T
>
(
nullptr
));
public:
static
constexpr
bool
value
=
sfinae_type
::
value
;
};
template
<
class
T
>
constexpr
bool
has_reserve_v
=
has_reserve
<
T
>::
value
;
template
<
class
T
>
struct
has_emplace_back
{
private:
template
<
class
List
>
static
auto
sfinae
(
List
*
l
)
->
decltype
(
l
->
emplace_back
(
std
::
declval
<
typename
List
::
value_type
>
()),
std
::
true_type
());
template
<
class
U
>
static
auto
sfinae
(...)
->
std
::
false_type
;
using
sfinae_type
=
decltype
(
sfinae
<
T
>
(
nullptr
));
public:
static
constexpr
bool
value
=
sfinae_type
::
value
;
};
template
<
class
T
>
constexpr
bool
has_emplace_back_v
=
has_emplace_back
<
T
>::
value
;
template
<
class
T
>
class
has_call_error_handler
{
private:
...
...
libcaf_core/caf/make_config_option.hpp
View file @
dbdf9b26
...
...
@@ -22,7 +22,6 @@
#include "caf/config_option.hpp"
#include "caf/config_value.hpp"
#include "caf/detail/parse.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/error.hpp"
#include "caf/expected.hpp"
...
...
@@ -30,62 +29,41 @@
#include "caf/pec.hpp"
#include "caf/string_view.hpp"
namespace
caf
{
namespace
detail
{
template
<
class
T
>
error
check_impl
(
const
config_value
&
x
)
{
if
(
holds_alternative
<
T
>
(
x
))
return
none
;
return
make_error
(
pec
::
type_mismatch
);
}
template
<
class
T
>
void
store_impl
(
void
*
ptr
,
const
config_value
&
x
)
{
*
static_cast
<
T
*>
(
ptr
)
=
get
<
T
>
(
x
);
}
template
<
class
T
>
config_value
get_impl
(
const
void
*
ptr
)
{
using
trait
=
detail
::
config_value_access_t
<
T
>
;
return
config_value
{
trait
::
convert
(
*
reinterpret_cast
<
const
T
*>
(
ptr
))};
}
namespace
caf
::
detail
{
template
<
class
T
>
expected
<
config_value
>
parse_impl
(
T
*
ptr
,
string_view
str
)
{
if
(
!
ptr
)
{
T
tmp
;
return
parse_impl
(
&
tmp
,
str
);
error
sync_impl
(
void
*
ptr
,
config_value
&
x
)
{
if
(
auto
val
=
get_as
<
T
>
(
x
))
{
if
(
auto
err
=
x
.
assign
(
*
val
);
!
err
)
{
if
(
ptr
)
*
static_cast
<
T
*>
(
ptr
)
=
std
::
move
(
*
val
);
return
none
;
}
else
{
return
err
;
}
}
else
{
return
std
::
move
(
val
.
error
());
}
if
constexpr
(
detail
::
has_clear_member
<
T
>::
value
)
ptr
->
clear
();
using
trait
=
detail
::
config_value_access_t
<
T
>
;
string_parser_state
ps
{
str
.
begin
(),
str
.
end
()};
trait
::
parse_cli
(
ps
,
*
ptr
,
top_level_cli_parsing
);
if
(
ps
.
code
!=
pec
::
success
)
return
make_error
(
ps
);
return
config_value
{
trait
::
convert
(
*
ptr
)};
}
CAF_CORE_EXPORT
expected
<
config_value
>
parse_impl
(
std
::
string
*
ptr
,
string_view
str
);
template
<
class
T
>
expected
<
config_value
>
parse_impl_delegate
(
void
*
ptr
,
string_view
str
)
{
return
parse_impl
(
reinterpret_cast
<
T
*>
(
ptr
),
str
);
config_value
get_impl
(
const
void
*
ptr
)
{
config_value
result
;
auto
err
=
result
.
assign
(
*
static_cast
<
const
T
*>
(
ptr
));
static_cast
<
void
>
(
err
);
// Safe to discard because sync() fails otherwise.
return
result
;
}
template
<
class
T
>
config_option
::
meta_state
*
option_meta_state_instance
()
{
using
trait
=
detail
::
config_value_access_t
<
T
>
;
static
config_option
::
meta_state
obj
{
check_impl
<
T
>
,
store_impl
<
T
>
,
get_impl
<
T
>
,
parse_impl_delegate
<
T
>
,
trait
::
type_name
()};
static
config_option
::
meta_state
obj
{
sync_impl
<
T
>
,
get_impl
<
T
>
,
config_value
::
mapped_type_name
<
T
>
()};
return
&
obj
;
}
}
// namespace detail
}
// namespace caf::detail
namespace
caf
{
/// Creates a config option that synchronizes with `storage`.
template
<
class
T
>
...
...
@@ -110,12 +88,12 @@ make_negated_config_option(bool& storage, string_view category,
string_view
name
,
string_view
description
);
// Reads timespans, but stores an integer representing microsecond resolution.
CAF_CORE_EXPORT
config_option
[[
deprecated
(
"use timespan options instead"
)]]
CAF_CORE_EXPORT
config_option
make_us_resolution_config_option
(
size_t
&
storage
,
string_view
category
,
string_view
name
,
string_view
description
);
// Reads timespans, but stores an integer representing millisecond resolution.
CAF_CORE_EXPORT
config_option
[[
deprecated
(
"use timespan options instead"
)]]
CAF_CORE_EXPORT
config_option
make_ms_resolution_config_option
(
size_t
&
storage
,
string_view
category
,
string_view
name
,
string_view
description
);
...
...
libcaf_core/caf/parser_state.hpp
View file @
dbdf9b26
...
...
@@ -58,6 +58,10 @@ struct parser_state {
e
=
last
;
}
parser_state
(
const
parser_state
&
)
noexcept
=
default
;
parser_state
&
operator
=
(
const
parser_state
&
)
noexcept
=
default
;
/// Returns the null terminator when reaching the end of the string,
/// otherwise the next character.
char
next
()
noexcept
{
...
...
libcaf_core/caf/sec.hpp
View file @
dbdf9b26
...
...
@@ -170,6 +170,8 @@ enum class sec : uint8_t {
/// An operation failed because the callee does not implement this
/// functionality.
unsupported_operation
,
/// A key lookup failed.
no_such_key
=
65
,
};
/// @relates sec
...
...
libcaf_core/caf/settings.hpp
View file @
dbdf9b26
...
...
@@ -38,8 +38,8 @@ 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
*
get_if
(
const
settings
*
xs
,
string_view
name
);
CAF_CORE_EXPORT
const
config_value
*
get_if
(
const
settings
*
xs
,
string_view
name
);
/// Tries to retrieve the value associated to `name` from `xs`.
/// @relates config_value
...
...
@@ -54,12 +54,14 @@ auto get_if(const settings* xs, string_view name) {
/// @relates config_value
template
<
class
T
>
bool
holds_alternative
(
const
settings
&
xs
,
string_view
name
)
{
using
access
=
detail
::
config_value_access_t
<
T
>
;
if
(
auto
value
=
get_if
(
&
xs
,
name
))
return
access
::
is
(
*
value
);
return
false
;
return
holds_alternative
<
T
>
(
*
value
);
else
return
false
;
}
/// Retrieves the value associated to `name` from `xs`.
/// @relates actor_system_config
template
<
class
T
>
T
get
(
const
settings
&
xs
,
string_view
name
)
{
auto
result
=
get_if
<
T
>
(
&
xs
,
name
);
...
...
@@ -67,27 +69,39 @@ T get(const settings& xs, string_view name) {
return
detail
::
move_if_not_ptr
(
result
);
}
template
<
class
T
,
class
=
typename
std
::
enable_if
<
!
std
::
is_pointer
<
T
>
::
value
&&
!
std
::
is_convertible
<
T
,
string_view
>::
value
>::
type
>
T
get_or
(
const
settings
&
xs
,
string_view
name
,
T
default_value
)
{
auto
result
=
get_if
<
T
>
(
&
xs
,
name
);
if
(
result
)
return
std
::
move
(
*
result
);
return
default_value
;
/// Retrieves the value associated to `name` from `xs` or returns `fallback`.
/// @relates actor_system_config
template
<
class
To
=
get_or_auto_deduce
,
class
Fallback
>
auto
get_or
(
const
settings
&
xs
,
string_view
name
,
Fallback
&&
fallback
)
{
if
(
auto
ptr
=
get_if
(
&
xs
,
name
))
{
return
get_or
<
To
>
(
*
ptr
,
std
::
forward
<
Fallback
>
(
fallback
));
}
else
if
constexpr
(
std
::
is_same
<
To
,
get_or_auto_deduce
>::
value
)
{
using
guide
=
get_or_deduction_guide
<
std
::
decay_t
<
Fallback
>>
;
return
guide
::
convert
(
std
::
forward
<
Fallback
>
(
fallback
));
}
else
{
return
To
{
std
::
forward
<
Fallback
>
(
fallback
)};
}
}
CAF_CORE_EXPORT
std
::
string
get_or
(
const
settings
&
xs
,
string_view
name
,
string_view
default_value
);
/// Tries to retrieve the value associated to `name` from `xs` as an instance of
/// type `T`.
/// @relates actor_system_config
template
<
class
T
>
expected
<
T
>
get_as
(
const
settings
&
xs
,
string_view
name
)
{
if
(
auto
ptr
=
get_if
(
&
xs
,
name
))
return
get_as
<
T
>
(
*
ptr
);
else
return
{
sec
::
no_such_key
};
}
/// @private
CAF_CORE_EXPORT
config_value
&
put_impl
(
settings
&
dict
,
const
std
::
vector
<
string_view
>&
path
,
config_value
&
value
);
CAF_CORE_EXPORT
config_value
&
put_impl
(
settings
&
dict
,
const
std
::
vector
<
string_view
>&
path
,
config_value
&
value
);
/// @private
CAF_CORE_EXPORT
config_value
&
put_impl
(
settings
&
dict
,
string_view
key
,
config_value
&
value
);
CAF_CORE_EXPORT
config_value
&
put_impl
(
settings
&
dict
,
string_view
key
,
config_value
&
value
);
/// Converts `value` to a `config_value` and assigns it to `key`.
/// @param dict Dictionary of key-value pairs.
...
...
@@ -118,7 +132,7 @@ CAF_CORE_EXPORT config_value::list& put_list(settings& xs, std::string name);
/// Inserts a new list named `name` into the dictionary `xs` and returns
/// a reference to it. Overrides existing entries with the same name.
CAF_CORE_EXPORT
config_value
::
dictionary
&
put_dictionary
(
settings
&
xs
,
std
::
string
name
);
CAF_CORE_EXPORT
config_value
::
dictionary
&
put_dictionary
(
settings
&
xs
,
std
::
string
name
);
}
// namespace caf
libcaf_core/caf/telemetry/histogram.hpp
View file @
dbdf9b26
...
...
@@ -128,7 +128,7 @@ private:
return
false
;
for
(
const
auto
&
lbl
:
labels
)
{
if
(
auto
ptr
=
get_if
<
settings
>
(
cfg
,
lbl
.
str
()))
{
if
(
auto
bounds
=
get_
if
<
std
::
vector
<
value_type
>>
(
ptr
,
"buckets"
))
{
if
(
auto
bounds
=
get_
as
<
std
::
vector
<
value_type
>>
(
*
ptr
,
"buckets"
))
{
std
::
sort
(
bounds
->
begin
(),
bounds
->
end
());
bounds
->
erase
(
std
::
unique
(
bounds
->
begin
(),
bounds
->
end
()),
bounds
->
end
());
...
...
libcaf_core/caf/telemetry/metric_registry.hpp
View file @
dbdf9b26
...
...
@@ -367,7 +367,7 @@ public:
if
(
auto
grp
=
get_if
<
settings
>
(
config_
,
prefix
))
{
if
(
sub_settings
=
get_if
<
settings
>
(
grp
,
name
);
sub_settings
!=
nullptr
)
{
if
(
auto
lst
=
get_
if
<
upper_bounds_list
>
(
sub_settings
,
"buckets"
))
{
if
(
auto
lst
=
get_
as
<
upper_bounds_list
>
(
*
sub_settings
,
"buckets"
))
{
std
::
sort
(
lst
->
begin
(),
lst
->
end
());
lst
->
erase
(
std
::
unique
(
lst
->
begin
(),
lst
->
end
()),
lst
->
end
());
if
(
!
lst
->
empty
())
...
...
libcaf_core/src/actor_system.cpp
View file @
dbdf9b26
...
...
@@ -295,10 +295,10 @@ actor_system::actor_system(actor_system_config& cfg)
hook
->
init
(
*
this
);
// Cache some configuration parameters for faster lookups at runtime.
using
string_list
=
std
::
vector
<
std
::
string
>
;
if
(
auto
lst
=
get_
if
<
string_list
>
(
&
cfg
,
if
(
auto
lst
=
get_
as
<
string_list
>
(
cfg
,
"caf.metrics-filters.actors.includes"
))
metrics_actors_includes_
=
std
::
move
(
*
lst
);
if
(
auto
lst
=
get_
if
<
string_list
>
(
&
cfg
,
if
(
auto
lst
=
get_
as
<
string_list
>
(
cfg
,
"caf.metrics-filters.actors.excludes"
))
metrics_actors_excludes_
=
std
::
move
(
*
lst
);
if
(
!
metrics_actors_includes_
.
empty
())
...
...
libcaf_core/src/actor_system_config.cpp
View file @
dbdf9b26
...
...
@@ -340,11 +340,10 @@ actor_system_config& actor_system_config::set_impl(string_view name,
if
(
opt
==
nullptr
)
{
std
::
cerr
<<
"*** failed to set config parameter "
<<
name
<<
": invalid name"
<<
std
::
endl
;
}
else
if
(
auto
err
=
opt
->
check
(
value
))
{
}
else
if
(
auto
err
=
opt
->
sync
(
value
))
{
std
::
cerr
<<
"*** failed to set config parameter "
<<
name
<<
": "
<<
to_string
(
err
)
<<
std
::
endl
;
}
else
{
opt
->
store
(
value
);
auto
category
=
opt
->
category
();
if
(
category
==
"global"
)
content
[
opt
->
long_name
()]
=
std
::
move
(
value
);
...
...
@@ -413,12 +412,13 @@ error actor_system_config::extract_config_file_path(string_list& args) {
args
.
erase
(
i
);
return
make_error
(
pec
::
missing_argument
,
std
::
move
(
str
));
}
auto
evalue
=
ptr
->
parse
(
path
);
if
(
!
evalue
)
return
std
::
move
(
evalue
.
error
());
put
(
content
,
"config-file"
,
*
evalue
);
ptr
->
store
(
*
evalue
);
return
none
;
config_value
val
{
path
};
if
(
auto
err
=
ptr
->
sync
(
val
);
!
err
)
{
put
(
content
,
"config-file"
,
std
::
move
(
val
));
return
none
;
}
else
{
return
err
;
}
}
const
settings
&
content
(
const
actor_system_config
&
cfg
)
{
...
...
libcaf_core/src/config_option.cpp
View file @
dbdf9b26
...
...
@@ -128,16 +128,13 @@ string_view config_option::full_name() const noexcept {
return
buf_slice
(
buf_
[
0
]
==
'?'
?
1
:
0
,
long_name_separator_
);
}
error
config_option
::
check
(
const
config_value
&
x
)
const
{
CAF_ASSERT
(
meta_
->
check
!=
nullptr
);
return
meta_
->
check
(
x
);
error
config_option
::
sync
(
config_value
&
x
)
const
{
return
meta_
->
sync
(
value_
,
x
);
}
void
config_option
::
store
(
const
config_value
&
x
)
const
{
if
(
value_
!=
nullptr
)
{
CAF_ASSERT
(
meta_
->
store
!=
nullptr
);
meta_
->
store
(
value_
,
x
);
}
error
config_option
::
store
(
const
config_value
&
x
)
const
{
auto
cpy
=
x
;
return
sync
(
cpy
);
}
string_view
config_option
::
type_name
()
const
noexcept
{
...
...
@@ -145,7 +142,7 @@ string_view config_option::type_name() const noexcept {
}
bool
config_option
::
is_flag
()
const
noexcept
{
return
type_name
()
==
"bool
ean
"
;
return
type_name
()
==
"bool"
;
}
bool
config_option
::
has_flat_cli_name
()
const
noexcept
{
...
...
@@ -153,7 +150,11 @@ bool config_option::has_flat_cli_name() const noexcept {
}
expected
<
config_value
>
config_option
::
parse
(
string_view
input
)
const
{
return
meta_
->
parse
(
value_
,
input
);
config_value
val
{
input
};
if
(
auto
err
=
sync
(
val
))
return
{
std
::
move
(
err
)};
else
return
{
std
::
move
(
val
)};
}
optional
<
config_value
>
config_option
::
get
()
const
{
...
...
libcaf_core/src/config_option_adder.cpp
View file @
dbdf9b26
...
...
@@ -18,8 +18,11 @@
#include "caf/config_option_adder.hpp"
#include "caf/config.hpp"
#include "caf/config_option_set.hpp"
CAF_PUSH_DEPRECATED_WARNING
namespace
caf
{
config_option_adder
::
config_option_adder
(
config_option_set
&
target
,
...
...
@@ -53,3 +56,5 @@ config_option_adder& config_option_adder::add_impl(config_option&& opt) {
}
}
// namespace caf
CAF_POP_WARNINGS
libcaf_core/src/config_option_set.cpp
View file @
dbdf9b26
...
...
@@ -137,6 +137,12 @@ auto config_option_set::parse(settings& config, argument_iterator first,
// Parses an argument.
using
iter
=
string
::
const_iterator
;
auto
consume
=
[
&
](
const
config_option
&
opt
,
iter
arg_begin
,
iter
arg_end
)
{
auto
to_pec_code
=
[](
const
error
&
err
)
{
if
(
err
.
category
()
==
type_id_v
<
pec
>
)
return
static_cast
<
pec
>
(
err
.
code
());
else
return
pec
::
invalid_argument
;
};
// Extract option name and category.
auto
opt_name
=
opt
.
long_name
();
auto
opt_ctg
=
opt
.
category
();
...
...
@@ -144,27 +150,31 @@ auto config_option_set::parse(settings& config, argument_iterator first,
auto
&
entry
=
opt_ctg
==
"global"
?
config
:
select_entry
(
config
,
opt_ctg
);
// Flags only consume the current element.
if
(
opt
.
is_flag
())
{
if
(
arg_begin
!=
arg_end
)
if
(
arg_begin
==
arg_end
)
{
config_value
cfg_true
{
true
};
if
(
auto
err
=
opt
.
sync
(
cfg_true
);
!
err
)
{
entry
[
opt_name
]
=
cfg_true
;
return
pec
::
success
;
}
else
{
return
to_pec_code
(
err
);
}
}
else
{
return
pec
::
invalid_argument
;
config_value
cfg_true
{
true
};
opt
.
store
(
cfg_true
);
entry
[
opt_name
]
=
cfg_true
;
}
}
else
{
if
(
arg_begin
==
arg_end
)
if
(
arg_begin
!=
arg_end
)
{
auto
arg_size
=
static_cast
<
size_t
>
(
std
::
distance
(
arg_begin
,
arg_end
));
config_value
val
{
string_view
{
std
::
addressof
(
*
arg_begin
),
arg_size
}};
if
(
auto
err
=
opt
.
sync
(
val
);
!
err
)
{
entry
[
opt_name
]
=
std
::
move
(
val
);
return
pec
::
success
;
}
else
{
return
to_pec_code
(
err
);
}
}
else
{
return
pec
::
missing_argument
;
auto
slice_size
=
static_cast
<
size_t
>
(
std
::
distance
(
arg_begin
,
arg_end
));
string_view
slice
{
&*
arg_begin
,
slice_size
};
auto
val
=
opt
.
parse
(
slice
);
if
(
!
val
)
{
auto
&
err
=
val
.
error
();
if
(
err
.
category
()
==
type_id_v
<
pec
>
)
return
static_cast
<
pec
>
(
err
.
code
());
return
pec
::
invalid_argument
;
}
opt
.
store
(
*
val
);
entry
[
opt_name
]
=
std
::
move
(
*
val
);
}
return
pec
::
success
;
};
// We loop over the first N-1 values, because we always consider two
// arguments at once.
...
...
libcaf_core/src/config_value.cpp
View file @
dbdf9b26
...
...
@@ -62,6 +62,11 @@ auto no_conversions() {
return
detail
::
make_overload
(
no_conversion
<
To
,
From
>
()...);
}
template
<
class
T
>
constexpr
ptrdiff_t
signed_index_of
()
{
return
detail
::
tl_index_of
<
typename
config_value
::
types
,
T
>::
value
;
}
}
// namespace
// -- constructors, destructors, and assignment operators ----------------------
...
...
@@ -153,6 +158,10 @@ const char* config_value::type_name_at_index(size_t index) noexcept {
return
type_names
[
index
];
}
ptrdiff_t
config_value
::
signed_index
()
const
noexcept
{
return
static_cast
<
ptrdiff_t
>
(
data_
.
index
());
}
// -- utility ------------------------------------------------------------------
type_id_t
config_value
::
type_id
()
const
noexcept
{
...
...
@@ -321,6 +330,16 @@ expected<timespan> config_value::to_timespan() const {
return
visit
(
f
,
data_
);
}
expected
<
uri
>
config_value
::
to_uri
()
const
{
using
result_type
=
expected
<
uri
>
;
auto
f
=
detail
::
make_overload
(
no_conversions
<
uri
,
none_t
,
bool
,
integer
,
real
,
timespan
,
config_value
::
list
,
config_value
::
dictionary
>
(),
[](
const
uri
&
x
)
{
return
result_type
{
x
};
},
[](
const
std
::
string
&
x
)
{
return
make_uri
(
x
);
});
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
)
{
...
...
@@ -335,18 +354,17 @@ expected<config_value::list> config_value::to_list() const {
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
();
// Check whether we can parse the string as a list. However, we also
// accept dictionaries that we convert to lists of key-value pairs. We
// need to try converting to dictionary *first*, because detail::parse for
// the list otherwise produces a list with one dictionary.
if
(
config_value
::
dictionary
dict
;
detail
::
parse
(
x
,
dict
)
==
none
)
{
config_value
::
list
tmp
;
dict_to_list
(
dict
,
tmp
);
return
result_type
{
std
::
move
(
tmp
)};
}
if
(
config_value
::
list
tmp
;
detail
::
parse
(
x
,
tmp
)
==
none
)
return
result_type
{
std
::
move
(
tmp
)};
std
::
string
msg
=
"cannot convert "
;
detail
::
print_escaped
(
msg
,
x
);
msg
+=
" to a list"
;
...
...
@@ -383,12 +401,11 @@ expected<config_value::dictionary> config_value::to_dictionary() const {
return
result_type
{
std
::
move
(
err
)};
}
},
[](
const
std
::
string
&
x
)
{
if
(
dictionary
tmp
;
detail
::
parse
(
x
,
tmp
)
==
none
)
{
[
this
](
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
lst
=
to_list
())
{
config_value
ls
{
std
::
move
(
*
lst
)};
if
(
auto
res
=
ls
.
to_dictionary
())
return
res
;
}
...
...
@@ -453,14 +470,66 @@ config_value::parse_msg_impl(string_view str,
// -- related free functions ---------------------------------------------------
bool
operator
<
(
double
x
,
const
config_value
&
y
)
{
return
config_value
{
x
}
<
y
;
}
bool
operator
<=
(
double
x
,
const
config_value
&
y
)
{
return
config_value
{
x
}
<=
y
;
}
bool
operator
==
(
double
x
,
const
config_value
&
y
)
{
return
config_value
{
x
}
==
y
;
}
bool
operator
>
(
double
x
,
const
config_value
&
y
)
{
return
config_value
{
x
}
>
y
;
}
bool
operator
>=
(
double
x
,
const
config_value
&
y
)
{
return
config_value
{
x
}
>=
y
;
}
bool
operator
<
(
const
config_value
&
x
,
double
y
)
{
return
x
<
config_value
{
y
};
}
bool
operator
<=
(
const
config_value
&
x
,
double
y
)
{
return
x
<=
config_value
{
y
};
}
bool
operator
==
(
const
config_value
&
x
,
double
y
)
{
return
x
==
config_value
{
y
};
}
bool
operator
>
(
const
config_value
&
x
,
double
y
)
{
return
x
>
config_value
{
y
};
}
bool
operator
>=
(
const
config_value
&
x
,
double
y
)
{
return
x
>=
config_value
{
y
};
}
bool
operator
<
(
const
config_value
&
x
,
const
config_value
&
y
)
{
return
x
.
get_data
()
<
y
.
get_data
();
}
bool
operator
<=
(
const
config_value
&
x
,
const
config_value
&
y
)
{
return
x
.
get_data
()
<=
y
.
get_data
();
}
bool
operator
==
(
const
config_value
&
x
,
const
config_value
&
y
)
{
return
x
.
get_data
()
==
y
.
get_data
();
}
bool
operator
>
(
const
config_value
&
x
,
const
config_value
&
y
)
{
return
x
.
get_data
()
>
y
.
get_data
();
}
bool
operator
>=
(
const
config_value
&
x
,
const
config_value
&
y
)
{
return
x
.
get_data
()
>=
y
.
get_data
();
}
namespace
{
void
to_string_impl
(
std
::
string
&
str
,
const
config_value
&
x
);
...
...
libcaf_core/src/config_value_reader.cpp
View file @
dbdf9b26
...
...
@@ -428,7 +428,7 @@ bool pull(config_value_reader& reader, T& x) {
auto
&
top
=
reader
.
top
();
if
(
holds_alternative
<
const
config_value
*>
(
top
))
{
auto
ptr
=
get
<
const
config_value
*>
(
top
);
if
(
auto
val
=
get_
if
<
internal_type
>
(
ptr
))
{
if
(
auto
val
=
get_
as
<
internal_type
>
(
*
ptr
))
{
assign
(
*
val
);
reader
.
pop
();
return
true
;
...
...
@@ -446,7 +446,7 @@ bool pull(config_value_reader& reader, T& x) {
return
false
;
}
auto
ptr
=
std
::
addressof
(
seq
.
current
());
if
(
auto
val
=
get_
if
<
internal_type
>
(
ptr
))
{
if
(
auto
val
=
get_
as
<
internal_type
>
(
*
ptr
))
{
assign
(
*
val
);
seq
.
advance
();
return
true
;
...
...
libcaf_core/src/config_value_writer.cpp
View file @
dbdf9b26
...
...
@@ -268,11 +268,12 @@ bool config_value_writer::begin_associative_array(size_t) {
CHECK_NOT_EMPTY
();
settings
*
inner
=
nullptr
;
auto
f
=
detail
::
make_overload
(
[
this
](
config_value
*
val
)
{
[
this
,
&
inner
](
config_value
*
val
)
{
// Morph the top element into a dictionary.
auto
&
dict
=
val
->
as_dictionary
();
dict
.
clear
();
st_
.
top
()
=
&
dict
;
inner
=
&
dict
;
return
true
;
},
[
this
](
settings
*
)
{
...
...
libcaf_core/src/detail/config_consumer.cpp
View file @
dbdf9b26
...
...
@@ -43,14 +43,14 @@ config_list_consumer::config_list_consumer(config_value_consumer* parent)
}
pec
config_list_consumer
::
end_list
()
{
auto
f
=
make_overload
(
[
this
](
config_consumer
*
ptr
)
{
return
ptr
->
value
(
config_value
{
std
::
move
(
xs_
)});
},
[
this
](
auto
*
ptr
)
{
ptr
->
value
(
config_value
{
std
::
move
(
xs_
)});
return
pec
::
success
;
});
auto
f
=
make_overload
(
[](
none_t
)
{
return
pec
::
success
;
},
[
this
](
config_consumer
*
ptr
)
{
return
ptr
->
value
(
config_value
{
std
::
move
(
result
)});
},
[
this
](
auto
*
ptr
)
{
ptr
->
value
(
config_value
{
std
::
move
(
result
)});
return
pec
::
success
;
});
return
visit
(
f
,
parent_
);
}
...
...
@@ -59,7 +59,8 @@ config_consumer config_list_consumer::begin_map() {
}
std
::
string
config_list_consumer
::
qualified_key
()
{
auto
f
=
make_overload
([](
config_value_consumer
*
)
{
return
std
::
string
{};
},
auto
f
=
make_overload
([](
none_t
)
{
return
std
::
string
{};
},
[](
config_value_consumer
*
)
{
return
std
::
string
{};
},
[](
auto
*
ptr
)
{
return
ptr
->
qualified_key
();
});
return
visit
(
f
,
parent_
);
}
...
...
@@ -156,16 +157,11 @@ void merge_into_place(settings& src, settings& dst) {
}
// namespace
pec
config_consumer
::
value_impl
(
config_value
&&
x
)
{
// See whether there's a config_option associated to this key and perform a
// type check if necessary.
const
config_option
*
opt
;
if
(
options_
==
nullptr
)
{
opt
=
nullptr
;
}
else
{
opt
=
options_
->
qualified_name_lookup
(
category_
,
current_key_
);
if
(
opt
&&
opt
->
check
(
x
)
!=
none
)
return
pec
::
type_mismatch
;
}
// Sync with config option object if available.
if
(
options_
!=
nullptr
)
if
(
auto
opt
=
options_
->
qualified_name_lookup
(
category_
,
current_key_
))
if
(
auto
err
=
opt
->
sync
(
x
))
return
pec
::
type_mismatch
;
// Insert / replace value in the map.
if
(
auto
dict
=
get_if
<
settings
>
(
&
x
))
{
// Merge values into the destination, because it can already contain any
...
...
@@ -178,13 +174,6 @@ pec config_consumer::value_impl(config_value&& x) {
}
else
{
cfg_
->
insert_or_assign
(
current_key_
,
std
::
move
(
x
));
}
// Sync with config option if needed.
if
(
opt
)
{
if
(
auto
i
=
cfg_
->
find
(
current_key_
);
i
!=
cfg_
->
end
())
opt
->
store
(
i
->
second
);
else
return
pec
::
invalid_state
;
}
return
pec
::
success
;
}
...
...
libcaf_core/src/detail/parse.cpp
View file @
dbdf9b26
...
...
@@ -165,17 +165,62 @@ void parse(string_parser_state& ps, uri& x) {
void
parse
(
string_parser_state
&
ps
,
config_value
&
x
)
{
ps
.
skip_whitespaces
();
if
(
ps
.
at_end
())
{
if
(
!
ps
.
at_end
())
{
detail
::
config_value_consumer
f
;
parser
::
read_config_value
(
ps
,
f
);
if
(
ps
.
code
<=
pec
::
trailing_character
)
x
=
std
::
move
(
f
.
result
);
}
else
{
ps
.
code
=
pec
::
unexpected_eof
;
}
}
void
parse
(
string_parser_state
&
ps
,
std
::
vector
<
config_value
>&
x
)
{
ps
.
skip_whitespaces
();
if
(
!
ps
.
at_end
())
{
detail
::
config_list_consumer
f
;
auto
fallback
=
ps
;
if
(
ps
.
consume
(
'['
))
{
parser
::
read_config_list
(
ps
,
f
);
if
(
ps
.
code
==
pec
::
success
)
{
x
=
std
::
move
(
f
.
result
);
}
else
{
// Rewind parser state and try parsing again as a list without
// surrounding '[]' characters. This catches edge cases like
// '[1, 2], [3, 4]'. On error, we restore the parser state after the
// first error.
auto
first_failure
=
ps
;
ps
=
fallback
;
f
.
result
.
clear
();
parser
::
lift_config_list
(
ps
,
f
);
if
(
ps
.
code
<=
pec
::
trailing_character
)
x
=
std
::
move
(
f
.
result
);
else
ps
=
first_failure
;
}
}
else
{
// If the string isn't surrounded by '[]' in the first place, we call
// lift_config_list and keep it's result in any case.
parser
::
lift_config_list
(
ps
,
f
);
if
(
ps
.
code
==
pec
::
success
)
x
=
std
::
move
(
f
.
result
);
}
}
else
{
ps
.
code
=
pec
::
unexpected_eof
;
}
}
void
parse
(
string_parser_state
&
ps
,
dictionary
<
config_value
>&
x
)
{
ps
.
skip_whitespaces
();
if
(
!
ps
.
at_end
())
{
detail
::
config_consumer
f
{
x
};
if
(
ps
.
consume
(
'{'
))
parser
::
read_config_map
(
ps
,
f
);
else
parser
::
read_config_map
<
false
>
(
ps
,
f
);
}
else
{
ps
.
code
=
pec
::
unexpected_eof
;
return
;
}
// Safe the string as fallback.
string_view
str
{
ps
.
i
,
ps
.
e
};
// Dispatch to parser.
detail
::
config_value_consumer
f
;
parser
::
read_config_value
(
ps
,
f
);
if
(
ps
.
code
<=
pec
::
trailing_character
)
x
=
std
::
move
(
f
.
result
);
}
PARSE_IMPL
(
ipv4_address
,
ipv4_address
)
...
...
libcaf_core/src/logger.cpp
View file @
dbdf9b26
...
...
@@ -310,7 +310,7 @@ void logger::init(actor_system_config& cfg) {
return
CAF_LOG_LEVEL_QUIET
;
};
auto
read_filter
=
[
&
cfg
](
string_list
&
var
,
string_view
key
)
{
if
(
auto
lst
=
get_
if
<
string_list
>
(
&
cfg
,
key
))
if
(
auto
lst
=
get_
as
<
string_list
>
(
cfg
,
key
))
var
=
std
::
move
(
*
lst
);
};
cfg_
.
file_verbosity
=
get_verbosity
(
"caf.logger.file.verbosity"
);
...
...
libcaf_core/src/make_config_option.cpp
View file @
dbdf9b26
...
...
@@ -23,73 +23,42 @@
#include "caf/config_value.hpp"
#include "caf/optional.hpp"
#define DEFAULT_META(type, parse_fun) \
config_option::meta_state type##_meta_state{ \
default_config_option_check<type>, default_config_option_store<type>, \
get_impl<type>, parse_fun, \
detail::config_value_access_t<type>::type_name()};
using
std
::
string
;
namespace
caf
{
namespace
detail
{
expected
<
config_value
>
parse_impl
(
std
::
string
*
ptr
,
string_view
str
)
{
// Parse quoted strings, otherwise consume the entire string.
auto
e
=
str
.
end
();
auto
i
=
std
::
find_if
(
str
.
begin
(),
e
,
[](
char
c
)
{
return
!
isspace
(
c
);
});
if
(
i
==
e
)
{
if
(
ptr
!=
nullptr
)
ptr
->
assign
(
i
,
e
);
return
config_value
{
std
::
string
{
i
,
e
}};
}
if
(
*
i
==
'"'
)
{
if
(
ptr
==
nullptr
)
{
std
::
string
tmp
;
if
(
auto
err
=
parse
(
str
,
tmp
))
return
err
;
return
config_value
{
std
::
move
(
tmp
)};
}
else
{
if
(
auto
err
=
parse
(
str
,
*
ptr
))
return
err
;
return
config_value
{
*
ptr
};
}
}
if
(
ptr
!=
nullptr
)
ptr
->
assign
(
str
.
begin
(),
str
.
end
());
return
config_value
{
std
::
string
{
str
.
begin
(),
str
.
end
()}};
}
}
// namespace detail
namespace
{
using
meta_state
=
config_option
::
meta_state
;
void
bool_store_neg
(
void
*
ptr
,
const
config_value
&
x
)
{
*
static_cast
<
bool
*>
(
ptr
)
=
!
get
<
bool
>
(
x
);
error
bool_sync_neg
(
void
*
ptr
,
config_value
&
x
)
{
if
(
auto
val
=
get_as
<
bool
>
(
x
))
{
x
=
config_value
{
*
val
};
if
(
ptr
)
*
static_cast
<
bool
*>
(
ptr
)
=
!*
val
;
return
none
;
}
else
{
return
std
::
move
(
val
.
error
());
}
}
config_value
bool_get_neg
(
const
void
*
ptr
)
{
return
config_value
{
!*
static_cast
<
const
bool
*>
(
ptr
)};
}
meta_state
bool_neg_meta
{
detail
::
check_impl
<
bool
>
,
bool_store_neg
,
bool_get_neg
,
nullptr
,
detail
::
config_value_access_t
<
bool
>::
type_name
()};
meta_state
bool_neg_meta
{
bool_sync_neg
,
bool_get_neg
,
"bool"
};
error
check_timespan
(
const
config_value
&
x
)
{
if
(
holds_alternative
<
timespan
>
(
x
))
template
<
uint64_t
Denominator
>
error
sync_timespan
(
void
*
ptr
,
config_value
&
x
)
{
if
(
auto
val
=
get_as
<
timespan
>
(
x
))
{
x
=
config_value
{
*
val
};
if
(
ptr
)
*
static_cast
<
size_t
*>
(
ptr
)
=
static_cast
<
size_t
>
(
get
<
timespan
>
(
x
).
count
())
/
Denominator
;
return
none
;
return
make_error
(
pec
::
type_mismatch
);
}
else
{
return
std
::
move
(
val
.
error
());
}
}
template
<
uint64_t
Denominator
>
void
store_timespan
(
void
*
ptr
,
const
config_value
&
x
)
{
*
static_cast
<
size_t
*>
(
ptr
)
=
static_cast
<
size_t
>
(
get
<
timespan
>
(
x
).
count
())
/
Denominator
;
}
template
<
uint64_t
Denominator
>
config_value
get_timespan
(
const
void
*
ptr
)
{
auto
ival
=
static_cast
<
int64_t
>
(
*
static_cast
<
const
size_t
*>
(
ptr
));
...
...
@@ -97,13 +66,10 @@ config_value get_timespan(const void* ptr) {
return
config_value
{
val
};
}
meta_state
us_res_meta
{
check_timespan
,
store_timespan
<
1000
>
,
get_timespan
<
1000
>
,
nullptr
,
detail
::
config_value_access_t
<
timespan
>::
type_name
()};
meta_state
us_res_meta
{
sync_timespan
<
1000
>
,
get_timespan
<
1000
>
,
"timespan"
};
meta_state
ms_res_meta
{
check_timespan
,
store_timespan
<
1000000
>
,
get_timespan
<
1000000
>
,
nullptr
,
detail
::
config_value_access_t
<
timespan
>::
type_name
()};
meta_state
ms_res_meta
{
sync_timespan
<
1000000
>
,
get_timespan
<
1000000
>
,
"timespan"
};
}
// namespace
...
...
libcaf_core/src/scheduler/abstract_coordinator.cpp
View file @
dbdf9b26
...
...
@@ -249,10 +249,8 @@ void abstract_coordinator::init(actor_system_config& cfg) {
namespace
sr
=
defaults
::
scheduler
;
max_throughput_
=
get_or
(
cfg
,
"caf.scheduler.max-throughput"
,
sr
::
max_throughput
);
if
(
auto
num_workers
=
get_if
<
size_t
>
(
&
cfg
,
"caf.scheduler.max-threads"
))
num_workers_
=
*
num_workers
;
else
num_workers_
=
default_thread_count
();
num_workers_
=
get_or
(
cfg
,
"caf.scheduler.max-threads"
,
default_thread_count
());
}
actor_system
::
module
::
id_t
abstract_coordinator
::
id
()
const
{
...
...
libcaf_core/src/sec_strings.cpp
View file @
dbdf9b26
...
...
@@ -146,6 +146,8 @@ std::string to_string(sec x) {
return
"type_clash"
;
case
sec
:
:
unsupported_operation
:
return
"unsupported_operation"
;
case
sec
:
:
no_such_key
:
return
"no_such_key"
;
};
}
...
...
@@ -345,6 +347,9 @@ bool from_string(string_view in, sec& out) {
}
else
if
(
in
==
"unsupported_operation"
)
{
out
=
sec
::
unsupported_operation
;
return
true
;
}
else
if
(
in
==
"no_such_key"
)
{
out
=
sec
::
no_such_key
;
return
true
;
}
else
{
return
false
;
}
...
...
@@ -421,6 +426,7 @@ bool from_integer(std::underlying_type_t<sec> in,
case
sec
:
:
connection_closed
:
case
sec
:
:
type_clash
:
case
sec
:
:
unsupported_operation
:
case
sec
:
:
no_such_key
:
out
=
result
;
return
true
;
};
...
...
libcaf_core/src/settings.cpp
View file @
dbdf9b26
...
...
@@ -43,12 +43,12 @@ const config_value* get_if(const settings* xs, string_view name) {
name
.
substr
(
pos
+
1
));
}
std
::
string
get_or
(
const
settings
&
xs
,
string_view
name
,
string_view
default_value
)
{
auto
result
=
get_if
<
std
::
string
>
(
&
xs
,
name
);
if
(
result
)
return
std
::
move
(
*
result
);
return
std
::
string
{
default_value
.
begin
(),
default_value
.
end
()
};
expected
<
std
::
string
>
get_or
(
const
settings
&
xs
,
string_view
name
,
const
char
*
fallback
)
{
if
(
auto
ptr
=
get_if
(
&
xs
,
name
))
return
get_as
<
std
::
string
>
(
*
ptr
);
else
return
{
std
::
string
{
fallback
}
};
}
config_value
&
put_impl
(
settings
&
dict
,
const
std
::
vector
<
string_view
>&
path
,
...
...
libcaf_core/test/actor_system_config.cpp
View file @
dbdf9b26
...
...
@@ -33,6 +33,7 @@
#include <vector>
using
namespace
caf
;
using
namespace
caf
::
literals
;
using
namespace
std
::
string_literals
;
...
...
@@ -161,12 +162,19 @@ CAF_TEST(file input overrides defaults but CLI args always win) {
#define CHECK_SYNCED(var, value) \
do { \
CAF_CHECK_EQUAL(var, value); \
CAF_CHECK_EQUAL(get<decltype(var)>(cfg, #var), value); \
if (auto maybe_val = get_as<decltype(var)>(cfg, #var)) { \
CAF_CHECK_EQUAL(*maybe_val, value); \
} else { \
auto cv = get_if(std::addressof(cfg.content), #var); \
CAF_ERROR("expected type " \
<< config_value::mapped_type_name<decltype(var)>() \
<< ", got: " << cv->type_name()); \
} \
} while (false)
// Checks whether an entry in content(cfg) is equal to `value`.
#define CHECK_TEXT_ONLY(type, var, value) \
CAF_CHECK_EQUAL(get<type>(cfg, #var), value)
CAF_CHECK_EQUAL(get
_as
<type>(cfg, #var), value)
#define ADD(var) add(var, #var, "...")
...
...
libcaf_core/test/config_option.cpp
View file @
dbdf9b26
...
...
@@ -22,6 +22,8 @@
#include "core-test.hpp"
#include <sstream>
#include "caf/make_config_option.hpp"
#include "caf/config_value.hpp"
#include "caf/expected.hpp"
...
...
@@ -32,6 +34,155 @@ using std::string;
namespace
{
struct
state
;
struct
baseline
{
std
::
vector
<
std
::
string
>
cli
;
std
::
string
conf
;
settings
res
;
std
::
function
<
bool
(
const
state
&
)
>
predicate
;
};
struct
request_pair
{
my_request
first
;
my_request
second
;
};
template
<
class
Inspector
>
bool
inspect
(
Inspector
&
f
,
request_pair
&
x
)
{
return
f
.
object
(
x
).
fields
(
f
.
field
(
"first"
,
x
.
first
),
f
.
field
(
"second"
,
x
.
second
));
}
struct
state
{
s1
my_app_s1
;
std
::
vector
<
int32_t
>
my_app_vector
;
level
my_app_severity
=
level
::
trace
;
my_request
my_app_request
;
request_pair
my_app_request_pair
;
config_option_set
options
;
state
()
{
config_option_adder
{
options
,
"?my.app"
}
.
add
(
my_app_s1
,
"s1"
,
""
)
.
add
(
my_app_vector
,
"vector,v"
,
""
)
.
add
(
my_app_severity
,
"severity,s"
,
""
)
.
add
(
my_app_request
,
"request,r"
,
""
)
.
add
(
my_app_request_pair
,
"request-pair,R"
,
""
);
config_option_adder
{
options
,
"sys"
}
.
add
<
std
::
string
>
(
"query,q"
,
""
)
.
add
<
int8_t
>
(
"threads,tTd"
,
""
);
}
void
run
(
baseline
&
x
,
size_t
index
)
{
settings
res
;
std
::
istringstream
src
{
x
.
conf
};
if
(
auto
parsed
=
actor_system_config
::
parse_config
(
src
,
options
))
{
res
=
std
::
move
(
*
parsed
);
}
else
{
CAF_ERROR
(
"failed to parse baseline at index "
<<
index
<<
": "
<<
parsed
.
error
());
return
;
}
auto
[
code
,
pos
]
=
options
.
parse
(
res
,
x
.
cli
);
if
(
pos
!=
x
.
cli
.
end
())
{
CAF_ERROR
(
"failed to parse all arguments for baseline at index "
<<
index
<<
", stopped at: "
<<
*
pos
<<
" ("
<<
code
<<
')'
);
return
;
}
if
(
code
!=
pec
::
success
)
{
CAF_ERROR
(
"CLI arguments for baseline at index "
<<
index
<<
" failed to parse: "
<<
code
);
return
;
}
if
(
!
x
.
predicate
(
*
this
))
{
CAF_ERROR
(
"predicate for baseline at index "
<<
index
<<
"failed! "
);
return
;
}
MESSAGE
(
"all checks for baseline at index "
<<
index
<<
" passed"
);
}
};
struct
fixture
{
std
::
vector
<
baseline
>
baselines
;
template
<
class
Predicate
>
void
add_test
(
std
::
vector
<
std
::
string
>
cli
,
std
::
string
conf
,
settings
res
,
Predicate
f
)
{
baselines
.
emplace_back
(
baseline
{
std
::
move
(
cli
),
std
::
move
(
conf
),
std
::
move
(
res
),
f
,
});
}
template
<
class
Predicate
>
void
add_test
(
std
::
vector
<
std
::
string
>
cli
,
std
::
string
conf
,
std
::
string
res
,
Predicate
f
)
{
config_value
cv_res
{
res
};
if
(
auto
parsed
=
get_as
<
settings
>
(
cv_res
))
add_test
(
std
::
move
(
cli
),
std
::
move
(
conf
),
std
::
move
(
*
parsed
),
std
::
move
(
f
));
else
CAF_FAIL
(
"failed to parse result settings: "
<<
parsed
.
error
()
<<
"
\n
INPUT:
\n
"
<<
res
<<
'\n'
);
}
template
<
class
Res
>
void
add_test
(
std
::
vector
<
std
::
string
>
cli
,
std
::
string
conf
,
Res
&&
res
)
{
return
add_test
(
std
::
move
(
cli
),
std
::
move
(
conf
),
std
::
forward
<
Res
>
(
res
),
[](
const
state
&
)
{
return
true
;
});
}
fixture
()
{
using
ivec
=
std
::
vector
<
int32_t
>
;
add_test
({
"-s"
,
"error"
},
""
,
R"_(my { app { severity = "error" } })_"
,
[](
auto
&
st
)
{
return
CHECK_EQ
(
st
.
my_app_severity
,
level
::
error
);
});
add_test
({
"-v"
,
"1, 2, 3"
},
""
,
R"_(my { app { vector = [1, 2, 3] } })_"
,
[](
auto
&
st
)
{
return
CHECK_EQ
(
st
.
my_app_vector
,
ivec
({
1
,
2
,
3
}));
});
add_test
({
"-v"
,
"[1, 2, 3]"
},
""
,
R"_(my { app { vector = [1, 2, 3] } })_"
);
add_test
({
"-v[1, 2, 3]"
},
""
,
R"_(my { app { vector = [1, 2, 3] } })_"
);
add_test
({
"-v1, 2, 3,"
},
""
,
R"_(my { app { vector = [1, 2, 3] } })_"
);
add_test
({
"-r"
,
R"_({"a":1,"b":2})_"
},
""
,
R"_(my { app { request { a = 1, b = 2 } } })_"
);
add_test
({
"-r"
,
R"_(a=1,b=2)_"
},
""
,
R"_(my { app { request { a = 1, b = 2 } } })_"
);
add_test
({
R"_(--my.app.request={a=1,b=2})_"
},
""
,
R"_(my { app { request { a = 1, b = 2 } } })_"
);
add_test
({
R"_(--my.app.request=a=1,b=2,)_"
},
""
,
R"_(my { app { request { a = 1, b = 2 } } })_"
);
add_test
({
"-R"
,
R"_({"first": {"a": 1, "b": 2}, "second": {"a": 3, "b": 4}})_"
},
""
,
R"_(my { app { request-pair { first { a = 1, b = 2 },
second { a = 3, b = 4 } } } })_"
);
add_test
({},
"sys{threads=2}"
,
R"_(sys { threads = 2 })_"
);
add_test
({
"-t"
,
"1"
},
"sys{threads=2}"
,
R"_(sys { threads = 1 })_"
);
add_test
({
"-T"
,
"1"
},
"sys{threads=2}"
,
R"_(sys { threads = 1 })_"
);
add_test
({
"-d"
,
"1"
},
"sys{threads=2}"
,
R"_(sys { threads = 1 })_"
);
add_test
({
"--sys.threads=1"
},
"sys{threads=2}"
,
R"_(sys { threads = 1 })_"
);
add_test
({
"--sys.query=foo"
},
""
,
R"_(sys { query = "foo" })_"
);
add_test
({
"-q"
,
"
\"
a
\"
in b"
},
""
,
R"_(sys { query = "\"a\" in b" })_"
);
}
};
}
// namespace
BEGIN_FIXTURE_SCOPE
(
fixture
)
SCENARIO
(
"options on the CLI override config files that override defaults"
)
{
for
(
size_t
index
=
0
;
index
<
baselines
.
size
();
++
index
)
{
state
st
;
st
.
run
(
baselines
[
index
],
index
);
}
}
constexpr
string_view
category
=
"category"
;
constexpr
string_view
name
=
"name"
;
constexpr
string_view
explanation
=
"explanation"
;
...
...
@@ -48,14 +199,13 @@ constexpr int64_t underflow() {
template
<
class
T
>
optional
<
T
>
read
(
string_view
arg
)
{
auto
co
=
make_config_option
<
T
>
(
category
,
name
,
explanation
);
auto
res
=
co
.
parse
(
arg
);
if
(
res
&&
holds_alternative
<
T
>
(
*
res
))
{
if
(
co
.
check
(
*
res
)
!=
none
)
CAF_ERROR
(
"co.parse() produced the wrong type!"
);
return
get
<
T
>
(
*
res
);
}
return
none
;
auto
result
=
T
{};
auto
co
=
make_config_option
<
T
>
(
result
,
category
,
name
,
explanation
);
config_value
val
{
arg
};
if
(
auto
err
=
co
.
sync
(
val
);
!
err
)
return
{
std
::
move
(
result
)};
else
return
none
;
}
// Unsigned integers.
...
...
@@ -98,8 +248,6 @@ void compare(const config_option& lhs, const config_option& rhs) {
CAF_CHECK_EQUAL
(
lhs
.
full_name
(),
rhs
.
full_name
());
}
}
// namespace
CAF_TEST
(
copy
constructor
)
{
auto
one
=
make_config_option
<
int
>
(
"cat1"
,
"one"
,
"option 1"
);
auto
two
=
one
;
...
...
@@ -166,12 +314,12 @@ CAF_TEST(type double) {
CAF_CHECK_EQUAL
(
unbox
(
read
<
double
>
(
"-1.0"
)),
-
1.0
);
CAF_CHECK_EQUAL
(
unbox
(
read
<
double
>
(
"-0.1"
)),
-
0.1
);
CAF_CHECK_EQUAL
(
read
<
double
>
(
"0"
),
0.
);
CAF_CHECK_EQUAL
(
read
<
double
>
(
"
\"
0.1
\"
"
),
none
);
CAF_CHECK_EQUAL
(
read
<
double
>
(
"
\"
0.1
\"
"
),
none
);
}
CAF_TEST
(
type
string
)
{
CAF_CHECK_EQUAL
(
unbox
(
read
<
string
>
(
"foo"
)),
"foo"
);
CAF_CHECK_EQUAL
(
unbox
(
read
<
string
>
(
"
\"
foo
\"
"
)),
"foo
"
);
CAF_CHECK_EQUAL
(
unbox
(
read
<
string
>
(
R"_("foo")_"
)),
R"_("foo")_
"
);
}
CAF_TEST
(
type
timespan
)
{
...
...
@@ -231,3 +379,5 @@ CAF_TEST(find by long opt) {
// No options to look through.
check
({},
false
,
false
);
}
END_FIXTURE_SCOPE
()
libcaf_core/test/config_option_set.cpp
View file @
dbdf9b26
...
...
@@ -53,10 +53,11 @@ struct fixture {
settings
cfg
;
auto
res
=
opts
.
parse
(
cfg
,
std
::
move
(
args
));
if
(
res
.
first
!=
pec
::
success
)
return
res
.
first
;
if
(
auto
x
=
get_if
<
T
>
(
&
cfg
,
key
))
return
detail
::
move_if_not_ptr
(
x
);
return
sec
::
invalid_argument
;
return
{
res
.
first
};
else
if
(
auto
x
=
get_as
<
T
>
(
cfg
,
key
))
return
{
std
::
move
(
*
x
)};
else
return
{
sec
::
invalid_argument
};
}
std
::
string
key
=
"value"
;
...
...
@@ -101,7 +102,7 @@ CAF_TEST(parse with ref syncing) {
settings
cfg
;
vector
<
string
>
args
{
"-i42"
,
"-f"
,
"1e
1
2"
,
"1e2"
,
"-shello"
,
"--bar.l=[
\"
hello
\"
,
\"
world
\"
]"
,
"-d"
,
...
...
@@ -114,34 +115,20 @@ CAF_TEST(parse with ref syncing) {
CAF_FAIL
(
"parser stopped at: "
<<
*
res
.
second
);
CAF_MESSAGE
(
"verify referenced values"
);
CAF_CHECK_EQUAL
(
foo_i
,
42
);
CAF_CHECK_EQUAL
(
foo_f
,
1e
1
2
);
CAF_CHECK_EQUAL
(
foo_f
,
1e2
);
CAF_CHECK_EQUAL
(
foo_b
,
true
);
CAF_CHECK_EQUAL
(
bar_s
,
"hello"
);
CAF_CHECK_EQUAL
(
bar_l
,
ls
({
"hello"
,
"world"
}));
CAF_CHECK_EQUAL
(
bar_d
,
ds
({{
"a"
,
"a"
},
{
"b"
,
"b"
}}));
CAF_MESSAGE
(
"verify dictionary content"
);
CAF_CHECK_EQUAL
(
get
<
int
>
(
cfg
,
"foo.i"
),
42
);
CAF_CHECK_EQUAL
(
get
_as
<
int
>
(
cfg
,
"foo.i"
),
42
);
}
CAF_TEST
(
string
parameters
)
{
opts
.
add
<
std
::
string
>
(
"value,v"
,
"some value"
);
CAF_MESSAGE
(
"test string option with and without quotes"
);
CAF_CHECK_EQUAL
(
read
<
std
::
string
>
({
"--value=
\"
foo
\\
tbar
\"
"
}),
"foo
\t
bar"
);
CAF_CHECK_EQUAL
(
read
<
std
::
string
>
({
"--value=foobar"
}),
"foobar"
);
CAF_CHECK_EQUAL
(
read
<
std
::
string
>
({
"-v"
,
"
\"
foobar
\"
"
}),
"foobar"
);
CAF_CHECK_EQUAL
(
read
<
std
::
string
>
({
"-v"
,
"foobar"
}),
"foobar"
);
CAF_CHECK_EQUAL
(
read
<
std
::
string
>
({
"-v
\"
foobar
\"
"
}),
"foobar"
);
CAF_CHECK_EQUAL
(
read
<
std
::
string
>
({
"-vfoobar"
}),
"foobar"
);
CAF_CHECK_EQUAL
(
read
<
std
::
string
>
({
"--value=
\"
'abc'
\"
"
}),
"'abc'"
);
CAF_CHECK_EQUAL
(
read
<
std
::
string
>
({
"--value='abc'"
}),
"'abc'"
);
CAF_CHECK_EQUAL
(
read
<
std
::
string
>
({
"-v"
,
"
\"
'abc'
\"
"
}),
"'abc'"
);
CAF_CHECK_EQUAL
(
read
<
std
::
string
>
({
"-v"
,
"'abc'"
}),
"'abc'"
);
CAF_CHECK_EQUAL
(
read
<
std
::
string
>
({
"-v'abc'"
}),
"'abc'"
);
CAF_CHECK_EQUAL
(
read
<
std
::
string
>
({
"--value=
\"
123
\"
"
}),
"123"
);
CAF_CHECK_EQUAL
(
read
<
std
::
string
>
({
"--value=123"
}),
"123"
);
CAF_CHECK_EQUAL
(
read
<
std
::
string
>
({
"-v"
,
"
\"
123
\"
"
}),
"123"
);
CAF_CHECK_EQUAL
(
read
<
std
::
string
>
({
"-v"
,
"123"
}),
"123"
);
CAF_CHECK_EQUAL
(
read
<
std
::
string
>
({
"-v123"
}),
"123"
);
}
CAF_TEST
(
flat
CLI
options
)
{
...
...
@@ -202,18 +189,19 @@ CAF_TEST(CLI arguments override defaults) {
CAF_MESSAGE
(
"test integer lists"
);
ints
=
int_list
{
1
,
2
,
3
};
cfg
[
"bar"
]
=
config_value
{
ints
};
CAF_CHECK_EQUAL
(
get
<
int_list
>
(
cfg
,
"bar"
),
int_list
({
1
,
2
,
3
}));
CAF_CHECK_EQUAL
(
get
_as
<
int_list
>
(
cfg
,
"bar"
),
int_list
({
1
,
2
,
3
}));
CAF_CHECK_EQUAL
(
read
<
int_list
>
(
cfg
,
{
"--bar=[10, 20, 30]"
}),
none
);
CAF_CHECK_EQUAL
(
ints
,
int_list
({
10
,
20
,
30
}));
CAF_CHECK_EQUAL
(
get
<
int_list
>
(
cfg
,
"bar"
),
int_list
({
10
,
20
,
30
}));
CAF_CHECK_EQUAL
(
get
_as
<
int_list
>
(
cfg
,
"bar"
),
int_list
({
10
,
20
,
30
}));
CAF_MESSAGE
(
"test string lists"
);
strings
=
string_list
{
"one"
,
"two"
,
"three"
};
cfg
[
"foo"
]
=
config_value
{
strings
};
CAF_CHECK_EQUAL
(
get
<
string_list
>
(
cfg
,
"foo"
),
CAF_CHECK_EQUAL
(
get
_as
<
string_list
>
(
cfg
,
"foo"
),
string_list
({
"one"
,
"two"
,
"three"
}));
CAF_CHECK_EQUAL
(
read
<
string_list
>
(
cfg
,
{
"--foo=[hello, world]"
}),
none
);
CAF_CHECK_EQUAL
(
read
<
string_list
>
(
cfg
,
{
R"_(--foo=["hello", "world"])_"
}),
none
);
CAF_CHECK_EQUAL
(
strings
,
string_list
({
"hello"
,
"world"
}));
CAF_CHECK_EQUAL
(
get
<
string_list
>
(
cfg
,
"foo"
),
CAF_CHECK_EQUAL
(
get
_as
<
string_list
>
(
cfg
,
"foo"
),
string_list
({
"hello"
,
"world"
}));
}
SUBTEST
(
"without ref syncing"
)
{
...
...
@@ -223,15 +211,16 @@ CAF_TEST(CLI arguments override defaults) {
opts
.
add
<
int_list
>
(
"global"
,
"bar,b"
,
"some list"
);
CAF_MESSAGE
(
"test integer lists"
);
cfg
[
"bar"
]
=
config_value
{
int_list
{
1
,
2
,
3
}};
CAF_CHECK_EQUAL
(
get
<
int_list
>
(
cfg
,
"bar"
),
int_list
({
1
,
2
,
3
}));
CAF_CHECK_EQUAL
(
get
_as
<
int_list
>
(
cfg
,
"bar"
),
int_list
({
1
,
2
,
3
}));
CAF_CHECK_EQUAL
(
read
<
int_list
>
(
cfg
,
{
"--bar=[10, 20, 30]"
}),
none
);
CAF_CHECK_EQUAL
(
get
<
int_list
>
(
cfg
,
"bar"
),
int_list
({
10
,
20
,
30
}));
CAF_CHECK_EQUAL
(
get
_as
<
int_list
>
(
cfg
,
"bar"
),
int_list
({
10
,
20
,
30
}));
CAF_MESSAGE
(
"test string lists"
);
cfg
[
"foo"
]
=
config_value
{
string_list
{
"one"
,
"two"
,
"three"
}};
CAF_CHECK_EQUAL
(
get
<
string_list
>
(
cfg
,
"foo"
),
CAF_CHECK_EQUAL
(
get
_as
<
string_list
>
(
cfg
,
"foo"
),
string_list
({
"one"
,
"two"
,
"three"
}));
CAF_CHECK_EQUAL
(
read
<
string_list
>
(
cfg
,
{
"--foo=[hello, world]"
}),
none
);
CAF_CHECK_EQUAL
(
get
<
string_list
>
(
cfg
,
"foo"
),
CAF_CHECK_EQUAL
(
read
<
string_list
>
(
cfg
,
{
R"_(--foo=["hello", "world"])_"
}),
none
);
CAF_CHECK_EQUAL
(
get_as
<
string_list
>
(
cfg
,
"foo"
),
string_list
({
"hello"
,
"world"
}));
}
}
...
...
@@ -240,8 +229,7 @@ CAF_TEST(CLI arguments may use custom types) {
settings
cfg
;
opts
.
add
<
foobar
>
(
"global"
,
"foobar,f"
,
"test option"
);
CAF_CHECK_EQUAL
(
read
<
foobar
>
(
cfg
,
{
"-f{foo=
\"
hello
\"
,bar=
\"
world
\"
}"
}),
none
);
auto
fb
=
get_if
<
foobar
>
(
&
cfg
,
"foobar"
);
if
(
CAF_CHECK
(
fb
))
if
(
auto
fb
=
get_as
<
foobar
>
(
cfg
,
"foobar"
);
CAF_CHECK
(
fb
))
CAF_CHECK_EQUAL
(
*
fb
,
foobar
(
"hello"
,
"world"
));
}
...
...
libcaf_core/test/config_value.cpp
View file @
dbdf9b26
This diff is collapsed.
Click to expand it.
libcaf_core/test/config_value_writer.cpp
View file @
dbdf9b26
...
...
@@ -42,6 +42,7 @@ using i64_list = std::vector<i64>;
struct
fixture
{
config_value
x
;
settings
dummy
;
template
<
class
T
>
void
set
(
const
T
&
value
)
{
...
...
@@ -50,19 +51,11 @@ struct fixture {
CAF_FAIL
(
"failed two write to settings: "
<<
writer
.
get_error
());
}
template
<
class
T
>
optional
<
T
>
get
(
const
settings
&
cfg
,
string_view
key
)
{
if
(
auto
ptr
=
get_if
<
T
>
(
&
cfg
,
key
))
const
settings
&
xs
()
const
{
if
(
auto
*
ptr
=
get_if
<
settings
>
(
&
x
))
return
*
ptr
;
return
none
;
}
template
<
class
T
>
optional
<
T
>
get
(
string_view
key
)
{
if
(
auto
*
xs
=
get_if
<
settings
>
(
&
x
))
return
get
<
T
>
(
*
xs
,
key
);
else
CAF_FAIL
(
"fixture does not contain a dictionary"
)
;
return
dummy
;
}
};
...
...
@@ -72,18 +65,18 @@ CAF_TEST_FIXTURE_SCOPE(config_value_writer_tests, fixture)
CAF_TEST
(
structs
become
dictionaries
)
{
set
(
foobar
{
"hello"
,
"world"
});
CAF_CHECK_EQUAL
(
get
<
std
::
string
>
(
"foo"
),
"hello"
s
);
CAF_CHECK_EQUAL
(
get
<
std
::
string
>
(
"bar"
),
"world"
s
);
CAF_CHECK_EQUAL
(
get
_as
<
std
::
string
>
(
xs
(),
"foo"
),
"hello"
s
);
CAF_CHECK_EQUAL
(
get
_as
<
std
::
string
>
(
xs
(),
"bar"
),
"world"
s
);
}
CAF_TEST
(
nested
structs
become
nested
dictionaries
)
{
set
(
line
{{
10
,
20
,
30
},
{
70
,
60
,
50
}});
CAF_CHECK_EQUAL
(
get
<
i64
>
(
"p1.x"
),
10
_i64
);
CAF_CHECK_EQUAL
(
get
<
i64
>
(
"p1.y"
),
20
_i64
);
CAF_CHECK_EQUAL
(
get
<
i64
>
(
"p1.z"
),
30
_i64
);
CAF_CHECK_EQUAL
(
get
<
i64
>
(
"p2.x"
),
70
_i64
);
CAF_CHECK_EQUAL
(
get
<
i64
>
(
"p2.y"
),
60
_i64
);
CAF_CHECK_EQUAL
(
get
<
i64
>
(
"p2.z"
),
50
_i64
);
CAF_CHECK_EQUAL
(
get
_as
<
i64
>
(
xs
(),
"p1.x"
),
10
_i64
);
CAF_CHECK_EQUAL
(
get
_as
<
i64
>
(
xs
(),
"p1.y"
),
20
_i64
);
CAF_CHECK_EQUAL
(
get
_as
<
i64
>
(
xs
(),
"p1.z"
),
30
_i64
);
CAF_CHECK_EQUAL
(
get
_as
<
i64
>
(
xs
(),
"p2.x"
),
70
_i64
);
CAF_CHECK_EQUAL
(
get
_as
<
i64
>
(
xs
(),
"p2.y"
),
60
_i64
);
CAF_CHECK_EQUAL
(
get
_as
<
i64
>
(
xs
(),
"p2.z"
),
50
_i64
);
}
CAF_TEST
(
empty
types
and
maps
become
dictionaries
)
{
...
...
@@ -100,36 +93,36 @@ CAF_TEST(empty types and maps become dictionaries) {
tst
.
v7
[
"two"
]
=
2
;
tst
.
v7
[
"three"
]
=
3
;
set
(
tst
);
CAF_CHECK_EQUAL
(
get
<
settings
>
(
"v1"
),
settings
{});
CAF_CHECK_EQUAL
(
get
<
i64
>
(
"v2"
),
42
_i64
);
CAF_CHECK_EQUAL
(
get
<
i64_list
>
(
"v3"
),
i64_list
({
-
1
,
-
2
,
-
3
,
-
4
}));
if
(
auto
v4
=
get
<
config_value
::
list
>
(
"v4"
);
CAF_CHECK
_EQUAL
(
v4
->
size
(),
2u
))
{
CAF_CHECK_EQUAL
(
get
_as
<
settings
>
(
xs
(),
"v1"
),
settings
{});
CAF_CHECK_EQUAL
(
get
_as
<
i64
>
(
xs
(),
"v2"
),
42
_i64
);
CAF_CHECK_EQUAL
(
get
_as
<
i64_list
>
(
xs
(),
"v3"
),
i64_list
({
-
1
,
-
2
,
-
3
,
-
4
}));
if
(
auto
v4
=
get
_as
<
config_value
::
list
>
(
xs
(),
"v4"
);
CAF_CHECK
(
v4
&&
v4
->
size
()
==
2u
))
{
if
(
auto
v1
=
v4
->
front
();
CAF_CHECK
(
holds_alternative
<
settings
>
(
v1
)))
{
auto
&
v1_xs
=
caf
::
get
<
settings
>
(
v1
);
auto
&
v1_xs
=
get
<
settings
>
(
v1
);
CAF_CHECK_EQUAL
(
get
<
double
>
(
v1_xs
,
"content"
),
0.0
);
CAF_CHECK_EQUAL
(
get
<
std
::
string
>
(
v1_xs
,
"@content-type"
),
to_string
(
type_name_v
<
double
>
));
}
if
(
auto
v2
=
v4
->
back
();
CAF_CHECK
(
holds_alternative
<
settings
>
(
v2
)))
{
auto
&
v2_xs
=
caf
::
get
<
settings
>
(
v2
);
auto
&
v2_xs
=
get
<
settings
>
(
v2
);
CAF_CHECK_EQUAL
(
get
<
double
>
(
v2_xs
,
"content"
),
1.0
);
CAF_CHECK_EQUAL
(
get
<
std
::
string
>
(
v2_xs
,
"@content-type"
),
to_string
(
type_name_v
<
double
>
));
}
}
CAF_CHECK_EQUAL
(
get
<
i64_list
>
(
"v5"
),
i64_list
({
10
,
20
}));
CAF_CHECK_EQUAL
(
get
_as
<
i64_list
>
(
xs
(),
"v5"
),
i64_list
({
10
,
20
}));
// TODO: check v6
CAF_CHECK_EQUAL
(
get
<
i64
>
(
"v7.one"
),
1
_i64
);
CAF_CHECK_EQUAL
(
get
<
i64
>
(
"v7.two"
),
2
_i64
);
CAF_CHECK_EQUAL
(
get
<
i64
>
(
"v7.three"
),
3
_i64
);
CAF_CHECK_EQUAL
(
get
<
config_value
::
list
>
(
"v8"
),
config_value
::
list
());
CAF_CHECK_EQUAL
(
get
_as
<
i64
>
(
xs
(),
"v7.one"
),
1
_i64
);
CAF_CHECK_EQUAL
(
get
_as
<
i64
>
(
xs
(),
"v7.two"
),
2
_i64
);
CAF_CHECK_EQUAL
(
get
_as
<
i64
>
(
xs
(),
"v7.three"
),
3
_i64
);
CAF_CHECK_EQUAL
(
get
_as
<
config_value
::
list
>
(
xs
(),
"v8"
),
config_value
::
list
());
}
CAF_TEST
(
custom
inspect
overloads
may
produce
single
values
)
{
auto
tue
=
weekday
::
tuesday
;
set
(
tue
);
CAF_CHECK_EQUAL
(
x
,
"tuesday"
s
);
CAF_CHECK_EQUAL
(
get_as
<
std
::
string
>
(
x
)
,
"tuesday"
s
);
}
CAF_TEST_FIXTURE_SCOPE_END
()
libcaf_core/test/core-test.cpp
View file @
dbdf9b26
...
...
@@ -4,6 +4,51 @@
#include "core-test.hpp"
std
::
string
to_string
(
level
lvl
)
{
switch
(
lvl
)
{
case
level
:
:
all
:
return
"all"
;
case
level
:
:
trace
:
return
"trace"
;
case
level
:
:
debug
:
return
"debug"
;
case
level
:
:
warning
:
return
"warning"
;
case
level
:
:
error
:
return
"error"
;
default:
return
"???"
;
}
}
bool
from_string
(
caf
::
string_view
str
,
level
&
lvl
)
{
auto
set
=
[
&
](
level
value
)
{
lvl
=
value
;
return
true
;
};
if
(
str
==
"all"
)
return
set
(
level
::
all
);
else
if
(
str
==
"trace"
)
return
set
(
level
::
trace
);
else
if
(
str
==
"debug"
)
return
set
(
level
::
debug
);
else
if
(
str
==
"warning"
)
return
set
(
level
::
warning
);
else
if
(
str
==
"error"
)
return
set
(
level
::
error
);
else
return
false
;
}
bool
from_integer
(
uint8_t
val
,
level
&
lvl
)
{
if
(
val
<
5
)
{
lvl
=
static_cast
<
level
>
(
val
);
return
true
;
}
else
{
return
false
;
}
}
int
main
(
int
argc
,
char
**
argv
)
{
using
namespace
caf
;
init_global_meta_objects
<
id_block
::
core_test
>
();
...
...
libcaf_core/test/core-test.hpp
View file @
dbdf9b26
...
...
@@ -150,7 +150,7 @@ struct s1 {
template
<
class
Inspector
>
bool
inspect
(
Inspector
&
f
,
s1
&
x
)
{
return
f
.
object
(
x
).
fields
(
f
.
field
(
"value"
,
x
.
value
)
);
return
f
.
apply
(
x
.
value
);
}
struct
s2
{
...
...
@@ -159,7 +159,7 @@ struct s2 {
template
<
class
Inspector
>
bool
inspect
(
Inspector
&
f
,
s2
&
x
)
{
return
f
.
object
(
x
).
fields
(
f
.
field
(
"value"
,
x
.
value
)
);
return
f
.
apply
(
x
.
value
);
}
struct
s3
{
...
...
@@ -171,7 +171,7 @@ struct s3 {
template
<
class
Inspector
>
bool
inspect
(
Inspector
&
f
,
s3
&
x
)
{
return
f
.
object
(
x
).
fields
(
f
.
field
(
"value"
,
x
.
value
)
);
return
f
.
apply
(
x
.
value
);
}
struct
test_array
{
...
...
@@ -267,21 +267,17 @@ bool inspect(Inspector& f, dummy_enum_class& x) {
return
f
.
apply
(
get
,
set
);
}
enum
class
level
{
all
,
trace
,
debug
,
warning
,
error
};
enum
class
level
:
uint8_t
{
all
,
trace
,
debug
,
warning
,
error
};
std
::
string
to_string
(
level
);
bool
from_string
(
caf
::
string_view
,
level
&
);
bool
from_integer
(
uint8_t
,
level
&
);
template
<
class
Inspector
>
bool
inspect
(
Inspector
&
f
,
level
&
x
)
{
using
integer_type
=
std
::
underlying_type_t
<
level
>
;
auto
get
=
[
&
x
]
{
return
static_cast
<
integer_type
>
(
x
);
};
auto
set
=
[
&
x
](
integer_type
val
)
{
if
(
val
>=
0
&&
val
<=
4
)
{
x
=
static_cast
<
level
>
(
val
);
return
true
;
}
else
{
return
false
;
}
};
return
f
.
apply
(
get
,
set
);
return
caf
::
default_enum_inspect
(
f
,
x
);
}
enum
dummy_enum
{
de_foo
,
de_bar
};
...
...
libcaf_core/test/detail/config_consumer.cpp
View file @
dbdf9b26
...
...
@@ -82,12 +82,11 @@ CAF_TEST(config_consumer) {
detail
::
parser
::
read_config
(
res
,
consumer
);
CAF_CHECK_EQUAL
(
res
.
code
,
pec
::
success
);
CAF_CHECK_EQUAL
(
string_view
(
res
.
i
,
res
.
e
),
string_view
());
CAF_CHECK_EQUAL
(
get
<
bool
>
(
config
,
"is_server"
),
true
);
CAF_CHECK_EQUAL
(
get
<
uint16_t
>
(
config
,
"port"
),
4242u
);
CAF_CHECK_EQUAL
(
get
<
ls
>
(
config
,
"nodes"
),
ls
({
"sun"
,
"venus"
}));
CAF_CHECK_EQUAL
(
get
<
string
>
(
config
,
"logger.file-name"
),
"foobar.conf"
);
CAF_MESSAGE
(
config
);
CAF_CHECK_EQUAL
(
get
<
timespan
>
(
config
,
"scheduler.timing"
),
timespan
(
2000
));
CAF_CHECK_EQUAL
(
get_as
<
bool
>
(
config
,
"is_server"
),
true
);
CAF_CHECK_EQUAL
(
get_as
<
uint16_t
>
(
config
,
"port"
),
4242u
);
CAF_CHECK_EQUAL
(
get_as
<
ls
>
(
config
,
"nodes"
),
ls
({
"sun"
,
"venus"
}));
CAF_CHECK_EQUAL
(
get_as
<
string
>
(
config
,
"logger.file-name"
),
"foobar.conf"
);
CAF_CHECK_EQUAL
(
get_as
<
timespan
>
(
config
,
"scheduler.timing"
),
timespan
(
2000
));
}
CAF_TEST
(
simplified
syntax
)
{
...
...
libcaf_core/test/detail/parse.cpp
View file @
dbdf9b26
...
...
@@ -171,41 +171,14 @@ CAF_TEST(strings) {
CAF_CHECK_EQUAL
(
read
<
std
::
string
>
(
"
\"
foo
\t\"
"
),
" foo
\t
"
);
}
CAF_TEST
(
lists
)
{
using
int_list
=
std
::
vector
<
int
>
;
using
string_list
=
std
::
vector
<
std
::
string
>
;
CAF_CHECK_EQUAL
(
read
<
int_list
>
(
"1"
),
int_list
({
1
}));
CAF_CHECK_EQUAL
(
read
<
int_list
>
(
"1, 2, 3"
),
int_list
({
1
,
2
,
3
}));
CAF_CHECK_EQUAL
(
read
<
int_list
>
(
"[1, 2, 3]"
),
int_list
({
1
,
2
,
3
}));
CAF_CHECK_EQUAL
(
read
<
string_list
>
(
"a, b ,
\"
c
\"
"
),
string_list
({
"a"
,
"b"
,
" c "
}));
}
CAF_TEST
(
maps
)
{
using
int_map
=
std
::
map
<
std
::
string
,
int
>
;
CAF_CHECK_EQUAL
(
read
<
int_map
>
(
R"(a=1, "b" = 42)"
),
int_map
({{
"a"
,
1
},
{
"b"
,
42
}}));
CAF_CHECK_EQUAL
(
read
<
int_map
>
(
R"({ a = 1 , b = 42 ,} )"
),
int_map
({{
"a"
,
1
},
{
"b"
,
42
}}));
}
CAF_TEST
(
uris
)
{
using
uri_list
=
std
::
vector
<
uri
>
;
auto
x_res
=
read
<
uri
>
(
"foo:bar"
);
if
(
x_res
==
none
)
{
if
(
auto
x_res
=
read
<
uri
>
(
"foo:bar"
))
{
auto
x
=
*
x_res
;
CAF_CHECK_EQUAL
(
x
.
scheme
(),
"foo"
);
CAF_CHECK_EQUAL
(
x
.
path
(),
"bar"
);
}
else
{
CAF_ERROR
(
"my:path not recognized as URI"
);
return
;
}
auto
x
=
*
x_res
;
CAF_CHECK_EQUAL
(
x
.
scheme
(),
"foo"
);
CAF_CHECK_EQUAL
(
x
.
path
(),
"bar"
);
auto
ls
=
unbox
(
read
<
uri_list
>
(
"foo:bar, <http://actor-framework.org/doc>"
));
CAF_REQUIRE_EQUAL
(
ls
.
size
(),
2u
);
CAF_CHECK_EQUAL
(
ls
[
0
].
scheme
(),
"foo"
);
CAF_CHECK_EQUAL
(
ls
[
0
].
path
(),
"bar"
);
CAF_CHECK_EQUAL
(
ls
[
1
].
scheme
(),
"http"
);
CAF_CHECK_EQUAL
(
ls
[
1
].
authority
().
host
,
std
::
string
{
"actor-framework.org"
});
CAF_CHECK_EQUAL
(
ls
[
1
].
path
(),
"doc"
);
}
CAF_TEST
(
IPv4
address
)
{
...
...
libcaf_core/test/detail/parser/read_bool.cpp
View file @
dbdf9b26
...
...
@@ -48,7 +48,8 @@ struct bool_parser {
detail
::
parser
::
read_bool
(
res
,
f
);
if
(
res
.
code
==
pec
::
success
)
return
f
.
x
;
return
res
.
code
;
else
return
res
.
code
;
}
};
...
...
@@ -61,23 +62,23 @@ struct fixture {
CAF_TEST_FIXTURE_SCOPE
(
read_bool_tests
,
fixture
)
CAF_TEST
(
valid
booleans
)
{
CAF_CHECK_EQUAL
(
p
(
"true"
),
true
);
CAF_CHECK_EQUAL
(
p
(
"false"
),
false
);
CAF_CHECK_EQUAL
(
p
(
"true"
),
res_t
{
true
}
);
CAF_CHECK_EQUAL
(
p
(
"false"
),
res_t
{
false
}
);
}
CAF_TEST
(
invalid
booleans
)
{
CAF_CHECK_EQUAL
(
p
(
""
),
pec
::
unexpected_eof
);
CAF_CHECK_EQUAL
(
p
(
"t"
),
pec
::
unexpected_eof
);
CAF_CHECK_EQUAL
(
p
(
"tr"
),
pec
::
unexpected_eof
);
CAF_CHECK_EQUAL
(
p
(
"tru"
),
pec
::
unexpected_eof
);
CAF_CHECK_EQUAL
(
p
(
" true"
),
pec
::
unexpected_character
);
CAF_CHECK_EQUAL
(
p
(
"f"
),
pec
::
unexpected_eof
);
CAF_CHECK_EQUAL
(
p
(
"fa"
),
pec
::
unexpected_eof
);
CAF_CHECK_EQUAL
(
p
(
"fal"
),
pec
::
unexpected_eof
);
CAF_CHECK_EQUAL
(
p
(
"fals"
),
pec
::
unexpected_eof
);
CAF_CHECK_EQUAL
(
p
(
" false"
),
pec
::
unexpected_character
);
CAF_CHECK_EQUAL
(
p
(
"tr
\n
ue"
),
pec
::
unexpected_newline
);
CAF_CHECK_EQUAL
(
p
(
"trues"
),
pec
::
trailing_character
);
CAF_CHECK_EQUAL
(
p
(
""
),
res_t
{
pec
::
unexpected_eof
}
);
CAF_CHECK_EQUAL
(
p
(
"t"
),
res_t
{
pec
::
unexpected_eof
}
);
CAF_CHECK_EQUAL
(
p
(
"tr"
),
res_t
{
pec
::
unexpected_eof
}
);
CAF_CHECK_EQUAL
(
p
(
"tru"
),
res_t
{
pec
::
unexpected_eof
}
);
CAF_CHECK_EQUAL
(
p
(
" true"
),
res_t
{
pec
::
unexpected_character
}
);
CAF_CHECK_EQUAL
(
p
(
"f"
),
res_t
{
pec
::
unexpected_eof
}
);
CAF_CHECK_EQUAL
(
p
(
"fa"
),
res_t
{
pec
::
unexpected_eof
}
);
CAF_CHECK_EQUAL
(
p
(
"fal"
),
res_t
{
pec
::
unexpected_eof
}
);
CAF_CHECK_EQUAL
(
p
(
"fals"
),
res_t
{
pec
::
unexpected_eof
}
);
CAF_CHECK_EQUAL
(
p
(
" false"
),
res_t
{
pec
::
unexpected_character
}
);
CAF_CHECK_EQUAL
(
p
(
"tr
\n
ue"
),
res_t
{
pec
::
unexpected_newline
}
);
CAF_CHECK_EQUAL
(
p
(
"trues"
),
res_t
{
pec
::
trailing_character
}
);
}
CAF_TEST_FIXTURE_SCOPE_END
()
libcaf_core/test/load_inspector.cpp
View file @
dbdf9b26
...
...
@@ -409,9 +409,10 @@ end object)_");
CAF_TEST
(
load
inspectors
support
variant
fields
with
fallbacks
)
{
fallback_dummy_message
d
;
using
content_type
=
decltype
(
d
.
content
);
d
.
content
=
std
::
string
{
"hello world"
};
CAF_CHECK
(
inspect
(
f
,
d
));
CAF_CHECK_EQUAL
(
d
.
content
,
42.0
);
CAF_CHECK_EQUAL
(
d
.
content
,
content_type
{
42.0
}
);
CAF_CHECK_EQUAL
(
f
.
log
,
R"_(
begin object fallback_dummy_message
begin optional variant field content
...
...
libcaf_core/test/message.cpp
View file @
dbdf9b26
...
...
@@ -120,12 +120,12 @@ CAF_TEST(to_string converts messages to strings) {
CAF_CHECK_EQUAL
(
msg_as_string
(
R"__(this is a "test")__"
),
teststr
);
CAF_CHECK_EQUAL
(
msg_as_string
(
make_tuple
(
1
,
2
,
3
),
4
,
5
),
"message([1, 2, 3], 4, 5)"
);
CAF_CHECK_EQUAL
(
msg_as_string
(
s1
{}),
"message(
s1([10, 20, 30])
)"
);
CAF_CHECK_EQUAL
(
msg_as_string
(
s1
{}),
"message(
[10, 20, 30]
)"
);
s2
tmp
;
tmp
.
value
[
0
][
1
]
=
100
;
CAF_CHECK_EQUAL
(
msg_as_string
(
s2
{}),
"message(
s2([[1, 10], [2, 20], [3, 30], [4, 40]])
)"
);
CAF_CHECK_EQUAL
(
msg_as_string
(
s3
{}),
"message(
s3([1, 2, 3, 4])
)"
);
"message(
[[1, 10], [2, 20], [3, 30], [4, 40]]
)"
);
CAF_CHECK_EQUAL
(
msg_as_string
(
s3
{}),
"message(
[1, 2, 3, 4]
)"
);
}
CAF_TEST
(
match_elements
exposes
element
types
)
{
...
...
libcaf_core/test/message_lifetime.cpp
View file @
dbdf9b26
...
...
@@ -120,7 +120,8 @@ CAF_TEST(message_lifetime_in_scoped_actor) {
self
->
send
(
self
,
msg
);
CAF_CHECK_EQUAL
(
msg
.
cdata
().
get_reference_count
(),
2u
);
self
->
receive
([
&
](
int
&
value
)
{
CAF_CHECK_NOT_EQUAL
(
&
value
,
msg
.
cdata
().
at
(
0
));
auto
addr
=
static_cast
<
void
*>
(
&
value
);
CAF_CHECK_NOT_EQUAL
(
addr
,
msg
.
cdata
().
at
(
0
));
value
=
10
;
});
CAF_CHECK_EQUAL
(
msg
.
get_as
<
int
>
(
0
),
42
);
...
...
libcaf_core/test/mixin/requester.cpp
View file @
dbdf9b26
...
...
@@ -93,7 +93,7 @@ CAF_TEST(requests without result) {
run_once
();
expect
((
int
,
int
),
from
(
client
).
to
(
server
).
with
(
1
,
2
));
expect
((
void
),
from
(
server
).
to
(
client
));
CAF_CHECK_EQUAL
(
*
result
,
unit
);
CAF_CHECK_EQUAL
(
*
result
,
result_type
{
unit
}
);
}
SUBTEST
(
"request.await"
)
{
auto
client
=
sys
.
spawn
([
=
](
event_based_actor
*
self
)
{
...
...
@@ -102,13 +102,13 @@ CAF_TEST(requests without result) {
run_once
();
expect
((
int
,
int
),
from
(
client
).
to
(
server
).
with
(
1
,
2
));
expect
((
void
),
from
(
server
).
to
(
client
));
CAF_CHECK_EQUAL
(
*
result
,
unit
);
CAF_CHECK_EQUAL
(
*
result
,
result_type
{
unit
}
);
}
SUBTEST
(
"request.receive"
)
{
auto
res_hdl
=
self
->
request
(
server
,
infinite
,
1
,
2
);
run
();
res_hdl
.
receive
([
&
]
{
*
result
=
unit
;
},
ERROR_HANDLER
);
CAF_CHECK_EQUAL
(
*
result
,
unit
);
CAF_CHECK_EQUAL
(
*
result
,
result_type
{
unit
}
);
}
}
...
...
@@ -121,7 +121,7 @@ CAF_TEST(requests with integer result) {
run_once
();
expect
((
int
,
int
),
from
(
client
).
to
(
server
).
with
(
1
,
2
));
expect
((
int
),
from
(
server
).
to
(
client
).
with
(
3
));
CAF_CHECK_EQUAL
(
*
result
,
3
);
CAF_CHECK_EQUAL
(
*
result
,
result_type
{
3
}
);
}
SUBTEST
(
"request.await"
)
{
auto
client
=
sys
.
spawn
([
=
](
event_based_actor
*
self
)
{
...
...
@@ -130,13 +130,13 @@ CAF_TEST(requests with integer result) {
run_once
();
expect
((
int
,
int
),
from
(
client
).
to
(
server
).
with
(
1
,
2
));
expect
((
int
),
from
(
server
).
to
(
client
).
with
(
3
));
CAF_CHECK_EQUAL
(
*
result
,
3
);
CAF_CHECK_EQUAL
(
*
result
,
result_type
{
3
}
);
}
SUBTEST
(
"request.receive"
)
{
auto
res_hdl
=
self
->
request
(
server
,
infinite
,
1
,
2
);
run
();
res_hdl
.
receive
([
&
](
int
x
)
{
*
result
=
x
;
},
ERROR_HANDLER
);
CAF_CHECK_EQUAL
(
*
result
,
3
);
CAF_CHECK_EQUAL
(
*
result
,
result_type
{
3
}
);
}
}
...
...
@@ -150,7 +150,7 @@ CAF_TEST(delegated request with integer result) {
expect
((
int
,
int
),
from
(
client
).
to
(
server
).
with
(
1
,
2
));
expect
((
int
,
int
),
from
(
client
).
to
(
worker
).
with
(
1
,
2
));
expect
((
int
),
from
(
worker
).
to
(
client
).
with
(
3
));
CAF_CHECK_EQUAL
(
*
result
,
3
);
CAF_CHECK_EQUAL
(
*
result
,
result_type
{
3
}
);
}
CAF_TEST
(
requesters
support
fan_out_request
)
{
...
...
libcaf_core/test/settings.cpp
View file @
dbdf9b26
...
...
@@ -61,23 +61,22 @@ struct fixture {
}
};
con
st
config_value
&
unpack
(
const
settings
&
x
,
string_view
key
)
{
auto
i
=
x
.
find
(
key
);
if
(
i
==
x
.
end
())
CAF_FAIL
(
"key not found in dictionary: "
<<
key
);
return
i
->
second
;
con
fig_value
unpack
(
const
settings
&
x
,
string_view
key
)
{
if
(
auto
i
=
x
.
find
(
key
);
i
!=
x
.
end
())
return
i
->
second
;
else
return
{}
;
}
template
<
class
...
Ts
>
const
config_value
&
unpack
(
const
settings
&
x
,
string_view
key
,
const
char
*
next_key
,
Ts
...
keys
)
{
auto
i
=
x
.
find
(
key
);
if
(
i
==
x
.
end
())
CAF_FAIL
(
"key not found in dictionary: "
<<
key
);
if
(
!
holds_alternative
<
settings
>
(
i
->
second
))
CAF_FAIL
(
"value is not a dictionary: "
<<
key
);
return
unpack
(
get
<
settings
>
(
i
->
second
),
{
next_key
,
strlen
(
next_key
)},
keys
...);
config_value
unpack
(
const
settings
&
x
,
string_view
key
,
const
char
*
next_key
,
Ts
...
keys
)
{
if
(
auto
i
=
x
.
find
(
key
);
i
==
x
.
end
())
return
{};
else
if
(
auto
ptr
=
get_if
<
settings
>
(
std
::
addressof
(
i
->
second
)))
return
unpack
(
*
ptr
,
{
next_key
,
strlen
(
next_key
)},
keys
...);
else
return
{};
}
struct
foobar
{
...
...
@@ -85,45 +84,12 @@ struct foobar {
int
bar
=
0
;
};
}
// namespace
namespace
caf
{
// Enable users to configure foobar's like this:
// my-value {
// foo = 42
// bar = 23
// }
template
<
>
struct
config_value_access
<
foobar
>
{
static
bool
is
(
const
config_value
&
x
)
{
auto
dict
=
caf
::
get_if
<
config_value
::
dictionary
>
(
&
x
);
if
(
dict
!=
nullptr
)
{
return
caf
::
get_if
<
int
>
(
dict
,
"foo"
)
!=
none
&&
caf
::
get_if
<
int
>
(
dict
,
"bar"
)
!=
none
;
}
return
false
;
}
static
optional
<
foobar
>
get_if
(
const
config_value
*
x
)
{
foobar
result
;
if
(
!
is
(
*
x
))
return
none
;
const
auto
&
dict
=
caf
::
get
<
config_value
::
dictionary
>
(
*
x
);
result
.
foo
=
caf
::
get
<
int
>
(
dict
,
"foo"
);
result
.
bar
=
caf
::
get
<
int
>
(
dict
,
"bar"
);
return
result
;
}
static
foobar
get
(
const
config_value
&
x
)
{
auto
result
=
get_if
(
&
x
);
if
(
!
result
)
CAF_RAISE_ERROR
(
"invalid type found"
);
return
std
::
move
(
*
result
);
}
};
template
<
class
Inspector
>
bool
inspect
(
Inspector
&
f
,
foobar
&
x
)
{
return
f
.
object
(
x
).
fields
(
f
.
field
(
"foo"
,
x
.
foo
),
f
.
field
(
"bar"
,
x
.
bar
));
}
}
// namespace
caf
}
// namespace
CAF_TEST_FIXTURE_SCOPE
(
settings_tests
,
fixture
)
...
...
@@ -135,11 +101,11 @@ CAF_TEST(put) {
CAF_CHECK
(
x
.
contains
(
"foo"
));
CAF_CHECK
(
x
.
contains
(
"logger"
));
CAF_CHECK
(
x
.
contains
(
"one"
));
CAF_CHECK_EQUAL
(
unpack
(
x
,
"foo"
),
"bar"
s
);
CAF_CHECK_EQUAL
(
unpack
(
x
,
"logger"
,
"console"
),
"none"
s
);
CAF_CHECK_EQUAL
(
unpack
(
x
,
"one"
,
"two"
,
"three"
),
"four"
s
);
CAF_CHECK_EQUAL
(
unpack
(
x
,
"foo"
),
config_value
{
"bar"
s
}
);
CAF_CHECK_EQUAL
(
unpack
(
x
,
"logger"
,
"console"
),
config_value
{
"none"
s
}
);
CAF_CHECK_EQUAL
(
unpack
(
x
,
"one"
,
"two"
,
"three"
),
config_value
{
"four"
s
}
);
put
(
x
,
"logger.console"
,
"trace"
);
CAF_CHECK_EQUAL
(
unpack
(
x
,
"logger"
,
"console"
),
"trace"
s
);
CAF_CHECK_EQUAL
(
unpack
(
x
,
"logger"
,
"console"
),
config_value
{
"trace"
s
}
);
}
CAF_TEST
(
put
missing
)
{
...
...
@@ -150,11 +116,11 @@ CAF_TEST(put missing) {
CAF_CHECK
(
x
.
contains
(
"foo"
));
CAF_CHECK
(
x
.
contains
(
"logger"
));
CAF_CHECK
(
x
.
contains
(
"one"
));
CAF_CHECK_EQUAL
(
unpack
(
x
,
"foo"
),
"bar"
s
);
CAF_CHECK_EQUAL
(
unpack
(
x
,
"logger"
,
"console"
),
"none"
s
);
CAF_CHECK_EQUAL
(
unpack
(
x
,
"one"
,
"two"
,
"three"
),
"four"
s
);
CAF_CHECK_EQUAL
(
unpack
(
x
,
"foo"
),
config_value
{
"bar"
s
}
);
CAF_CHECK_EQUAL
(
unpack
(
x
,
"logger"
,
"console"
),
config_value
{
"none"
s
}
);
CAF_CHECK_EQUAL
(
unpack
(
x
,
"one"
,
"two"
,
"three"
),
config_value
{
"four"
s
}
);
put_missing
(
x
,
"logger.console"
,
"trace"
);
CAF_CHECK_EQUAL
(
unpack
(
x
,
"logger"
,
"console"
),
"none"
s
);
CAF_CHECK_EQUAL
(
unpack
(
x
,
"logger"
,
"console"
),
config_value
{
"none"
s
}
);
}
CAF_TEST
(
put
list
)
{
...
...
@@ -170,11 +136,12 @@ CAF_TEST(put list) {
CAF_TEST
(
put
dictionary
)
{
put_dictionary
(
x
,
"logger"
).
emplace
(
"console"
,
"none"
);
CAF_CHECK
(
x
.
contains
(
"logger"
));
CAF_CHECK_EQUAL
(
unpack
(
x
,
"logger"
,
"console"
),
"none"
s
);
CAF_CHECK_EQUAL
(
unpack
(
x
,
"logger"
,
"console"
),
config_value
{
"none"
s
}
);
put_dictionary
(
x
,
"foo.bar"
).
emplace
(
"value"
,
42
);
CAF_CHECK_EQUAL
(
unpack
(
x
,
"foo"
,
"bar"
,
"value"
),
42
);
CAF_CHECK_EQUAL
(
unpack
(
x
,
"foo"
,
"bar"
,
"value"
),
config_value
{
42
}
);
put_dictionary
(
x
,
"one.two.three"
).
emplace
(
"four"
,
"five"
);
CAF_CHECK_EQUAL
(
unpack
(
x
,
"one"
,
"two"
,
"three"
,
"four"
),
"five"
s
);
CAF_CHECK_EQUAL
(
unpack
(
x
,
"one"
,
"two"
,
"three"
,
"four"
),
config_value
{
"five"
s
});
}
CAF_TEST
(
get
and
get_if
)
{
...
...
@@ -186,8 +153,8 @@ CAF_TEST(get and get_if) {
CAF_CHECK
(
get
<
std
::
string
>
(
x
,
"logger.console"
)
==
"none"
s
);
CAF_CHECK
(
get_if
(
&
x
,
"one.two.three"
)
!=
nullptr
);
CAF_CHECK
(
get_if
<
std
::
string
>
(
&
x
,
"one.two.three"
)
==
nullptr
);
CAF_REQUIRE
(
get_if
<
int
>
(
&
x
,
"one.two.three"
)
!=
none
);
CAF_CHECK
(
get
<
in
t
>
(
x
,
"one.two.three"
)
==
4
);
if
(
CAF_CHECK
(
get_if
<
int64_t
>
(
&
x
,
"one.two.three"
)
!=
nullptr
))
CAF_CHECK
(
get
<
int64_
t
>
(
x
,
"one.two.three"
)
==
4
);
}
CAF_TEST
(
get_or
)
{
...
...
@@ -199,11 +166,10 @@ CAF_TEST(get_or) {
CAF_TEST
(
custom
type
)
{
put
(
x
,
"my-value.foo"
,
42
);
put
(
x
,
"my-value.bar"
,
24
);
CAF_REQUIRE
(
holds_alternative
<
foobar
>
(
x
,
"my-value"
));
CAF_REQUIRE
(
get_if
<
foobar
>
(
&
x
,
"my-value"
)
!=
caf
::
none
);
auto
fb
=
get
<
foobar
>
(
x
,
"my-value"
);
CAF_CHECK_EQUAL
(
fb
.
foo
,
42
);
CAF_CHECK_EQUAL
(
fb
.
bar
,
24
);
if
(
auto
fb
=
get_as
<
foobar
>
(
x
,
"my-value"
);
CAF_CHECK
(
fb
))
{
CAF_CHECK_EQUAL
(
fb
->
foo
,
42
);
CAF_CHECK_EQUAL
(
fb
->
bar
,
24
);
}
}
CAF_TEST
(
read_config
accepts
the
to_string
output
of
settings
)
{
...
...
libcaf_core/test/span.cpp
View file @
dbdf9b26
...
...
@@ -86,10 +86,10 @@ CAF_TEST(subspans) {
CAF_TEST
(
free
iterator
functions
)
{
auto
xs
=
make_span
(
chars
);
CAF_CHECK
_EQUAL
(
xs
.
begin
(),
begin
(
xs
));
CAF_CHECK
_EQUAL
(
xs
.
cbegin
(),
cbegin
(
xs
));
CAF_CHECK
_EQUAL
(
xs
.
end
(),
end
(
xs
));
CAF_CHECK
_EQUAL
(
xs
.
cend
(),
cend
(
xs
));
CAF_CHECK
(
xs
.
begin
()
==
begin
(
xs
));
CAF_CHECK
(
xs
.
cbegin
()
==
cbegin
(
xs
));
CAF_CHECK
(
xs
.
end
()
==
end
(
xs
));
CAF_CHECK
(
xs
.
cend
()
==
cend
(
xs
));
}
CAF_TEST
(
as
bytes
)
{
...
...
@@ -108,10 +108,10 @@ CAF_TEST(make_span) {
CAF_CHECK
(
std
::
equal
(
xs
.
begin
(),
xs
.
end
(),
chars
.
begin
()));
CAF_CHECK
(
std
::
equal
(
ys
.
begin
(),
ys
.
end
(),
chars
.
begin
()));
CAF_CHECK
(
std
::
equal
(
zs
.
begin
(),
zs
.
end
(),
chars
.
begin
()));
CAF_CHECK
_EQUAL
(
end
(
xs
),
end
(
ys
));
CAF_CHECK
_EQUAL
(
end
(
ys
),
end
(
zs
));
CAF_CHECK
_EQUAL
(
begin
(
xs
),
begin
(
ys
));
CAF_CHECK
_EQUAL
(
begin
(
ys
),
begin
(
zs
));
CAF_CHECK
(
end
(
xs
)
==
end
(
ys
));
CAF_CHECK
(
end
(
ys
)
==
end
(
zs
));
CAF_CHECK
(
begin
(
xs
)
==
begin
(
ys
));
CAF_CHECK
(
begin
(
ys
)
==
begin
(
zs
));
}
CAF_TEST
(
spans
are
convertible
from
compatible
containers
)
{
...
...
libcaf_core/test/variant.cpp
View file @
dbdf9b26
...
...
@@ -81,8 +81,7 @@ using v20 = variant<i01, i02, i03, i04, i05, i06, i07, i08, i09, i10,
do { \
using type = std::decay_t<decltype(y)>; \
auto&& tmp = x; \
CAF_CHECK(holds_alternative<type>(tmp)); \
if (holds_alternative<type>(tmp)) \
if (CAF_CHECK(holds_alternative<type>(tmp))) \
CAF_CHECK_EQUAL(get<type>(tmp), y); \
} while (false)
...
...
@@ -102,9 +101,6 @@ using v20 = variant<i01, i02, i03, i04, i05, i06, i07, i08, i09, i10,
CAF_TEST
(
copying_moving_roundtrips
)
{
actor_system_config
cfg
;
actor_system
sys
{
cfg
};
// default construction
variant
<
none_t
>
x1
;
CAF_CHECK_EQUAL
(
x1
,
none
);
variant
<
int
,
none_t
>
x2
;
VARIANT_EQ
(
x2
,
0
);
v20
x3
;
...
...
@@ -129,11 +125,10 @@ CAF_TEST(constructors) {
variant
<
float
,
int
,
std
::
string
>
b
{
"bar"
s
};
variant
<
int
,
std
::
string
,
double
>
c
{
123
};
variant
<
bool
,
uint8_t
>
d
{
uint8_t
{
252
}};
CAF_CHECK_EQUAL
(
a
,
42
);
CAF_CHECK_EQUAL
(
b
,
"bar"
s
);
CAF_CHECK_EQUAL
(
c
,
123
);
CAF_CHECK_NOT_EQUAL
(
c
,
"123"
s
);
CAF_CHECK_EQUAL
(
d
,
uint8_t
{
252
});
VARIANT_EQ
(
a
,
42
);
VARIANT_EQ
(
b
,
"bar"
s
);
VARIANT_EQ
(
c
,
123
);
VARIANT_EQ
(
d
,
uint8_t
{
252
});
}
CAF_TEST
(
n_ary_visit
)
{
...
...
@@ -152,8 +147,7 @@ CAF_TEST(get_if) {
CAF_CHECK_EQUAL
(
get_if
<
int
>
(
&
b
),
nullptr
);
CAF_CHECK_NOT_EQUAL
(
get_if
<
std
::
string
>
(
&
b
),
nullptr
);
CAF_MESSAGE
(
"test get_if via unit test framework"
);
CAF_CHECK_NOT_EQUAL
(
b
,
42
);
CAF_CHECK_EQUAL
(
b
,
"foo"
s
);
VARIANT_EQ
(
b
,
"foo"
s
);
}
CAF_TEST
(
less_than
)
{
...
...
libcaf_io/src/io/basp/instance.cpp
View file @
dbdf9b26
...
...
@@ -46,7 +46,7 @@ instance::instance(abstract_broker* parent, callee& lstnr)
:
tbl_
(
parent
),
this_node_
(
parent
->
system
().
node
()),
callee_
(
lstnr
)
{
CAF_ASSERT
(
this_node_
!=
none
);
size_t
workers
;
if
(
auto
workers_cfg
=
get_
if
<
size_t
>
(
&
config
(),
"caf.middleman.workers"
))
if
(
auto
workers_cfg
=
get_
as
<
size_t
>
(
config
(),
"caf.middleman.workers"
))
workers
=
*
workers_cfg
;
else
workers
=
std
::
min
(
3u
,
std
::
thread
::
hardware_concurrency
()
/
4u
)
+
1
;
...
...
@@ -250,7 +250,7 @@ void instance::write_server_handshake(execution_unit* ctx, byte_buffer& out_buf,
auto
writer
=
make_callback
([
&
](
binary_serializer
&
sink
)
{
using
string_list
=
std
::
vector
<
std
::
string
>
;
string_list
app_ids
;
if
(
auto
ids
=
get_
if
<
string_list
>
(
&
config
(),
if
(
auto
ids
=
get_
as
<
string_list
>
(
config
(),
"caf.middleman.app-identifiers"
))
app_ids
=
std
::
move
(
*
ids
);
else
...
...
@@ -349,7 +349,7 @@ connection_state instance::handle(execution_unit* ctx, connection_handle hdl,
}
// Check the application ID.
string_list
whitelist
;
if
(
auto
ls
=
get_
if
<
string_list
>
(
&
config
(),
if
(
auto
ls
=
get_
as
<
string_list
>
(
config
(),
"caf.middleman.app-identifiers"
))
whitelist
=
std
::
move
(
*
ls
);
else
...
...
libcaf_io/src/io/middleman.cpp
View file @
dbdf9b26
...
...
@@ -130,7 +130,7 @@ public:
bool
start
(
const
config_value
::
dictionary
&
cfg
)
override
{
// Read port, address and reuse flag from the config.
uint16_t
port
=
0
;
if
(
auto
cfg_port
=
get_
if
<
uint16_t
>
(
&
cfg
,
"port"
))
{
if
(
auto
cfg_port
=
get_
as
<
uint16_t
>
(
cfg
,
"port"
))
{
port
=
*
cfg_port
;
}
else
{
return
false
;
...
...
libcaf_test/caf/test/bdd_dsl.hpp
View file @
dbdf9b26
...
...
@@ -46,3 +46,8 @@
#define REQUIRE_GE(lhs, rhs) CAF_REQUIRE_GREATER_OR_EQUAL(lhs, rhs)
#define MESSAGE(what) CAF_MESSAGE(what)
#define BEGIN_FIXTURE_SCOPE(fixture_class) \
CAF_TEST_FIXTURE_SCOPE(CAF_UNIFYN(tests), fixture_class)
#define END_FIXTURE_SCOPE() CAF_TEST_FIXTURE_SCOPE_END()
libcaf_test/caf/test/unit_test.hpp
View file @
dbdf9b26
...
...
@@ -483,6 +483,11 @@ bool check(test* parent, const char* file, size_t line, const char* expr,
return
result
;
}
bool
check_un
(
bool
result
,
const
char
*
file
,
size_t
line
,
const
char
*
expr
);
bool
check_bin
(
bool
result
,
const
char
*
file
,
size_t
line
,
const
char
*
expr
,
const
std
::
string
&
lhs
,
const
std
::
string
&
rhs
);
}
// namespace detail
}
// namespace caf::test
...
...
@@ -546,18 +551,13 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture;
} while (false)
#define CAF_CHECK(...) \
([](bool expr_result) { \
auto caf_check_res \
= ::caf::test::detail::check(::caf::test::engine::current_test(), \
__FILE__, __LINE__, #__VA_ARGS__, false, \
expr_result); \
::caf::test::engine::last_check_file(__FILE__); \
::caf::test::engine::last_check_line(__LINE__); \
return caf_check_res; \
})(static_cast<bool>(__VA_ARGS__))
[](bool expr_result) { \
return ::caf::test::detail::check_un(expr_result, __FILE__, __LINE__, \
#__VA_ARGS__); \
}(static_cast<bool>(__VA_ARGS__))
#define CAF_CHECK_FUNC(func, x_expr, y_expr) \
([](auto&& x_val, auto&& y_val) {
\
[](auto&& x_val, auto&& y_val) {
\
func comparator; \
auto caf_check_res \
= ::caf::test::detail::check(::caf::test::engine::current_test(), \
...
...
@@ -567,7 +567,7 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture;
::caf::test::engine::last_check_file(__FILE__); \
::caf::test::engine::last_check_line(__LINE__); \
return caf_check_res; \
}
)
(x_expr, y_expr)
}(x_expr, y_expr)
#define CAF_CHECK_FAIL(...) \
do { \
...
...
@@ -642,32 +642,81 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture;
// -- CAF_CHECK* predicate family ----------------------------------------------
#define CAF_CHECK_EQUAL(x, y) CAF_CHECK_FUNC(::caf::test::equal_to, x, y)
#define CAF_CHECK_NOT_EQUAL(x, y) \
CAF_CHECK_FUNC(::caf::test::not_equal_to, x, y)
#define CAF_CHECK_LESS(x, y) CAF_CHECK_FUNC(::caf::test::less_than, x, y)
#define CAF_CHECK_NOT_LESS(x, y) \
CAF_CHECK_FUNC(::caf::test::negated<::caf::test::less_than>, x, y)
#define CAF_CHECK_LESS_OR_EQUAL(x, y) \
CAF_CHECK_FUNC(::caf::test::less_than_or_equal, x, y)
#define CAF_CHECK_NOT_LESS_OR_EQUAL(x, y) \
CAF_CHECK_FUNC(::caf::test::negated<::caf::test::less_than_or_equal>, x, y)
#define CAF_CHECK_GREATER(x, y) CAF_CHECK_FUNC(::caf::test::greater_than, x, y)
#define CAF_CHECK_NOT_GREATER(x, y) \
CAF_CHECK_FUNC(::caf::test::negated<::caf::test::greater_than>, x, y)
#define CAF_CHECK_GREATER_OR_EQUAL(x, y) \
CAF_CHECK_FUNC(::caf::test::greater_than_or_equal, x, y)
#define CAF_CHECK_NOT_GREATER_OR_EQUAL(x, y) \
CAF_CHECK_FUNC(::caf::test::negated<::caf::test::greater_than_or_equal>, x, y)
#define CAF_CHECK_EQUAL(x_expr, y_expr) \
[](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin(x_val == y_val, __FILE__, __LINE__, \
#x_expr " == " #y_expr, \
caf::deep_to_string(x_val), \
caf::deep_to_string(y_val)); \
}(x_expr, y_expr)
#define CAF_CHECK_NOT_EQUAL(x_expr, y_expr) \
[](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin(x_val != y_val, __FILE__, __LINE__, \
#x_expr " != " #y_expr, \
caf::deep_to_string(x_val), \
caf::deep_to_string(y_val)); \
}(x_expr, y_expr)
#define CAF_CHECK_LESS(x_expr, y_expr) \
[](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin(x_val < y_val, __FILE__, __LINE__, \
#x_expr " < " #y_expr, \
caf::deep_to_string(x_val), \
caf::deep_to_string(y_val)); \
}(x_expr, y_expr)
#define CAF_CHECK_NOT_LESS(x_expr, y_expr) \
[](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin( \
!(x_val < y_val), __FILE__, __LINE__, "not " #x_expr " < " #y_expr, \
caf::deep_to_string(x_val), caf::deep_to_string(y_val)); \
}(x_expr, y_expr)
#define CAF_CHECK_LESS_OR_EQUAL(x_expr, y_expr) \
[](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin(x_val <= y_val, __FILE__, __LINE__, \
#x_expr " <= " #y_expr, \
caf::deep_to_string(x_val), \
caf::deep_to_string(y_val)); \
}(x_expr, y_expr)
#define CAF_CHECK_NOT_LESS_OR_EQUAL(x_expr, y_expr) \
[](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin( \
!(x_val <= y_val), __FILE__, __LINE__, "not " #x_expr " <= " #y_expr, \
caf::deep_to_string(x_val), caf::deep_to_string(y_val)); \
}(x_expr, y_expr)
#define CAF_CHECK_GREATER(x_expr, y_expr) \
[](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin(x_val > y_val, __FILE__, __LINE__, \
#x_expr " > " #y_expr, \
caf::deep_to_string(x_val), \
caf::deep_to_string(y_val)); \
}(x_expr, y_expr)
#define CAF_CHECK_NOT_GREATER(x_expr, y_expr) \
[](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin( \
!(x_val > y_val), __FILE__, __LINE__, "not " #x_expr " > " #y_expr, \
caf::deep_to_string(x_val), caf::deep_to_string(y_val)); \
}(x_expr, y_expr)
#define CAF_CHECK_GREATER_OR_EQUAL(x_expr, y_expr) \
[](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin(x_val >= y_val, __FILE__, __LINE__, \
#x_expr " >= " #y_expr, \
caf::deep_to_string(x_val), \
caf::deep_to_string(y_val)); \
}(x_expr, y_expr)
#define CAF_CHECK_NOT_GREATER_OR_EQUAL(x_expr, y_expr) \
[](const auto& x_val, const auto& y_val) { \
return ::caf::test::detail::check_bin( \
!(x_val >= y_val), __FILE__, __LINE__, "not " #x_expr " >= " #y_expr, \
caf::deep_to_string(x_val), caf::deep_to_string(y_val)); \
}(x_expr, y_expr)
// -- CAF_CHECK* predicate family ----------------------------------------------
...
...
libcaf_test/caf/test/unit_test_impl.hpp
View file @
dbdf9b26
...
...
@@ -165,6 +165,51 @@ bool check(test* parent, const char* file, size_t line, const char* expr,
return
result
;
}
bool
check_un
(
bool
result
,
const
char
*
file
,
size_t
line
,
const
char
*
expr
)
{
string_view
rel_up
=
"../"
;
while
(
strncmp
(
file
,
rel_up
.
data
(),
rel_up
.
size
())
==
0
)
file
+=
rel_up
.
size
();
auto
parent
=
engine
::
current_test
();
auto
out
=
logger
::
instance
().
massive
();
if
(
result
)
{
out
<<
term
::
green
<<
"** "
<<
term
::
blue
<<
file
<<
term
::
yellow
<<
":"
<<
term
::
blue
<<
line
<<
fill
(
line
)
<<
term
::
reset
<<
"passed"
<<
'\n'
;
parent
->
pass
();
}
else
{
out
<<
term
::
red
<<
"!! "
<<
term
::
blue
<<
file
<<
term
::
yellow
<<
":"
<<
term
::
blue
<<
line
<<
fill
(
line
)
<<
term
::
red
<<
"check failed: "
<<
expr
<<
term
::
reset
<<
'\n'
;
parent
->
fail
(
false
);
}
engine
::
last_check_file
(
file
);
engine
::
last_check_line
(
line
);
return
result
;
}
bool
check_bin
(
bool
result
,
const
char
*
file
,
size_t
line
,
const
char
*
expr
,
const
std
::
string
&
lhs
,
const
std
::
string
&
rhs
)
{
string_view
rel_up
=
"../"
;
while
(
strncmp
(
file
,
rel_up
.
data
(),
rel_up
.
size
())
==
0
)
file
+=
rel_up
.
size
();
auto
parent
=
engine
::
current_test
();
auto
out
=
logger
::
instance
().
massive
();
if
(
result
)
{
out
<<
term
::
green
<<
"** "
<<
term
::
blue
<<
file
<<
term
::
yellow
<<
":"
<<
term
::
blue
<<
line
<<
fill
(
line
)
<<
term
::
reset
<<
"passed"
<<
'\n'
;
parent
->
pass
();
}
else
{
out
<<
term
::
red
<<
"!! "
<<
term
::
blue
<<
file
<<
term
::
yellow
<<
":"
<<
term
::
blue
<<
line
<<
fill
(
line
)
<<
term
::
red
<<
"check failed: "
<<
expr
<<
term
::
reset
<<
'\n'
<<
" lhs: "
<<
lhs
<<
'\n'
<<
" rhs: "
<<
rhs
<<
'\n'
;
parent
->
fail
(
false
);
}
engine
::
last_check_file
(
file
);
engine
::
last_check_line
(
line
);
return
result
;
}
}
// namespace detail
logger
::
stream
::
stream
(
logger
&
parent
,
logger
::
level
lvl
)
...
...
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