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
2ee9e994
Unverified
Commit
2ee9e994
authored
May 06, 2020
by
Joseph Noir
Committed by
GitHub
May 06, 2020
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1101
Fix CLI parsing and ref syncing
parents
9e801235
0babb782
Changes
18
Show whitespace changes
Inline
Side-by-side
Showing
18 changed files
with
337 additions
and
107 deletions
+337
-107
.clang-format
.clang-format
+4
-4
libcaf_core/caf/config_option_set.hpp
libcaf_core/caf/config_option_set.hpp
+5
-0
libcaf_core/caf/config_value.hpp
libcaf_core/caf/config_value.hpp
+107
-22
libcaf_core/caf/config_value_adaptor_access.hpp
libcaf_core/caf/config_value_adaptor_access.hpp
+3
-2
libcaf_core/caf/config_value_field.hpp
libcaf_core/caf/config_value_field.hpp
+1
-1
libcaf_core/caf/config_value_object_access.hpp
libcaf_core/caf/config_value_object_access.hpp
+4
-3
libcaf_core/caf/detail/config_value_adaptor_field_impl.hpp
libcaf_core/caf/detail/config_value_adaptor_field_impl.hpp
+0
-1
libcaf_core/caf/detail/config_value_field_base.hpp
libcaf_core/caf/detail/config_value_field_base.hpp
+6
-3
libcaf_core/caf/detail/config_value_field_impl.hpp
libcaf_core/caf/detail/config_value_field_impl.hpp
+0
-1
libcaf_core/caf/detail/dispatch_parse_cli.hpp
libcaf_core/caf/detail/dispatch_parse_cli.hpp
+0
-62
libcaf_core/caf/detail/parser/read_uri.hpp
libcaf_core/caf/detail/parser/read_uri.hpp
+3
-2
libcaf_core/caf/detail/type_traits.hpp
libcaf_core/caf/detail/type_traits.hpp
+1
-0
libcaf_core/caf/make_config_option.hpp
libcaf_core/caf/make_config_option.hpp
+14
-1
libcaf_core/src/config_option_set.cpp
libcaf_core/src/config_option_set.cpp
+1
-0
libcaf_core/test/config_option_set.cpp
libcaf_core/test/config_option_set.cpp
+82
-0
libcaf_core/test/config_value.cpp
libcaf_core/test/config_value.cpp
+83
-4
libcaf_core/test/make_config_value_field.cpp
libcaf_core/test/make_config_value_field.cpp
+1
-1
libcaf_test/caf/test/unit_test.hpp
libcaf_test/caf/test/unit_test.hpp
+22
-0
No files found.
.clang-format
View file @
2ee9e994
...
...
@@ -32,13 +32,13 @@ IndentWidth: 2
IndentWrappedFunctionNames: false
KeepEmptyLinesAtTheStartOfBlocks: false
Language: Cpp
MacroBlockBegin: "^BEGIN_STATE$"
MacroBlockEnd: "^END_STATE$"
MacroBlockBegin: "^BEGIN_STATE$
|CAF_BEGIN_TYPE_ID_BLOCK
"
MacroBlockEnd: "^END_STATE$
|CAF_END_TYPE_ID_BLOCK
"
MaxEmptyLinesToKeep: 1
NamespaceIndentation: None
PenaltyBreakAssignment:
10
PenaltyBreakAssignment:
25
PenaltyBreakBeforeFirstCallParameter: 30
PenaltyReturnTypeOnItsOwnLine: 5
PenaltyReturnTypeOnItsOwnLine:
2
5
PointerAlignment: Left
ReflowComments: true
SortIncludes: true
...
...
libcaf_core/caf/config_option_set.hpp
View file @
2ee9e994
...
...
@@ -165,6 +165,11 @@ public:
/// Generates human-readable help text for all options.
std
::
string
help_text
(
bool
global_only
=
true
)
const
;
/// Drops all options.
void
clear
()
{
opts_
.
clear
();
}
// -- parsing ----------------------------------------------------------------
/// Parses a given range as CLI arguments into `config`.
...
...
libcaf_core/caf/config_value.hpp
View file @
2ee9e994
...
...
@@ -216,6 +216,22 @@ private:
variant_type
data_
;
};
// -- convenience constants ----------------------------------------------------
/// Type of the `top_level_cli_parsing` constant.
using
top_level_cli_parsing_t
=
std
::
false_type
;
/// Signals parsing of top-level config values when used as third argument to
/// `parse_cli`.
constexpr
auto
top_level_cli_parsing
=
top_level_cli_parsing_t
{};
/// Type of the `top_level_cli_parsing` constant.
using
nested_cli_parsing_t
=
std
::
true_type
;
/// Signals parsing of nested config values when used as third argument to
/// `parse_cli`.
constexpr
auto
nested_cli_parsing
=
nested_cli_parsing_t
{};
// -- SumType-like access ------------------------------------------------------
template
<
class
T
>
...
...
@@ -236,7 +252,8 @@ struct default_config_value_access {
return
x
;
}
static
void
parse_cli
(
string_parser_state
&
ps
,
T
&
x
)
{
template
<
class
Nested
>
static
void
parse_cli
(
string_parser_state
&
ps
,
T
&
x
,
Nested
)
{
detail
::
parse
(
ps
,
x
);
}
};
...
...
@@ -266,19 +283,36 @@ CAF_DEFAULT_CONFIG_VALUE_ACCESS(config_value::dictionary, "dictionary");
#undef CAF_DEFAULT_CONFIG_VALUE_ACCESS
template
<
>
struct
config_value_access
<
std
::
string
>
:
default_config_value_access
<
std
::
string
>
{
struct
config_value_access
<
std
::
string
>
{
using
super
=
default_config_value_access
<
std
::
string
>
;
static
std
::
string
type_name
()
{
return
"string"
;
}
using
super
::
parse_cli
;
static
bool
is
(
const
config_value
&
x
)
{
return
holds_alternative
<
std
::
string
>
(
x
.
get_data
());
}
static
const
std
::
string
*
get_if
(
const
config_value
*
x
)
{
return
caf
::
get_if
<
std
::
string
>
(
&
(
x
->
get_data
()));
}
static
std
::
string
get
(
const
config_value
&
x
)
{
return
caf
::
get
<
std
::
string
>
(
x
.
get_data
());
}
static
std
::
string
convert
(
std
::
string
x
)
{
return
x
;
}
template
<
bool
IsNested
>
static
void
parse_cli
(
string_parser_state
&
ps
,
std
::
string
&
x
,
const
char
*
char_blacklist
)
{
detail
::
parse_element
(
ps
,
x
,
char_blacklist
);
std
::
integral_constant
<
bool
,
IsNested
>
)
{
if
(
IsNested
)
detail
::
parse_element
(
ps
,
x
,
",={}[]"
);
else
detail
::
parse
(
ps
,
x
);
}
};
...
...
@@ -426,7 +460,8 @@ struct select_config_value_access<T, select_config_value_hint::is_integral> {
return
x
;
}
static
void
parse_cli
(
string_parser_state
&
ps
,
T
&
x
)
{
template
<
class
Nested
>
static
void
parse_cli
(
string_parser_state
&
ps
,
T
&
x
,
Nested
)
{
detail
::
parse
(
ps
,
x
);
}
};
...
...
@@ -486,23 +521,70 @@ struct select_config_value_access<T, select_config_value_hint::is_list> {
return
result
;
}
static
void
parse_cli
(
string_parser_state
&
ps
,
T
&
xs
)
{
config_value
::
dictionary
result
;
bool
has_open_token
=
ps
.
consume
(
'['
);
static
void
parse_cli
(
string_parser_state
&
ps
,
T
&
xs
,
top_level_cli_parsing_t
)
{
bool
has_open_token
;
auto
subtype
=
select_config_value_oracle
<
value_type
>
();
if
(
subtype
==
select_config_value_hint
::
is_list
)
{
// The outer square brackets are optional in nested lists. This means we
// need to check for "[[" at the beginning and otherwise we assume the
// leading '[' was omitted.
string_parser_state
tmp
{
ps
.
i
,
ps
.
e
};
has_open_token
=
tmp
.
consume
(
'['
)
&&
tmp
.
consume
(
'['
);
if
(
has_open_token
)
ps
.
consume
(
'['
);
}
else
{
has_open_token
=
ps
.
consume
(
'['
);
}
do
{
if
(
has_open_token
&&
ps
.
consume
(
']'
))
{
ps
.
skip_whitespaces
();
if
(
has_open_token
)
{
if
(
ps
.
consume
(
']'
))
{
ps
.
skip_whitespaces
();
ps
.
code
=
ps
.
at_end
()
?
pec
::
success
:
pec
::
trailing_character
;
return
;
}
}
else
if
(
ps
.
at_end
())
{
// Allow trailing commas and empty strings.
ps
.
code
=
pec
::
success
;
return
;
}
value_type
tmp
;
value_trait
::
parse_cli
(
ps
,
tmp
);
value_trait
::
parse_cli
(
ps
,
tmp
,
nested_cli_parsing
);
if
(
ps
.
code
>
pec
::
trailing_character
)
return
;
xs
.
insert
(
xs
.
end
(),
std
::
move
(
tmp
));
}
while
(
ps
.
consume
(
','
));
if
(
has_open_token
&&
!
ps
.
consume
(
']'
))
if
(
has_open_token
&&
!
ps
.
consume
(
']'
))
{
ps
.
code
=
ps
.
at_end
()
?
pec
::
unexpected_eof
:
pec
::
unexpected_character
;
return
;
}
ps
.
skip_whitespaces
();
ps
.
code
=
ps
.
at_end
()
?
pec
::
success
:
pec
::
trailing_character
;
}
static
void
parse_cli
(
string_parser_state
&
ps
,
T
&
xs
,
nested_cli_parsing_t
)
{
if
(
!
ps
.
consume
(
'['
))
{
ps
.
code
=
ps
.
at_end
()
?
pec
::
unexpected_eof
:
pec
::
unexpected_character
;
return
;
}
do
{
if
(
ps
.
consume
(
']'
))
{
ps
.
skip_whitespaces
();
ps
.
code
=
ps
.
at_end
()
?
pec
::
success
:
pec
::
trailing_character
;
return
;
}
value_type
tmp
;
value_trait
::
parse_cli
(
ps
,
tmp
,
nested_cli_parsing
);
if
(
ps
.
code
>
pec
::
trailing_character
)
return
;
xs
.
insert
(
xs
.
end
(),
std
::
move
(
tmp
));
}
while
(
ps
.
consume
(
','
));
if
(
!
ps
.
consume
(
']'
))
{
ps
.
code
=
ps
.
at_end
()
?
pec
::
unexpected_eof
:
pec
::
unexpected_character
;
return
;
}
ps
.
skip_whitespaces
();
ps
.
code
=
ps
.
at_end
()
?
pec
::
success
:
pec
::
trailing_character
;
}
...
...
@@ -559,7 +641,8 @@ struct select_config_value_access<T, select_config_value_hint::is_map> {
return
std
::
move
(
*
result
);
}
static
void
parse_cli
(
string_parser_state
&
ps
,
map_type
&
xs
)
{
template
<
class
Nested
>
static
void
parse_cli
(
string_parser_state
&
ps
,
map_type
&
xs
,
Nested
)
{
detail
::
parse
(
ps
,
xs
);
}
...
...
@@ -574,29 +657,30 @@ struct select_config_value_access<T, select_config_value_hint::is_map> {
template
<
>
struct
config_value_access
<
float
>
{
static
inline
std
::
string
type_name
()
{
static
std
::
string
type_name
()
{
return
"real32"
;
}
static
inline
bool
is
(
const
config_value
&
x
)
{
static
bool
is
(
const
config_value
&
x
)
{
return
holds_alternative
<
double
>
(
x
.
get_data
());
}
static
inline
optional
<
float
>
get_if
(
const
config_value
*
x
)
{
static
optional
<
float
>
get_if
(
const
config_value
*
x
)
{
if
(
auto
res
=
caf
::
get_if
<
double
>
(
&
(
x
->
get_data
())))
return
static_cast
<
float
>
(
*
res
);
return
none
;
}
static
inline
float
get
(
const
config_value
&
x
)
{
static
float
get
(
const
config_value
&
x
)
{
return
static_cast
<
float
>
(
caf
::
get
<
double
>
(
x
.
get_data
()));
}
static
inline
double
convert
(
float
x
)
{
static
double
convert
(
float
x
)
{
return
x
;
}
static
inline
void
parse_cli
(
string_parser_state
&
ps
,
float
&
x
)
{
template
<
class
Nested
>
static
void
parse_cli
(
string_parser_state
&
ps
,
float
&
x
,
Nested
)
{
detail
::
parse
(
ps
,
x
);
}
};
...
...
@@ -648,7 +732,8 @@ struct config_value_access<std::tuple<Ts...>> {
return
result
;
}
static
void
parse_cli
(
string_parser_state
&
ps
,
tuple_type
&
xs
)
{
template
<
class
Nested
>
static
void
parse_cli
(
string_parser_state
&
ps
,
tuple_type
&
xs
,
Nested
)
{
rec_parse
(
ps
,
xs
,
detail
::
int_token
<
0
>
(),
detail
::
type_list
<
Ts
...
>
());
}
...
...
@@ -727,7 +812,7 @@ private:
static
void
rec_parse
(
string_parser_state
&
ps
,
tuple_type
&
xs
,
detail
::
int_token
<
Pos
>
,
detail
::
type_list
<
U
,
Us
...
>
)
{
using
trait
=
select_config_value_access_t
<
U
>
;
trait
::
parse_cli
(
std
::
get
<
Pos
>
(
xs
));
trait
::
parse_cli
(
std
::
get
<
Pos
>
(
xs
)
,
nested_cli_parsing
);
if
(
ps
.
code
>
pec
::
trailing_character
)
return
;
if
(
sizeof
...(
Us
)
>
0
&&
!
ps
.
consume
(
','
))
...
...
libcaf_core/caf/config_value_adaptor_access.hpp
View file @
2ee9e994
...
...
@@ -90,9 +90,10 @@ struct config_value_adaptor_access {
return
result
;
}
static
void
parse_cli
(
string_parser_state
&
ps
,
value_type
&
x
)
{
template
<
class
Nested
>
static
void
parse_cli
(
string_parser_state
&
ps
,
value_type
&
x
,
Nested
nested
)
{
tuple_type
tmp
;
tuple_access
::
parse_cli
(
ps
,
tmp
);
tuple_access
::
parse_cli
(
ps
,
tmp
,
nested
);
if
(
ps
.
code
<=
pec
::
trailing_character
)
convert
(
tmp
,
x
);
}
...
...
libcaf_core/caf/config_value_field.hpp
View file @
2ee9e994
...
...
@@ -57,7 +57,7 @@ public:
/// Parses the content for this field in `object` from `ps`.
virtual
void
parse_cli
(
string_parser_state
&
ps
,
Object
&
object
,
const
char
*
char_blacklist
=
""
)
const
=
0
;
bool
is_nested
)
const
=
0
;
};
}
// namespace caf
libcaf_core/caf/config_value_object_access.hpp
View file @
2ee9e994
...
...
@@ -91,7 +91,8 @@ struct config_value_object_access {
return
result
;
}
static
void
parse_cli
(
string_parser_state
&
ps
,
object_type
&
x
)
{
template
<
class
Nested
>
static
void
parse_cli
(
string_parser_state
&
ps
,
object_type
&
x
,
Nested
)
{
using
field_type
=
config_value_field
<
object_type
>
;
std
::
vector
<
field_type
*>
parsed_fields
;
auto
got
=
[
&
](
field_type
*
f
)
{
...
...
@@ -131,7 +132,7 @@ struct config_value_object_access {
return
;
}
std
::
string
field_name
;
string_access
::
parse_cli
(
ps
,
field_name
,
"=}"
);
string_access
::
parse_cli
(
ps
,
field_name
,
nested_cli_parsing
);
if
(
ps
.
code
>
pec
::
trailing_character
)
return
;
if
(
!
ps
.
consume
(
'='
))
{
...
...
@@ -151,7 +152,7 @@ struct config_value_object_access {
ps
.
code
=
pec
::
repeated_field_name
;
return
;
}
fptr
->
parse_cli
(
ps
,
x
,
",}"
);
fptr
->
parse_cli
(
ps
,
x
,
true
);
if
(
ps
.
code
>
pec
::
trailing_character
)
return
;
if
(
ps
.
at_end
())
{
...
...
libcaf_core/caf/detail/config_value_adaptor_field_impl.hpp
View file @
2ee9e994
...
...
@@ -25,7 +25,6 @@
#include "caf/config_value_adaptor_field.hpp"
#include "caf/config_value_field.hpp"
#include "caf/detail/config_value_field_base.hpp"
#include "caf/detail/dispatch_parse_cli.hpp"
#include "caf/string_view.hpp"
namespace
caf
{
...
...
libcaf_core/caf/detail/config_value_field_base.hpp
View file @
2ee9e994
...
...
@@ -20,7 +20,6 @@
#include "caf/config_value.hpp"
#include "caf/config_value_field.hpp"
#include "caf/detail/dispatch_parse_cli.hpp"
#include "caf/optional.hpp"
namespace
caf
{
...
...
@@ -83,9 +82,13 @@ public:
}
void
parse_cli
(
string_parser_state
&
ps
,
object_type
&
x
,
const
char
*
char_blacklist
)
const
override
{
bool
nested
)
const
override
{
using
access
=
caf
::
select_config_value_access_t
<
value_type
>
;
value_type
tmp
;
dispatch_parse_cli
(
ps
,
tmp
,
char_blacklist
);
if
(
nested
)
access
::
parse_cli
(
ps
,
tmp
,
nested_cli_parsing
);
else
access
::
parse_cli
(
ps
,
tmp
,
top_level_cli_parsing
);
if
(
ps
.
code
<=
pec
::
trailing_character
)
{
if
(
predicate_
&&
!
predicate_
(
tmp
))
ps
.
code
=
pec
::
illegal_argument
;
...
...
libcaf_core/caf/detail/config_value_field_impl.hpp
View file @
2ee9e994
...
...
@@ -24,7 +24,6 @@
#include "caf/config_value.hpp"
#include "caf/config_value_field.hpp"
#include "caf/detail/config_value_field_base.hpp"
#include "caf/detail/dispatch_parse_cli.hpp"
#include "caf/detail/parse.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/optional.hpp"
...
...
libcaf_core/caf/detail/dispatch_parse_cli.hpp
deleted
100644 → 0
View file @
9e801235
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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
{
namespace
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 detail
}
// namespace caf
libcaf_core/caf/detail/parser/read_uri.hpp
View file @
2ee9e994
...
...
@@ -130,8 +130,9 @@ void read_uri(State& ps, Consumer&& consumer) {
return
res
;
};
// Allowed character sets.
auto
path_char
=
[](
char
c
)
{
return
uri_unprotected_char
(
c
)
||
c
==
'/'
||
c
==
':'
;
};
auto
path_char
=
[](
char
c
)
{
return
uri_unprotected_char
(
c
)
||
c
==
'/'
||
c
==
':'
;
};
// Utility setters for avoiding code duplication.
auto
set_path
=
[
&
]
{
consumer
.
path
(
take_str
());
};
auto
set_host
=
[
&
]
{
consumer
.
host
(
take_str
());
};
...
...
libcaf_core/caf/detail/type_traits.hpp
View file @
2ee9e994
...
...
@@ -638,6 +638,7 @@ public:
CAF_HAS_MEMBER_TRAIT
(
size
);
CAF_HAS_MEMBER_TRAIT
(
data
);
CAF_HAS_MEMBER_TRAIT
(
clear
);
/// Checks whether F is convertible to either `std::function<void (T&)>`
/// or `std::function<void (const T&)>`.
...
...
libcaf_core/caf/make_config_option.hpp
View file @
2ee9e994
...
...
@@ -52,15 +52,28 @@ config_value get_impl(const void* ptr) {
return
config_value
{
trait
::
convert
(
*
reinterpret_cast
<
const
T
*>
(
ptr
))};
}
template
<
class
T
>
typename
std
::
enable_if
<
detail
::
has_clear_member
<
T
>::
value
>::
type
clear_before_parsing
(
T
&
xs
)
{
xs
.
clear
();
}
template
<
class
T
>
typename
std
::
enable_if
<!
detail
::
has_clear_member
<
T
>::
value
>::
type
clear_before_parsing
(
T
&
)
{
// nop
}
template
<
class
T
>
expected
<
config_value
>
parse_impl
(
T
*
ptr
,
string_view
str
)
{
if
(
!
ptr
)
{
T
tmp
;
return
parse_impl
(
&
tmp
,
str
);
}
clear_before_parsing
(
*
ptr
);
using
trait
=
select_config_value_access_t
<
T
>
;
string_parser_state
ps
{
str
.
begin
(),
str
.
end
()};
trait
::
parse_cli
(
ps
,
*
ptr
);
trait
::
parse_cli
(
ps
,
*
ptr
,
top_level_cli_parsing
);
if
(
ps
.
code
!=
pec
::
success
)
return
make_error
(
ps
);
return
config_value
{
trait
::
convert
(
*
ptr
)};
...
...
libcaf_core/src/config_option_set.cpp
View file @
2ee9e994
...
...
@@ -161,6 +161,7 @@ auto config_option_set::parse(settings& config, argument_iterator first,
return
static_cast
<
pec
>
(
err
.
code
());
return
pec
::
illegal_argument
;
}
opt
.
store
(
*
val
);
entry
[
opt_name
]
=
std
::
move
(
*
val
);
}
return
pec
::
success
;
...
...
libcaf_core/test/config_option_set.cpp
View file @
2ee9e994
...
...
@@ -38,6 +38,14 @@ namespace {
struct
fixture
{
config_option_set
opts
;
template
<
class
T
>
error
read
(
settings
&
cfg
,
std
::
vector
<
std
::
string
>
args
)
{
auto
res
=
opts
.
parse
(
cfg
,
std
::
move
(
args
));
if
(
res
.
first
!=
pec
::
success
)
return
res
.
first
;
return
none
;
}
template
<
class
T
>
expected
<
T
>
read
(
std
::
vector
<
std
::
string
>
args
)
{
settings
cfg
;
...
...
@@ -160,4 +168,78 @@ CAF_TEST(flat CLI parsing with nested categories) {
CAF_CHECK_EQUAL
(
read
<
std
::
string
>
({
"--foo.goo.bar=foobar"
}),
"foobar"
);
}
CAF_TEST
(
square
brackets
are
optional
on
the
command
line
)
{
using
int_list
=
std
::
vector
<
int
>
;
opts
.
add
<
int_list
>
(
"global"
,
"value,v"
,
"some list"
);
CAF_CHECK_EQUAL
(
read
<
int_list
>
({
"--value=[1]"
}),
int_list
({
1
}));
CAF_CHECK_EQUAL
(
read
<
int_list
>
({
"--value=[1,]"
}),
int_list
({
1
}));
CAF_CHECK_EQUAL
(
read
<
int_list
>
({
"--value=[ 1 , ]"
}),
int_list
({
1
}));
CAF_CHECK_EQUAL
(
read
<
int_list
>
({
"--value=[1,2]"
}),
int_list
({
1
,
2
}));
CAF_CHECK_EQUAL
(
read
<
int_list
>
({
"--value=[1, 2, 3]"
}),
int_list
({
1
,
2
,
3
}));
CAF_CHECK_EQUAL
(
read
<
int_list
>
({
"--value=[1, 2, 3, ]"
}),
int_list
({
1
,
2
,
3
}));
CAF_CHECK_EQUAL
(
read
<
int_list
>
({
"--value=1"
}),
int_list
({
1
}));
CAF_CHECK_EQUAL
(
read
<
int_list
>
({
"--value=1,2,3"
}),
int_list
({
1
,
2
,
3
}));
CAF_CHECK_EQUAL
(
read
<
int_list
>
({
"--value=1, 2 , 3 , "
}),
int_list
({
1
,
2
,
3
}));
CAF_CHECK_EQUAL
(
read
<
int_list
>
({
"-v"
,
"[1]"
}),
int_list
({
1
}));
CAF_CHECK_EQUAL
(
read
<
int_list
>
({
"-v"
,
"[1,]"
}),
int_list
({
1
}));
CAF_CHECK_EQUAL
(
read
<
int_list
>
({
"-v"
,
"[ 1 , ]"
}),
int_list
({
1
}));
CAF_CHECK_EQUAL
(
read
<
int_list
>
({
"-v"
,
"[1,2]"
}),
int_list
({
1
,
2
}));
CAF_CHECK_EQUAL
(
read
<
int_list
>
({
"-v"
,
"[1, 2, 3]"
}),
int_list
({
1
,
2
,
3
}));
CAF_CHECK_EQUAL
(
read
<
int_list
>
({
"-v"
,
"[1, 2, 3, ]"
}),
int_list
({
1
,
2
,
3
}));
CAF_CHECK_EQUAL
(
read
<
int_list
>
({
"-v"
,
"1"
}),
int_list
({
1
}));
CAF_CHECK_EQUAL
(
read
<
int_list
>
({
"-v"
,
"1,2,3"
}),
int_list
({
1
,
2
,
3
}));
CAF_CHECK_EQUAL
(
read
<
int_list
>
({
"-v"
,
"1, 2 , 3 , "
}),
int_list
({
1
,
2
,
3
}));
}
#define SUBTEST(msg) \
opts.clear(); \
if (true)
CAF_TEST
(
CLI
arguments
override
defaults
)
{
using
int_list
=
std
::
vector
<
int
>
;
using
string_list
=
std
::
vector
<
std
::
string
>
;
SUBTEST
(
"with ref syncing"
)
{
settings
cfg
;
int_list
ints
;
string_list
strings
;
CAF_MESSAGE
(
"add --foo and --bar options"
);
opts
.
add
(
strings
,
"global"
,
"foo,f"
,
"some list"
);
opts
.
add
(
ints
,
"global"
,
"bar,b"
,
"some list"
);
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
(
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_MESSAGE
(
"test string lists"
);
strings
=
string_list
{
"one"
,
"two"
,
"three"
};
cfg
[
"foo"
]
=
config_value
{
strings
};
CAF_CHECK_EQUAL
(
get
<
string_list
>
(
cfg
,
"foo"
),
string_list
({
"one"
,
"two"
,
"three"
}));
CAF_CHECK_EQUAL
(
read
<
string_list
>
(
cfg
,
{
"--foo=[hello, world]"
}),
none
);
CAF_CHECK_EQUAL
(
strings
,
string_list
({
"hello"
,
"world"
}));
CAF_CHECK_EQUAL
(
get
<
string_list
>
(
cfg
,
"foo"
),
string_list
({
"hello"
,
"world"
}));
}
SUBTEST
(
"without ref syncing"
)
{
settings
cfg
;
CAF_MESSAGE
(
"add --foo and --bar options"
);
opts
.
add
<
string_list
>
(
"global"
,
"foo,f"
,
"some list"
);
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
(
read
<
int_list
>
(
cfg
,
{
"--bar=[10, 20, 30]"
}),
none
);
CAF_CHECK_EQUAL
(
get
<
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"
),
string_list
({
"one"
,
"two"
,
"three"
}));
CAF_CHECK_EQUAL
(
read
<
string_list
>
(
cfg
,
{
"--foo=[hello, world]"
}),
none
);
CAF_CHECK_EQUAL
(
get
<
string_list
>
(
cfg
,
"foo"
),
string_list
({
"hello"
,
"world"
}));
}
}
CAF_TEST_FIXTURE_SCOPE_END
()
libcaf_core/test/config_value.cpp
View file @
2ee9e994
...
...
@@ -16,10 +16,11 @@
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "caf/config.hpp"
#define CAF_SUITE config_value
#include "caf/test/unit_test.hpp"
#include "caf/config_value.hpp"
#include "caf/test/dsl.hpp"
#include <list>
#include <map>
...
...
@@ -34,10 +35,11 @@
#include "caf/atom.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/detail/bounds_checker.hpp"
#include "caf/make_config_option.hpp"
#include "caf/none.hpp"
#include "caf/pec.hpp"
#include "caf/variant.hpp"
#include "caf/string_view.hpp"
#include "caf/variant.hpp"
using
std
::
string
;
...
...
@@ -255,6 +257,83 @@ CAF_TEST(successful parsing) {
CAF_CHECK_EQUAL
(
get
<
di
>
(
parse
(
"{a=1,b=2}"
)),
di
({{
"a"
,
1
},
{
"b"
,
2
}}));
}
#define CHECK_CLI_PARSE(type, str, ...) \
do { \
/* Note: parse_impl from make_config_option.hpp internally dispatches */
\
/* to parse_cli. No need to replicate that wrapping code here. */
\
if (auto res = caf::detail::parse_impl<type>(nullptr, str)) { \
type expected_res{__VA_ARGS__}; \
if (auto unboxed = ::caf::get_if<type>(std::addressof(*res))) { \
if (*unboxed == expected_res) \
CAF_CHECK_PASSED("parse(" << str << ") == " << expected_res); \
else \
CAF_CHECK_FAILED(*unboxed << " != " << expected_res); \
} else { \
CAF_CHECK_FAILED(*res << " != " << expected_res); \
} \
} else { \
CAF_CHECK_FAILED("parse(" << str << ") -> " << res.error()); \
} \
} while (false)
#define CHECK_CLI_PARSE_FAILS(type, str) \
do { \
if (auto res = caf::detail::parse_impl<type>(nullptr, str)) { \
CAF_CHECK_FAILED("unexpected parser result: " << *res); \
} else { \
CAF_CHECK_PASSED("parse(" << str << ") == " << res.error()); \
} \
} while (false)
CAF_TEST
(
parsing
via
parse_cli
enables
shortcut
syntax
for
some
types
)
{
using
ls
=
std
::
vector
<
string
>
;
// List-of-strings.
using
li
=
std
::
vector
<
int
>
;
// List-of-integers.
using
lli
=
std
::
vector
<
li
>
;
// List-of-list-of-integers.
CAF_MESSAGE
(
"lists can omit square brackets"
);
CHECK_CLI_PARSE
(
int
,
"123"
,
123
);
CHECK_CLI_PARSE
(
li
,
"[ 1,2 , 3 ,]"
,
1
,
2
,
3
);
CHECK_CLI_PARSE
(
li
,
"[ 1,2 , 3 ]"
,
1
,
2
,
3
);
CHECK_CLI_PARSE
(
li
,
" 1,2 , 3 ,"
,
1
,
2
,
3
);
CHECK_CLI_PARSE
(
li
,
" 1,2 , 3 "
,
1
,
2
,
3
);
CHECK_CLI_PARSE
(
li
,
" [ ] "
,
li
{});
CHECK_CLI_PARSE
(
li
,
" "
,
li
{});
CHECK_CLI_PARSE
(
li
,
""
,
li
{});
CHECK_CLI_PARSE
(
li
,
"[123]"
,
123
);
CHECK_CLI_PARSE
(
li
,
"123"
,
123
);
CAF_MESSAGE
(
"brackets must have matching opening/closing brackets"
);
CHECK_CLI_PARSE_FAILS
(
li
,
" 1,2 , 3 ,]"
);
CHECK_CLI_PARSE_FAILS
(
li
,
" 1,2 , 3 ]"
);
CHECK_CLI_PARSE_FAILS
(
li
,
"123]"
);
CHECK_CLI_PARSE_FAILS
(
li
,
"[ 1,2 , 3 ,"
);
CHECK_CLI_PARSE_FAILS
(
li
,
"[ 1,2 , 3 "
);
CHECK_CLI_PARSE_FAILS
(
li
,
"[123"
);
CAF_MESSAGE
(
"string lists can omit quotation marks"
);
CHECK_CLI_PARSE
(
string
,
R"_("123")_"
,
"123"
);
CHECK_CLI_PARSE
(
string
,
R"_(123)_"
,
"123"
);
CHECK_CLI_PARSE
(
ls
,
R"_([ "1 ","2" , "3" ,])_"
,
"1 "
,
"2"
,
"3"
);
CHECK_CLI_PARSE
(
ls
,
R"_([ 1,2 , 3 ,])_"
,
"1"
,
"2"
,
"3"
);
CHECK_CLI_PARSE
(
ls
,
R"_([ 1,2 , 3 ])_"
,
"1"
,
"2"
,
"3"
);
CHECK_CLI_PARSE
(
ls
,
R"_( 1,2 , 3 ,)_"
,
"1"
,
"2"
,
"3"
);
CHECK_CLI_PARSE
(
ls
,
R"_( 1,2 , 3 )_"
,
"1"
,
"2"
,
"3"
);
CHECK_CLI_PARSE
(
ls
,
R"_( [ ] )_"
,
ls
{});
CHECK_CLI_PARSE
(
ls
,
R"_( )_"
,
ls
{});
CHECK_CLI_PARSE
(
ls
,
R"_(["abc"])_"
,
"abc"
);
CHECK_CLI_PARSE
(
ls
,
R"_([abc])_"
,
"abc"
);
CHECK_CLI_PARSE
(
ls
,
R"_("abc")_"
,
"abc"
);
CHECK_CLI_PARSE
(
ls
,
R"_(abc)_"
,
"abc"
);
CAF_MESSAGE
(
"nested lists can omit the outer square brackets"
);
CHECK_CLI_PARSE
(
lli
,
"[[1, 2, 3, ], ]"
,
li
({
1
,
2
,
3
}));
CHECK_CLI_PARSE
(
lli
,
"[[1, 2, 3]]"
,
li
({
1
,
2
,
3
}));
CHECK_CLI_PARSE
(
lli
,
"[1, 2, 3, ]"
,
li
({
1
,
2
,
3
}));
CHECK_CLI_PARSE
(
lli
,
"[1, 2, 3]"
,
li
({
1
,
2
,
3
}));
CHECK_CLI_PARSE
(
lli
,
"[[1], [2]]"
,
li
({
1
}),
li
({
2
}));
CHECK_CLI_PARSE
(
lli
,
"[1], [2]"
,
li
({
1
}),
li
({
2
}));
CHECK_CLI_PARSE_FAILS
(
lli
,
"1"
);
CHECK_CLI_PARSE_FAILS
(
lli
,
"1, 2"
);
CHECK_CLI_PARSE_FAILS
(
lli
,
"[1, 2]]"
);
CHECK_CLI_PARSE_FAILS
(
lli
,
"[[1, 2]"
);
}
CAF_TEST
(
unsuccessful
parsing
)
{
auto
parse
=
[](
const
string
&
str
)
{
auto
x
=
config_value
::
parse
(
str
);
...
...
libcaf_core/test/make_config_value_field.cpp
View file @
2ee9e994
...
...
@@ -139,7 +139,7 @@ struct fixture {
CAF_CHECK
(
!
foo_field
.
set
(
x
,
config_value
(
-
1
)));
string_view
input
=
"123"
;
string_parser_state
ps
{
input
.
begin
(),
input
.
end
()};
foo_field
.
parse_cli
(
ps
,
x
);
foo_field
.
parse_cli
(
ps
,
x
,
false
);
CAF_CHECK_EQUAL
(
ps
.
code
,
pec
::
success
);
CAF_CHECK_EQUAL
(
foo_field
.
get
(
x
),
config_value
(
123
));
}
...
...
libcaf_test/caf/test/unit_test.hpp
View file @
2ee9e994
...
...
@@ -535,6 +535,28 @@ using caf_test_case_auto_fixture = caf::test::dummy_fixture;
::caf::test::engine::last_check_line(__LINE__); \
} while (false)
#define CAF_CHECK_PASSED(msg) \
do { \
auto out = ::caf::test::logger::instance().massive(); \
out << term::green << "** " << term::blue << __FILE__ << term::yellow \
<< ":" << term::blue << __LINE__ \
<< ::caf::test::detail::fill(__LINE__) << term::reset << msg << '\n'; \
::caf::test::engine::current_test()->pass(); \
::caf::test::engine::last_check_file(__FILE__); \
::caf::test::engine::last_check_line(__LINE__); \
} while (false)
#define CAF_CHECK_FAILED(msg) \
do { \
auto out = ::caf::test::logger::instance().massive(); \
out << term::red << "!! " << term::blue << __FILE__ << term::yellow << ":" \
<< term::blue << __LINE__ << ::caf::test::detail::fill(__LINE__) \
<< term::reset << msg << '\n'; \
::caf::test::engine::current_test()->fail(false); \
::caf::test::engine::last_check_file(__FILE__); \
::caf::test::engine::last_check_line(__LINE__); \
} while (false)
#define CAF_CHECK(...) \
do { \
static_cast<void>(::caf::test::detail::check( \
...
...
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