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
d8035474
Unverified
Commit
d8035474
authored
Jun 26, 2020
by
Joseph Noir
Committed by
GitHub
Jun 26, 2020
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1115
Switch to new, cleaner config syntax
parents
c5f6c835
817c6e01
Changes
27
Hide whitespace changes
Inline
Side-by-side
Showing
27 changed files
with
1303 additions
and
182 deletions
+1303
-182
CHANGELOG.md
CHANGELOG.md
+11
-0
examples/caf-application.conf
examples/caf-application.conf
+86
-0
examples/caf-application.ini
examples/caf-application.ini
+0
-80
libcaf_core/CMakeLists.txt
libcaf_core/CMakeLists.txt
+3
-0
libcaf_core/caf/actor_system_config.hpp
libcaf_core/caf/actor_system_config.hpp
+18
-14
libcaf_core/caf/detail/config_consumer.hpp
libcaf_core/caf/detail/config_consumer.hpp
+153
-0
libcaf_core/caf/detail/parser/chars.hpp
libcaf_core/caf/detail/parser/chars.hpp
+2
-0
libcaf_core/caf/detail/parser/fsm.hpp
libcaf_core/caf/detail/parser/fsm.hpp
+17
-4
libcaf_core/caf/detail/parser/fsm_undef.hpp
libcaf_core/caf/detail/parser/fsm_undef.hpp
+2
-0
libcaf_core/caf/detail/parser/read_config.hpp
libcaf_core/caf/detail/parser/read_config.hpp
+249
-0
libcaf_core/caf/detail/parser/read_ini.hpp
libcaf_core/caf/detail/parser/read_ini.hpp
+1
-1
libcaf_core/caf/detail/parser/read_string.hpp
libcaf_core/caf/detail/parser/read_string.hpp
+28
-6
libcaf_core/caf/pec.hpp
libcaf_core/caf/pec.hpp
+3
-0
libcaf_core/caf/string_view.hpp
libcaf_core/caf/string_view.hpp
+5
-0
libcaf_core/src/actor_system_config.cpp
libcaf_core/src/actor_system_config.cpp
+61
-22
libcaf_core/src/config_option_set.cpp
libcaf_core/src/config_option_set.cpp
+1
-1
libcaf_core/src/config_value.cpp
libcaf_core/src/config_value.cpp
+4
-4
libcaf_core/src/detail/config_consumer.cpp
libcaf_core/src/detail/config_consumer.cpp
+201
-0
libcaf_core/src/detail/parser/chars.cpp
libcaf_core/src/detail/parser/chars.cpp
+2
-0
libcaf_core/src/pec_strings.cpp
libcaf_core/src/pec_strings.cpp
+2
-0
libcaf_core/test/actor_system_config.cpp
libcaf_core/test/actor_system_config.cpp
+5
-5
libcaf_core/test/detail/config_consumer.cpp
libcaf_core/test/detail/config_consumer.cpp
+112
-0
libcaf_core/test/detail/parser/read_config.cpp
libcaf_core/test/detail/parser/read_config.cpp
+237
-0
libcaf_core/test/detail/parser/read_ini.cpp
libcaf_core/test/detail/parser/read_ini.cpp
+0
-8
libcaf_core/test/detail/parser/read_string.cpp
libcaf_core/test/detail/parser/read_string.cpp
+22
-5
libcaf_core/test/settings.cpp
libcaf_core/test/settings.cpp
+16
-0
manual/ConfiguringActorApplications.rst
manual/ConfiguringActorApplications.rst
+62
-32
No files found.
CHANGELOG.md
View file @
d8035474
...
@@ -50,6 +50,14 @@ is based on [Keep a Changelog](https://keepachangelog.com).
...
@@ -50,6 +50,14 @@ is based on [Keep a Changelog](https://keepachangelog.com).
-
The
`to_string`
output for
`error`
now renders the error code enum by default.
-
The
`to_string`
output for
`error`
now renders the error code enum by default.
This renders the member functions
`actor_system::render`
and
This renders the member functions
`actor_system::render`
and
`actor_system_config::render`
obsolete.
`actor_system_config::render`
obsolete.
-
The configuration format of CAF has come a long way since first starting to
allow user-defined configuration via
`.ini`
files. Rather than sticking with
the weird hybrid that evolved over the years, we are going to get rid of the
last pieces of INI syntax and go with the much cleaner, scoped syntax. CAF is
still picking up
`caf-application.ini`
with this release and uses the
deprecated INI parser when given any other file name that ends in
`.ini`
.
However, the next version of CAF is only going to recognize the new format.
The new default file name is
`caf-application.conf`
.
### Changed
### Changed
...
@@ -112,6 +120,9 @@ is based on [Keep a Changelog](https://keepachangelog.com).
...
@@ -112,6 +120,9 @@ is based on [Keep a Changelog](https://keepachangelog.com).
example:
`typed_actor<result<double>(double)>`
. We have reimplemented the
example:
`typed_actor<result<double>(double)>`
. We have reimplemented the
metaprogramming facilities
`racts_to<...>`
and
`replies_to<...>::with<...>`
metaprogramming facilities
`racts_to<...>`
and
`replies_to<...>::with<...>`
as an alternative way of writing the function signature.
as an alternative way of writing the function signature.
-
All parsing functions in
`actor_system_config`
that take an input stream
exclusively use the new configuration syntax (please consult the manual for
details and examples for the configuration syntax).
### Removed
### Removed
...
...
examples/caf-application.conf
0 → 100644
View file @
d8035474
# This file shows all possible parameters with defaults. For some values, CAF
# computes a value at runtime if the configuration does not provide a value. For
# example, "scheduler.max-threads" has no hard-coded default and instead adjusts
# to the number of cores available.
# when using the default scheduler
scheduler
{
# accepted alternative: "sharing"
policy
=
"stealing"
# configures whether the scheduler generates profiling output
enable
-
profiling
=
false
# forces a fixed number of threads if set
# max-threads=... (detected at runtime)
# maximum number of messages actors can consume in one run (int64 max)
max
-
throughput
=
9223372036854775807
# measurement resolution in milliseconds (only if profiling is enabled)
profiling
-
resolution
=
100
ms
# output file for profiler data (only if profiling is enabled)
profiling
-
output
-
file
=
"/dev/null"
}
# when using "stealing" as scheduler policy
work
-
stealing
{
# number of zero-sleep-interval polling attempts
aggressive
-
poll
-
attempts
=
100
# frequency of steal attempts during aggressive polling
aggressive
-
steal
-
interval
=
10
# number of moderately aggressive polling attempts
moderate
-
poll
-
attempts
=
500
# frequency of steal attempts during moderate polling
moderate
-
steal
-
interval
=
5
# sleep interval between poll attempts
moderate
-
sleep
-
duration
=
50
us
# frequency of steal attempts during relaxed polling
relaxed
-
steal
-
interval
=
1
# sleep interval between poll attempts
relaxed
-
sleep
-
duration
=
10
ms
}
# when loading io::middleman
middleman
{
# configures whether MMs try to span a full mesh
enable
-
automatic
-
connections
=
false
# application identifier of this node, prevents connection to other CAF
# instances with different identifier
app
-
identifier
=
""
# maximum number of consecutive I/O reads per broker
max
-
consecutive
-
reads
=
50
# heartbeat message interval in ms (0 disables heartbeating)
heartbeat
-
interval
=
0
ms
# configures whether the MM attaches its internal utility actors to the
# scheduler instead of dedicating individual threads (needed only for
# deterministic testing)
attach
-
utility
-
actors
=
false
# configures whether the MM starts a background thread for I/O activity,
# setting this to true allows fully deterministic execution in unit test and
# requires the user to trigger I/O manually
manual
-
multiplexing
=
false
# disables communication via TCP
disable
-
tcp
=
false
# enable communication via UDP
enable
-
udp
=
false
# configures how many background workers are spawned for deserialization,
# by default CAF uses 1-4 workers depending on the number of cores
# workers=... (detected at runtime)
}
# when compiling with logging enabled
logger
{
# file name template for output log file files (empty string disables logging)
file
-
name
=
"actor_log_[PID]_[TIMESTAMP]_[NODE].log"
# format for rendering individual log file entries
file
-
format
=
"%r %c %p %a %t %C %M %F:%L %m%n"
# configures the minimum severity of messages that are written to the log file
# (quiet|error|warning|info|debug|trace)
file
-
verbosity
=
"trace"
# mode for console log output generation (none|colored|uncolored)
console
=
"none"
# format for printing individual log entries to the console
console
-
format
=
"%m"
# configures the minimum severity of messages that are written to the console
# (quiet|error|warning|info|debug|trace)
console
-
verbosity
=
"trace"
# excludes listed components from logging (list of strings)
component
-
blacklist
=[]
}
examples/caf-application.ini
deleted
100644 → 0
View file @
c5f6c835
; This file shows all possible parameters with defaults.
; Values enclosed in <> are detected at runtime unless defined by the user.
; when using the default scheduler
[scheduler]
; accepted alternative: 'sharing'
policy
=
'stealing'
; configures whether the scheduler generates profiling output
enable-profiling
=
false
; forces a fixed number of threads if set
max-threads
=
<number of cores>
; maximum number of messages actors can consume in one run
max-throughput
=
<infinite>
; measurement resolution in milliseconds (only if profiling is enabled)
profiling-resolution
=
100ms
; output file for profiler data (only if profiling is enabled)
profiling-output-file
=
"/dev/null"
; when using 'stealing' as scheduler policy
[work-stealing]
; number of zero-sleep-interval polling attempts
aggressive-poll-attempts
=
100
; frequency of steal attempts during aggressive polling
aggressive-steal-interval
=
10
; number of moderately aggressive polling attempts
moderate-poll-attempts
=
500
; frequency of steal attempts during moderate polling
moderate-steal-interval
=
5
; sleep interval between poll attempts
moderate-sleep-duration
=
50us
; frequency of steal attempts during relaxed polling
relaxed-steal-interval
=
1
; sleep interval between poll attempts
relaxed-sleep-duration
=
10ms
; when loading io::middleman
[middleman]
; configures whether MMs try to span a full mesh
enable-automatic-connections
=
false
; application identifier of this node, prevents connection to other CAF
; instances with different identifier
app-identifier
=
""
; maximum number of consecutive I/O reads per broker
max-consecutive-reads
=
50
; heartbeat message interval in ms (0 disables heartbeating)
heartbeat-interval
=
0ms
; configures whether the MM attaches its internal utility actors to the
; scheduler instead of dedicating individual threads (needed only for
; deterministic testing)
attach-utility-actors
=
false
; configures whether the MM starts a background thread for I/O activity,
; setting this to true allows fully deterministic execution in unit test and
; requires the user to trigger I/O manually
manual-multiplexing
=
false
; disables communication via TCP
disable-tcp
=
false
; enable communication via UDP
enable-udp
=
false
; configures how many background workers are spawned for deserialization,
; by default CAF uses 1-4 workers depending on the number of cores
workers
=
<min(3, number of cores / 4) + 1>
; when compiling with logging enabled
[logger]
; file name template for output log file files (empty string disables logging)
file-name
=
"actor_log_[PID]_[TIMESTAMP]_[NODE].log"
; format for rendering individual log file entries
file-format
=
"%r %c %p %a %t %C %M %F:%L %m%n"
; configures the minimum severity of messages that are written to the log file
; (quiet|error|warning|info|debug|trace)
file-verbosity
=
'trace'
; mode for console log output generation (none|colored|uncolored)
console
=
'none'
; format for printing individual log entries to the console
console-format
=
"%m"
; configures the minimum severity of messages that are written to the console
; (quiet|error|warning|info|debug|trace)
console-verbosity
=
'trace'
; excludes listed components from logging (list of atoms)
component-blacklist
=
[]
libcaf_core/CMakeLists.txt
View file @
d8035474
...
@@ -79,6 +79,7 @@ add_library(libcaf_core_obj OBJECT ${CAF_CORE_HEADERS}
...
@@ -79,6 +79,7 @@ add_library(libcaf_core_obj OBJECT ${CAF_CORE_HEADERS}
src/detail/behavior_impl.cpp
src/detail/behavior_impl.cpp
src/detail/behavior_stack.cpp
src/detail/behavior_stack.cpp
src/detail/blocking_behavior.cpp
src/detail/blocking_behavior.cpp
src/detail/config_consumer.cpp
src/detail/get_mac_addresses.cpp
src/detail/get_mac_addresses.cpp
src/detail/get_process_id.cpp
src/detail/get_process_id.cpp
src/detail/get_root_uuid.cpp
src/detail/get_root_uuid.cpp
...
@@ -227,12 +228,14 @@ caf_add_test_suites(caf-core-test
...
@@ -227,12 +228,14 @@ caf_add_test_suites(caf-core-test
deep_to_string
deep_to_string
detached_actors
detached_actors
detail.bounds_checker
detail.bounds_checker
detail.config_consumer
detail.ieee_754
detail.ieee_754
detail.ini_consumer
detail.ini_consumer
detail.limited_vector
detail.limited_vector
detail.meta_object
detail.meta_object
detail.parse
detail.parse
detail.parser.read_bool
detail.parser.read_bool
detail.parser.read_config
detail.parser.read_floating_point
detail.parser.read_floating_point
detail.parser.read_ini
detail.parser.read_ini
detail.parser.read_number
detail.parser.read_number
...
...
libcaf_core/caf/actor_system_config.hpp
View file @
d8035474
...
@@ -107,23 +107,23 @@ public:
...
@@ -107,23 +107,23 @@ public:
// -- modifiers --------------------------------------------------------------
// -- modifiers --------------------------------------------------------------
/// Parses `args` as tuple of strings containing CLI options and `
ini_stream`
/// Parses `args` as tuple of strings containing CLI options and `
config` as
///
as INI formatted input stream
.
///
configuration file
.
error
parse
(
string_list
args
,
std
::
istream
&
ini
);
error
parse
(
string_list
args
,
std
::
istream
&
config
);
/// Parses `args` as tuple of strings containing CLI options and tries to
/// Parses `args` as tuple of strings containing CLI options and tries to open
/// open `ini_file_cstr` as INI formatted config file. The parsers tries to
/// `config_file_cstr` as config file. The parsers tries to open
/// open `caf-application.ini` if `ini_file_cstr` is `nullptr`.
/// `caf-application.conf` or `caf-application.ini` (deprecated) if
error
parse
(
string_list
args
,
const
char
*
ini_file_cstr
=
nullptr
);
/// `config_file_cstr` is `nullptr`.
error
parse
(
string_list
args
,
const
char
*
config_file_cstr
=
nullptr
);
/// Parses the CLI options `{argc, argv}` and `ini_stream` as INI formatted
/// Parses the CLI options `{argc, argv}` and `config` as configuration file.
/// input stream.
error
parse
(
int
argc
,
char
**
argv
,
std
::
istream
&
config
);
error
parse
(
int
argc
,
char
**
argv
,
std
::
istream
&
ini
);
/// Parses the CLI options `{argc, argv}` and tries to open `
ini
_file_cstr`
/// Parses the CLI options `{argc, argv}` and tries to open `
config
_file_cstr`
/// as
INI formatted config file. The parsers tries to open
/// as
config file. The parsers tries to open `caf-application.conf` or
/// `caf-application.ini`
if `ini
_file_cstr` is `nullptr`.
/// `caf-application.ini`
(deprecated) if `config
_file_cstr` is `nullptr`.
error
parse
(
int
argc
,
char
**
argv
,
const
char
*
ini
_file_cstr
=
nullptr
);
error
parse
(
int
argc
,
char
**
argv
,
const
char
*
config
_file_cstr
=
nullptr
);
/// Allows other nodes to spawn actors created by `fun`
/// Allows other nodes to spawn actors created by `fun`
/// dynamically by using `name` as identifier.
/// dynamically by using `name` as identifier.
...
@@ -324,6 +324,10 @@ protected:
...
@@ -324,6 +324,10 @@ protected:
config_option_set
custom_options_
;
config_option_set
custom_options_
;
private:
private:
// TODO: deprecated. Remove with CAF 0.19.
static
error
parse_ini
(
std
::
istream
&
source
,
const
config_option_set
&
opts
,
settings
&
result
);
actor_system_config
&
set_impl
(
string_view
name
,
config_value
value
);
actor_system_config
&
set_impl
(
string_view
name
,
config_value
value
);
error
extract_config_file_path
(
string_list
&
args
);
error
extract_config_file_path
(
string_list
&
args
);
...
...
libcaf_core/caf/detail/config_consumer.hpp
0 → 100644
View file @
d8035474
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 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 <string>
#include "caf/config_option_set.hpp"
#include "caf/config_value.hpp"
#include "caf/detail/core_export.hpp"
#include "caf/dictionary.hpp"
#include "caf/settings.hpp"
namespace
caf
::
detail
{
class
config_consumer
;
class
config_value_consumer
;
/// Consumes a list of config values.
class
CAF_CORE_EXPORT
config_list_consumer
{
public:
// -- constructors, destructors, and assignment operators --------------------
config_list_consumer
(
const
config_option_set
*
options
,
config_consumer
*
parent
);
config_list_consumer
(
const
config_option_set
*
options
,
config_list_consumer
*
parent
);
explicit
config_list_consumer
(
config_value_consumer
*
parent
);
config_list_consumer
(
config_list_consumer
&&
)
=
default
;
config_list_consumer
&
operator
=
(
config_list_consumer
&&
)
=
default
;
// -- properties -------------------------------------------------------------
pec
end_list
();
config_list_consumer
begin_list
()
{
return
config_list_consumer
{
options_
,
this
};
}
config_consumer
begin_map
();
template
<
class
T
>
void
value
(
T
&&
x
)
{
xs_
.
emplace_back
(
std
::
forward
<
T
>
(
x
));
}
std
::
string
qualified_key
();
private:
// -- member variables -------------------------------------------------------
const
config_option_set
*
options_
=
nullptr
;
variant
<
config_consumer
*
,
config_list_consumer
*
,
config_value_consumer
*>
parent_
;
config_value
::
list
xs_
;
};
/// Consumes a series of key-value pairs from an application configuration.
class
CAF_CORE_EXPORT
config_consumer
{
public:
// -- constructors, destructors, and assignment operators --------------------
config_consumer
(
const
config_option_set
*
options
,
config_consumer
*
parent
);
config_consumer
(
const
config_option_set
*
options
,
config_list_consumer
*
parent
);
config_consumer
(
const
config_option_set
&
options
,
settings
&
cfg
);
explicit
config_consumer
(
settings
&
cfg
);
config_consumer
(
config_consumer
&&
other
);
config_consumer
&
operator
=
(
config_consumer
&&
other
);
~
config_consumer
();
// -- properties -------------------------------------------------------------
config_consumer
begin_map
()
{
return
config_consumer
{
options_
,
this
};
}
void
end_map
();
config_list_consumer
begin_list
()
{
return
config_list_consumer
{
options_
,
this
};
}
void
key
(
std
::
string
name
)
{
current_key_
=
std
::
move
(
name
);
}
template
<
class
T
>
pec
value
(
T
&&
x
)
{
return
value_impl
(
config_value
{
std
::
forward
<
T
>
(
x
)});
}
const
std
::
string
&
current_key
()
{
return
current_key_
;
}
std
::
string
qualified_key
();
private:
void
destroy
();
pec
value_impl
(
config_value
&&
x
);
// -- member variables -------------------------------------------------------
const
config_option_set
*
options_
=
nullptr
;
variant
<
none_t
,
config_consumer
*
,
config_list_consumer
*>
parent_
;
settings
*
cfg_
=
nullptr
;
std
::
string
current_key_
;
std
::
string
category_
;
};
/// Consumes a single configuration value.
class
CAF_CORE_EXPORT
config_value_consumer
{
public:
config_value
result
;
template
<
class
T
>
void
value
(
T
&&
x
)
{
result
=
config_value
{
std
::
forward
<
T
>
(
x
)};
}
config_list_consumer
begin_list
();
config_consumer
begin_map
();
};
}
// namespace caf::detail
libcaf_core/caf/detail/parser/chars.hpp
View file @
d8035474
...
@@ -59,4 +59,6 @@ CAF_CORE_EXPORT extern const char decimal_chars[11];
...
@@ -59,4 +59,6 @@ CAF_CORE_EXPORT extern const char decimal_chars[11];
CAF_CORE_EXPORT
extern
const
char
octal_chars
[
9
];
CAF_CORE_EXPORT
extern
const
char
octal_chars
[
9
];
CAF_CORE_EXPORT
extern
const
char
quote_marks
[
3
];
}
// namespace caf::detail::parser
}
// namespace caf::detail::parser
libcaf_core/caf/detail/parser/fsm.hpp
View file @
d8035474
...
@@ -20,8 +20,21 @@
...
@@ -20,8 +20,21 @@
// DSL is supposed to clean up all defines made in this header via
// DSL is supposed to clean up all defines made in this header via
// `include "caf/detail/parser/fsm_undef.hpp"` at the end.
// `include "caf/detail/parser/fsm_undef.hpp"` at the end.
#include <type_traits>
#include "caf/detail/pp.hpp"
#include "caf/detail/pp.hpp"
#define CAF_FSM_EVAL_ACTION(action) \
auto action_impl = [&]() -> decltype(auto) { return action; }; \
if constexpr (std::is_same<pec, decltype(action_impl())>::value) { \
if (auto code = action_impl(); code != pec::success) { \
ps.code = code; \
return; \
} \
} else { \
action_impl(); \
}
#define CAF_FSM_EVAL_MISMATCH_EC \
#define CAF_FSM_EVAL_MISMATCH_EC \
if (mismatch_ec == caf::pec::unexpected_character) \
if (mismatch_ec == caf::pec::unexpected_character) \
ps.code = ch != '\n' ? mismatch_ec : caf::pec::unexpected_newline; \
ps.code = ch != '\n' ? mismatch_ec : caf::pec::unexpected_newline; \
...
@@ -107,7 +120,7 @@
...
@@ -107,7 +120,7 @@
#define CAF_TRANSITION_IMPL3(target, whitelist, action) \
#define CAF_TRANSITION_IMPL3(target, whitelist, action) \
if (::caf::detail::parser::in_whitelist(whitelist, ch)) { \
if (::caf::detail::parser::in_whitelist(whitelist, ch)) { \
action;
\
CAF_FSM_EVAL_ACTION(action)
\
CAF_TRANSITION_IMPL1(target) \
CAF_TRANSITION_IMPL1(target) \
}
}
...
@@ -139,7 +152,7 @@
...
@@ -139,7 +152,7 @@
#define CAF_EPSILON_IMPL3(target, whitelist, action) \
#define CAF_EPSILON_IMPL3(target, whitelist, action) \
if (::caf::detail::parser::in_whitelist(whitelist, ch)) { \
if (::caf::detail::parser::in_whitelist(whitelist, ch)) { \
action;
\
CAF_FSM_EVAL_ACTION(action)
\
CAF_EPSILON_IMPL1(target) \
CAF_EPSILON_IMPL1(target) \
}
}
...
@@ -167,7 +180,7 @@
...
@@ -167,7 +180,7 @@
#define CAF_FSM_TRANSITION_IMPL4(fsm_call, target, whitelist, action) \
#define CAF_FSM_TRANSITION_IMPL4(fsm_call, target, whitelist, action) \
if (::caf::detail::parser::in_whitelist(whitelist, ch)) { \
if (::caf::detail::parser::in_whitelist(whitelist, ch)) { \
action;
\
CAF_FSM_EVAL_ACTION(action)
\
CAF_FSM_TRANSITION_IMPL2(fsm_call, target) \
CAF_FSM_TRANSITION_IMPL2(fsm_call, target) \
}
}
...
@@ -195,7 +208,7 @@
...
@@ -195,7 +208,7 @@
#define CAF_FSM_EPSILON_IMPL4(fsm_call, target, whitelist, action) \
#define CAF_FSM_EPSILON_IMPL4(fsm_call, target, whitelist, action) \
if (::caf::detail::parser::in_whitelist(whitelist, ch)) { \
if (::caf::detail::parser::in_whitelist(whitelist, ch)) { \
action;
\
CAF_FSM_EVAL_ACTION(action)
\
CAF_FSM_EPSILON_IMPL2(fsm_call, target) \
CAF_FSM_EPSILON_IMPL2(fsm_call, target) \
}
}
...
...
libcaf_core/caf/detail/parser/fsm_undef.hpp
View file @
d8035474
...
@@ -18,6 +18,8 @@
...
@@ -18,6 +18,8 @@
// This header intentionally has no `#pragma once`. See fsm.hpp.
// This header intentionally has no `#pragma once`. See fsm.hpp.
#undef CAF_FSM_EVAL_ACTION
#undef CAF_FSM_EVAL_MISMATCH_EC
#undef CAF_FSM_EVAL_MISMATCH_EC
#undef start
#undef start
...
...
libcaf_core/caf/detail/parser/read_config.hpp
0 → 100644
View file @
d8035474
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 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 <ctype.h>
#include <stack>
#include "caf/config.hpp"
#include "caf/detail/parser/chars.hpp"
#include "caf/detail/parser/read_bool.hpp"
#include "caf/detail/parser/read_number_or_timespan.hpp"
#include "caf/detail/parser/read_string.hpp"
#include "caf/detail/parser/read_uri.hpp"
#include "caf/detail/scope_guard.hpp"
#include "caf/pec.hpp"
#include "caf/uri_builder.hpp"
CAF_PUSH_UNUSED_LABEL_WARNING
#include "caf/detail/parser/fsm.hpp"
namespace
caf
::
detail
::
parser
{
// Example input:
//
// section1 {
// value1 = 123
// value2 = "string"
// subsection1 = {
// value3 = 1.23
// value4 = 4e20
// }
// }
// section2 {
// value5 = 'atom'
// value6 = [1, 'two', "three", {
// a = "b",
// b = "c",
// }]
// }
//
template
<
class
State
,
class
Consumer
>
void
read_config_comment
(
State
&
ps
,
Consumer
&&
)
{
// clang-format off
start
();
term_state
(
init
)
{
transition
(
done
,
'\n'
)
transition
(
init
)
}
term_state
(
done
)
{
// nop
}
fin
();
// clang-format on
}
template
<
class
State
,
class
Consumer
,
class
InsideList
=
std
::
false_type
>
void
read_config_value
(
State
&
ps
,
Consumer
&&
consumer
,
InsideList
inside_list
=
{});
template
<
class
State
,
class
Consumer
>
void
read_config_list
(
State
&
ps
,
Consumer
&&
consumer
)
{
// clang-format off
start
();
state
(
init
)
{
epsilon
(
before_value
)
}
state
(
before_value
)
{
transition
(
before_value
,
"
\t\n
"
)
transition
(
done
,
']'
,
consumer
.
end_list
())
fsm_epsilon
(
read_config_comment
(
ps
,
consumer
),
before_value
,
'#'
)
fsm_epsilon
(
read_config_value
(
ps
,
consumer
,
std
::
true_type
{}),
after_value
)
}
state
(
after_value
)
{
transition
(
after_value
,
"
\t\n
"
)
transition
(
before_value
,
','
)
transition
(
done
,
']'
,
consumer
.
end_list
())
fsm_epsilon
(
read_config_comment
(
ps
,
consumer
),
after_value
,
'#'
)
}
term_state
(
done
)
{
// nop
}
fin
();
// clang-format on
}
template
<
bool
Nested
=
true
,
class
State
,
class
Consumer
>
void
read_config_map
(
State
&
ps
,
Consumer
&&
consumer
)
{
std
::
string
key
;
auto
alnum_or_dash
=
[](
char
x
)
{
return
isalnum
(
x
)
||
x
==
'-'
||
x
==
'_'
;
};
// clang-format off
start
();
term_state
(
init
)
{
epsilon
(
await_key_name
)
}
state
(
await_key_name
)
{
transition
(
await_key_name
,
"
\t\n
"
)
fsm_epsilon
(
read_config_comment
(
ps
,
consumer
),
await_key_name
,
'#'
)
fsm_epsilon
(
read_string
(
ps
,
key
),
await_assignment
,
quote_marks
)
transition
(
read_key_name
,
alnum_or_dash
,
key
=
ch
)
transition_if
(
Nested
,
done
,
'}'
,
consumer
.
end_map
())
epsilon_if
(
!
Nested
,
done
)
}
// Reads a key of a "key=value" line.
state
(
read_key_name
)
{
transition
(
read_key_name
,
alnum_or_dash
,
key
+=
ch
)
epsilon
(
await_assignment
)
}
// Reads the assignment operator in a "key=value" line.
state
(
await_assignment
)
{
transition
(
await_assignment
,
"
\t
"
)
transition
(
await_value
,
"=:"
,
consumer
.
key
(
std
::
move
(
key
)))
epsilon
(
await_value
,
'{'
,
consumer
.
key
(
std
::
move
(
key
)))
}
// Reads the value in a "key=value" line.
state
(
await_value
)
{
transition
(
await_value
,
"
\t
"
)
fsm_epsilon
(
read_config_value
(
ps
,
consumer
),
after_value
)
}
// Waits for end-of-line after reading a value
unstable_state
(
after_value
)
{
transition
(
after_value
,
"
\t
"
)
transition
(
had_newline
,
"
\n
"
)
transition
(
await_key_name
,
','
)
transition_if
(
Nested
,
done
,
'}'
,
consumer
.
end_map
())
fsm_epsilon
(
read_config_comment
(
ps
,
consumer
),
had_newline
,
'#'
)
epsilon_if
(
!
Nested
,
done
)
epsilon
(
unexpected_end_of_input
)
}
// Allows users to skip the ',' for separating key/value pairs
unstable_state
(
had_newline
)
{
transition
(
had_newline
,
"
\t\n
"
)
transition
(
await_key_name
,
','
)
transition_if
(
Nested
,
done
,
'}'
,
consumer
.
end_map
())
fsm_epsilon
(
read_config_comment
(
ps
,
consumer
),
had_newline
,
'#'
)
fsm_epsilon
(
read_string
(
ps
,
key
),
await_assignment
,
quote_marks
)
epsilon
(
read_key_name
,
alnum_or_dash
)
epsilon_if
(
!
Nested
,
done
)
epsilon
(
unexpected_end_of_input
)
}
state
(
unexpected_end_of_input
)
{
// no transitions, only needed for the unstable states
}
term_state
(
done
)
{
//nop
}
fin
();
// clang-format on
}
template
<
class
State
,
class
Consumer
>
void
read_config_uri
(
State
&
ps
,
Consumer
&&
consumer
)
{
uri_builder
builder
;
auto
g
=
make_scope_guard
([
&
]
{
if
(
ps
.
code
<=
pec
::
trailing_character
)
consumer
.
value
(
builder
.
make
());
});
// clang-format off
start
();
state
(
init
)
{
transition
(
init
,
"
\t\n
"
)
transition
(
before_uri
,
'<'
)
}
state
(
before_uri
)
{
transition
(
before_uri
,
"
\t\n
"
)
fsm_epsilon
(
read_uri
(
ps
,
builder
),
after_uri
)
}
state
(
after_uri
)
{
transition
(
after_uri
,
"
\t\n
"
)
transition
(
done
,
'>'
)
}
term_state
(
done
)
{
// nop
}
fin
();
// clang-format on
}
template
<
class
State
,
class
Consumer
,
class
InsideList
>
void
read_config_value
(
State
&
ps
,
Consumer
&&
consumer
,
InsideList
inside_list
)
{
// clang-format off
start
();
state
(
init
)
{
fsm_epsilon
(
read_string
(
ps
,
consumer
),
done
,
quote_marks
)
fsm_epsilon
(
read_number
(
ps
,
consumer
),
done
,
'.'
)
fsm_epsilon
(
read_bool
(
ps
,
consumer
),
done
,
"ft"
)
fsm_epsilon
(
read_number_or_timespan
(
ps
,
consumer
,
inside_list
),
done
,
"0123456789+-"
)
fsm_epsilon
(
read_config_uri
(
ps
,
consumer
),
done
,
'<'
)
fsm_transition
(
read_config_list
(
ps
,
consumer
.
begin_list
()),
done
,
'['
)
fsm_transition
(
read_config_map
(
ps
,
consumer
.
begin_map
()),
done
,
'{'
)
}
term_state
(
done
)
{
// nop
}
fin
();
// clang-format on
}
template
<
class
State
,
class
Consumer
>
void
read_config
(
State
&
ps
,
Consumer
&&
consumer
)
{
// clang-format off
start
();
// Checks whether there's a top-level '{'.
term_state
(
init
)
{
transition
(
init
,
"
\t\n
"
)
fsm_epsilon
(
read_config_comment
(
ps
,
consumer
),
init
,
'#'
)
fsm_transition
(
read_config_map
<
false
>
(
ps
,
consumer
),
await_closing_brance
,
'{'
)
fsm_epsilon
(
read_config_map
<
false
>
(
ps
,
consumer
),
init
)
}
state
(
await_closing_brance
)
{
transition
(
await_closing_brance
,
"
\t\n
"
)
fsm_epsilon
(
read_config_comment
(
ps
,
consumer
),
await_closing_brance
,
'#'
)
transition
(
done
,
'}'
)
}
term_state
(
done
)
{
transition
(
done
,
"
\t\n
"
)
fsm_epsilon
(
read_config_comment
(
ps
,
consumer
),
done
,
'#'
)
}
fin
();
// clang-format on
}
}
// namespace caf::detail::parser
#include "caf/detail/parser/fsm_undef.hpp"
CAF_POP_WARNINGS
libcaf_core/caf/detail/parser/read_ini.hpp
View file @
d8035474
...
@@ -188,7 +188,7 @@ void read_ini_value(State& ps, Consumer&& consumer, InsideList inside_list) {
...
@@ -188,7 +188,7 @@ void read_ini_value(State& ps, Consumer&& consumer, InsideList inside_list) {
// clang-format off
// clang-format off
start
();
start
();
state
(
init
)
{
state
(
init
)
{
fsm_epsilon
(
read_string
(
ps
,
consumer
),
done
,
'"'
)
fsm_epsilon
(
read_string
(
ps
,
consumer
),
done
,
quote_marks
)
fsm_epsilon
(
read_number
(
ps
,
consumer
),
done
,
'.'
)
fsm_epsilon
(
read_number
(
ps
,
consumer
),
done
,
'.'
)
fsm_epsilon
(
read_bool
(
ps
,
consumer
),
done
,
"ft"
)
fsm_epsilon
(
read_bool
(
ps
,
consumer
),
done
,
"ft"
)
fsm_epsilon
(
read_number_or_timespan
(
ps
,
consumer
,
inside_list
),
fsm_epsilon
(
read_number_or_timespan
(
ps
,
consumer
,
inside_list
),
...
...
libcaf_core/caf/detail/parser/read_string.hpp
View file @
d8035474
...
@@ -36,21 +36,40 @@ namespace caf::detail::parser {
...
@@ -36,21 +36,40 @@ namespace caf::detail::parser {
/// unquoted strings may only include alphanumeric characters.
/// unquoted strings may only include alphanumeric characters.
template
<
class
State
,
class
Consumer
>
template
<
class
State
,
class
Consumer
>
void
read_string
(
State
&
ps
,
Consumer
&&
consumer
)
{
void
read_string
(
State
&
ps
,
Consumer
&&
consumer
)
{
std
::
string
res
;
// Allow Consumer to be a string&, in which case we simply store the result
// directly in the reference.
using
res_type
=
std
::
conditional_t
<
std
::
is_same
<
Consumer
,
std
::
string
&>::
value
,
std
::
string
&
,
std
::
string
>
;
auto
init_res
=
[](
auto
&
c
)
->
res_type
{
if
constexpr
(
std
::
is_same
<
res_type
,
std
::
string
&>::
value
)
{
c
.
clear
();
return
c
;
}
else
{
return
std
::
string
{};
}
};
res_type
res
=
init_res
(
consumer
);
auto
g
=
caf
::
detail
::
make_scope_guard
([
&
]
{
auto
g
=
caf
::
detail
::
make_scope_guard
([
&
]
{
if
(
ps
.
code
<=
pec
::
trailing_character
)
if
constexpr
(
std
::
is_same
<
res_type
,
std
::
string
>::
value
)
consumer
.
value
(
std
::
move
(
res
));
if
(
ps
.
code
<=
pec
::
trailing_character
)
consumer
.
value
(
std
::
move
(
res
));
});
});
static
constexpr
char
single_quote
=
'\''
;
static
constexpr
char
double_quote
=
'"'
;
char
quote_mark
=
'\0'
;
// clang-format off
// clang-format off
start
();
start
();
state
(
init
)
{
state
(
init
)
{
transition
(
init
,
"
\t
"
)
transition
(
init
,
"
\t
"
)
transition
(
read_chars
,
'"'
)
transition
(
read_chars
,
double_quote
,
quote_mark
=
double_quote
)
transition
(
read_chars
,
single_quote
,
quote_mark
=
single_quote
)
transition
(
read_unquoted_chars
,
alphanumeric_chars
,
res
+=
ch
)
transition
(
read_unquoted_chars
,
alphanumeric_chars
,
res
+=
ch
)
}
}
state
(
read_chars
)
{
state
(
read_chars
)
{
transition
(
escape
,
'\\'
)
transition
(
escape
,
'\\'
)
transition
(
done
,
'"'
)
transition_if
(
quote_mark
==
double_quote
,
done
,
double_quote
)
transition_if
(
quote_mark
==
single_quote
,
done
,
single_quote
)
error_transition
(
pec
::
unexpected_newline
,
'\n'
)
error_transition
(
pec
::
unexpected_newline
,
'\n'
)
transition
(
read_chars
,
any_char
,
res
+=
ch
)
transition
(
read_chars
,
any_char
,
res
+=
ch
)
}
}
...
@@ -59,7 +78,10 @@ void read_string(State& ps, Consumer&& consumer) {
...
@@ -59,7 +78,10 @@ void read_string(State& ps, Consumer&& consumer) {
transition
(
read_chars
,
'r'
,
res
+=
'\r'
)
transition
(
read_chars
,
'r'
,
res
+=
'\r'
)
transition
(
read_chars
,
't'
,
res
+=
'\t'
)
transition
(
read_chars
,
't'
,
res
+=
'\t'
)
transition
(
read_chars
,
'\\'
,
res
+=
'\\'
)
transition
(
read_chars
,
'\\'
,
res
+=
'\\'
)
transition
(
read_chars
,
'"'
,
res
+=
'"'
)
transition_if
(
quote_mark
==
double_quote
,
read_chars
,
double_quote
,
res
+=
double_quote
)
transition_if
(
quote_mark
==
single_quote
,
read_chars
,
single_quote
,
res
+=
single_quote
)
error_transition
(
pec
::
invalid_escape_sequence
)
error_transition
(
pec
::
invalid_escape_sequence
)
}
}
term_state
(
read_unquoted_chars
)
{
term_state
(
read_unquoted_chars
)
{
...
...
libcaf_core/caf/pec.hpp
View file @
d8035474
...
@@ -75,6 +75,9 @@ enum class pec : uint8_t {
...
@@ -75,6 +75,9 @@ enum class pec : uint8_t {
missing_field
=
20
,
missing_field
=
20
,
/// Parsing a range statement ('n..m' or 'n..m..step') failed.
/// Parsing a range statement ('n..m' or 'n..m..step') failed.
invalid_range_expression
,
invalid_range_expression
,
/// Stopped after running into an invalid parser state. Should never happen
/// and most likely indicates a bug in the implementation.
invalid_state
,
};
};
CAF_CORE_EXPORT
std
::
string
to_string
(
pec
);
CAF_CORE_EXPORT
std
::
string
to_string
(
pec
);
...
...
libcaf_core/caf/string_view.hpp
View file @
d8035474
...
@@ -97,6 +97,11 @@ public:
...
@@ -97,6 +97,11 @@ public:
// nop
// nop
}
}
constexpr
string_view
(
iterator
first
,
iterator
last
)
noexcept
:
data_
(
first
),
size_
(
static_cast
<
size_t
>
(
last
-
first
))
{
// nop
}
#ifdef CAF_GCC
#ifdef CAF_GCC
constexpr
string_view
(
const
char
*
cstr
)
noexcept
constexpr
string_view
(
const
char
*
cstr
)
noexcept
:
data_
(
cstr
),
size_
(
strlen
(
cstr
))
{
:
data_
(
cstr
),
size_
(
strlen
(
cstr
))
{
...
...
libcaf_core/src/actor_system_config.cpp
View file @
d8035474
...
@@ -27,8 +27,9 @@
...
@@ -27,8 +27,9 @@
#include "caf/config_option.hpp"
#include "caf/config_option.hpp"
#include "caf/config_option_adder.hpp"
#include "caf/config_option_adder.hpp"
#include "caf/defaults.hpp"
#include "caf/defaults.hpp"
#include "caf/detail/config_consumer.hpp"
#include "caf/detail/gcd.hpp"
#include "caf/detail/gcd.hpp"
#include "caf/detail/
ini_consumer
.hpp"
#include "caf/detail/
parser/read_config
.hpp"
#include "caf/detail/parser/read_ini.hpp"
#include "caf/detail/parser/read_ini.hpp"
#include "caf/detail/parser/read_string.hpp"
#include "caf/detail/parser/read_string.hpp"
#include "caf/message_builder.hpp"
#include "caf/message_builder.hpp"
...
@@ -40,7 +41,7 @@ namespace caf {
...
@@ -40,7 +41,7 @@ namespace caf {
namespace
{
namespace
{
constexpr
const
char
*
default_config_file
=
"
caf-application.ini
"
;
constexpr
const
char
*
default_config_file
=
"
$DEFAULT
"
;
}
// namespace
}
// namespace
...
@@ -226,35 +227,35 @@ error actor_system_config::parse(int argc, char** argv, std::istream& ini) {
...
@@ -226,35 +227,35 @@ error actor_system_config::parse(int argc, char** argv, std::istream& ini) {
namespace
{
namespace
{
struct
ini
_iter
{
struct
config
_iter
{
std
::
istream
*
ini
;
std
::
istream
*
ini
;
char
ch
;
char
ch
;
explicit
ini
_iter
(
std
::
istream
*
istr
)
:
ini
(
istr
)
{
explicit
config
_iter
(
std
::
istream
*
istr
)
:
ini
(
istr
)
{
ini
->
get
(
ch
);
ini
->
get
(
ch
);
}
}
ini
_iter
()
:
ini
(
nullptr
),
ch
(
'\0'
)
{
config
_iter
()
:
ini
(
nullptr
),
ch
(
'\0'
)
{
// nop
// nop
}
}
ini_iter
(
const
ini
_iter
&
)
=
default
;
config_iter
(
const
config
_iter
&
)
=
default
;
ini_iter
&
operator
=
(
const
ini
_iter
&
)
=
default
;
config_iter
&
operator
=
(
const
config
_iter
&
)
=
default
;
inline
char
operator
*
()
const
{
inline
char
operator
*
()
const
{
return
ch
;
return
ch
;
}
}
inline
ini
_iter
&
operator
++
()
{
inline
config
_iter
&
operator
++
()
{
ini
->
get
(
ch
);
ini
->
get
(
ch
);
return
*
this
;
return
*
this
;
}
}
};
};
struct
ini
_sentinel
{};
struct
config
_sentinel
{};
bool
operator
!=
(
ini_iter
iter
,
ini
_sentinel
)
{
bool
operator
!=
(
config_iter
iter
,
config
_sentinel
)
{
return
!
iter
.
ini
->
fail
();
return
!
iter
.
ini
->
fail
();
}
}
...
@@ -299,17 +300,17 @@ void print(const config_value::dictionary& xs, indentation indent) {
...
@@ -299,17 +300,17 @@ void print(const config_value::dictionary& xs, indentation indent) {
}
// namespace
}
// namespace
error
actor_system_config
::
parse
(
string_list
args
,
std
::
istream
&
ini
)
{
error
actor_system_config
::
parse
(
string_list
args
,
std
::
istream
&
config
)
{
// Content
of the INI file overrides
hard-coded defaults.
// Content
s of the config file override
hard-coded defaults.
if
(
ini
.
good
())
{
if
(
config
.
good
())
{
if
(
auto
err
=
parse_config
(
ini
,
custom_options_
,
content
))
if
(
auto
err
=
parse_config
(
config
,
custom_options_
,
content
))
return
err
;
return
err
;
}
else
{
}
else
{
// Not finding an explicitly defined config file is an error.
// Not finding an explicitly defined config file is an error.
if
(
auto
fname
=
get_if
<
std
::
string
>
(
&
content
,
"config-file"
))
if
(
auto
fname
=
get_if
<
std
::
string
>
(
&
content
,
"config-file"
))
return
make_error
(
sec
::
cannot_open_file
,
*
fname
);
return
make_error
(
sec
::
cannot_open_file
,
*
fname
);
}
}
// CLI options override the content of the
INI
file.
// CLI options override the content of the
config
file.
using
std
::
make_move_iterator
;
using
std
::
make_move_iterator
;
auto
res
=
custom_options_
.
parse
(
content
,
args
);
auto
res
=
custom_options_
.
parse
(
content
,
args
);
if
(
res
.
second
!=
args
.
end
())
{
if
(
res
.
second
!=
args
.
end
())
{
...
@@ -337,15 +338,32 @@ error actor_system_config::parse(string_list args, std::istream& ini) {
...
@@ -337,15 +338,32 @@ error actor_system_config::parse(string_list args, std::istream& ini) {
return
adjust_content
();
return
adjust_content
();
}
}
error
actor_system_config
::
parse
(
string_list
args
,
const
char
*
ini_file_cstr
)
{
error
actor_system_config
::
parse
(
string_list
args
,
const
char
*
config_file_cstr
)
{
// Override default config file name if set by user.
// Override default config file name if set by user.
if
(
ini
_file_cstr
!=
nullptr
)
if
(
config
_file_cstr
!=
nullptr
)
config_file_path
=
ini
_file_cstr
;
config_file_path
=
config
_file_cstr
;
// CLI arguments always win.
// CLI arguments always win.
if
(
auto
err
=
extract_config_file_path
(
args
))
if
(
auto
err
=
extract_config_file_path
(
args
))
return
err
;
return
err
;
std
::
ifstream
ini
{
config_file_path
};
if
(
config_file_path
==
"$DEFAULT"
)
{
return
parse
(
std
::
move
(
args
),
ini
);
std
::
ifstream
conf
{
"caf-application.conf"
};
// TODO: The .ini parser is deprecated. Remove with CAF 0.19.
if
(
!
conf
.
is_open
())
{
conf
.
open
(
"caf-application.ini"
);
if
(
conf
.
is_open
())
{
// Consume the ini file here, because the parse() overload that takes
// an istream assumes the new config format.
if
(
auto
err
=
parse_ini
(
conf
,
custom_options_
,
content
))
return
err
;
std
::
ifstream
dummy
;
return
parse
(
std
::
move
(
args
),
dummy
);
}
}
return
parse
(
std
::
move
(
args
),
conf
);
}
std
::
ifstream
conf
{
config_file_path
};
return
parse
(
std
::
move
(
args
),
conf
);
}
}
actor_system_config
&
actor_system_config
::
add_actor_factory
(
std
::
string
name
,
actor_system_config
&
actor_system_config
::
add_actor_factory
(
std
::
string
name
,
...
@@ -400,6 +418,14 @@ actor_system_config::parse_config_file(const char* filename,
...
@@ -400,6 +418,14 @@ actor_system_config::parse_config_file(const char* filename,
std
::
ifstream
f
{
filename
};
std
::
ifstream
f
{
filename
};
if
(
!
f
.
is_open
())
if
(
!
f
.
is_open
())
return
make_error
(
sec
::
cannot_open_file
,
filename
);
return
make_error
(
sec
::
cannot_open_file
,
filename
);
// TODO: The .ini parser is deprecated. Remove this block with CAF 0.19.
string_view
fname
{
filename
,
strlen
(
filename
)};
if
(
ends_with
(
fname
,
".ini"
))
{
settings
result
;
if
(
auto
err
=
parse_ini
(
f
,
opts
,
result
))
return
err
;
return
result
;
}
return
parse_config
(
f
,
opts
);
return
parse_config
(
f
,
opts
);
}
}
...
@@ -422,8 +448,21 @@ error actor_system_config::parse_config(std::istream& source,
...
@@ -422,8 +448,21 @@ error actor_system_config::parse_config(std::istream& source,
settings
&
result
)
{
settings
&
result
)
{
if
(
!
source
)
if
(
!
source
)
return
make_error
(
sec
::
runtime_error
,
"source stream invalid"
);
return
make_error
(
sec
::
runtime_error
,
"source stream invalid"
);
detail
::
ini_consumer
consumer
{
opts
,
result
};
detail
::
config_consumer
consumer
{
opts
,
result
};
parser_state
<
ini_iter
,
ini_sentinel
>
res
{
ini_iter
{
&
source
}};
parser_state
<
config_iter
,
config_sentinel
>
res
{
config_iter
{
&
source
}};
detail
::
parser
::
read_config
(
res
,
consumer
);
if
(
res
.
i
!=
res
.
e
)
return
make_error
(
res
.
code
,
res
.
line
,
res
.
column
);
return
none
;
}
error
actor_system_config
::
parse_ini
(
std
::
istream
&
source
,
const
config_option_set
&
opts
,
settings
&
result
)
{
if
(
!
source
)
return
make_error
(
sec
::
runtime_error
,
"source stream invalid"
);
detail
::
config_consumer
consumer
{
opts
,
result
};
parser_state
<
config_iter
,
config_sentinel
>
res
{
config_iter
{
&
source
}};
detail
::
parser
::
read_ini
(
res
,
consumer
);
detail
::
parser
::
read_ini
(
res
,
consumer
);
if
(
res
.
i
!=
res
.
e
)
if
(
res
.
i
!=
res
.
e
)
return
make_error
(
res
.
code
,
res
.
line
,
res
.
column
);
return
make_error
(
res
.
code
,
res
.
line
,
res
.
column
);
...
...
libcaf_core/src/config_option_set.cpp
View file @
d8035474
...
@@ -276,7 +276,7 @@ config_option_set::qualified_name_lookup(string_view category,
...
@@ -276,7 +276,7 @@ config_option_set::qualified_name_lookup(string_view category,
config_option_set
::
option_pointer
config_option_set
::
option_pointer
config_option_set
::
qualified_name_lookup
(
string_view
name
)
const
{
config_option_set
::
qualified_name_lookup
(
string_view
name
)
const
{
auto
sep
=
name
.
find
(
'.'
);
auto
sep
=
name
.
r
find
(
'.'
);
if
(
sep
==
string
::
npos
)
if
(
sep
==
string
::
npos
)
return
nullptr
;
return
nullptr
;
return
qualified_name_lookup
(
name
.
substr
(
0
,
sep
),
name
.
substr
(
sep
+
1
));
return
qualified_name_lookup
(
name
.
substr
(
0
,
sep
),
name
.
substr
(
sep
+
1
));
...
...
libcaf_core/src/config_value.cpp
View file @
d8035474
...
@@ -22,8 +22,8 @@
...
@@ -22,8 +22,8 @@
#include <ostream>
#include <ostream>
#include "caf/deep_to_string.hpp"
#include "caf/deep_to_string.hpp"
#include "caf/detail/
ini
_consumer.hpp"
#include "caf/detail/
config
_consumer.hpp"
#include "caf/detail/parser/read_
ini
.hpp"
#include "caf/detail/parser/read_
config
.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/detail/type_traits.hpp"
#include "caf/expected.hpp"
#include "caf/expected.hpp"
#include "caf/parser_state.hpp"
#include "caf/parser_state.hpp"
...
@@ -67,9 +67,9 @@ expected<config_value> config_value::parse(string_view::iterator first,
...
@@ -67,9 +67,9 @@ expected<config_value> config_value::parse(string_view::iterator first,
if
(
++
i
==
last
)
if
(
++
i
==
last
)
return
make_error
(
pec
::
unexpected_eof
);
return
make_error
(
pec
::
unexpected_eof
);
// Dispatch to parser.
// Dispatch to parser.
detail
::
ini
_value_consumer
f
;
detail
::
config
_value_consumer
f
;
string_parser_state
res
{
i
,
last
};
string_parser_state
res
{
i
,
last
};
parser
::
read_
ini
_value
(
res
,
f
);
parser
::
read_
config
_value
(
res
,
f
);
if
(
res
.
code
==
pec
::
success
)
if
(
res
.
code
==
pec
::
success
)
return
std
::
move
(
f
.
result
);
return
std
::
move
(
f
.
result
);
// Assume an unescaped string unless the first character clearly indicates
// Assume an unescaped string unless the first character clearly indicates
...
...
libcaf_core/src/detail/config_consumer.cpp
0 → 100644
View file @
d8035474
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 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. *
******************************************************************************/
#include "caf/detail/config_consumer.hpp"
#include "caf/detail/overload.hpp"
#include "caf/pec.hpp"
namespace
caf
::
detail
{
// -- config_list_consumer -----------------------------------------------------
config_list_consumer
::
config_list_consumer
(
const
config_option_set
*
options
,
config_consumer
*
parent
)
:
options_
(
options
),
parent_
(
parent
)
{
// nop
}
config_list_consumer
::
config_list_consumer
(
const
config_option_set
*
options
,
config_list_consumer
*
parent
)
:
options_
(
options
),
parent_
(
parent
)
{
// nop
}
config_list_consumer
::
config_list_consumer
(
config_value_consumer
*
parent
)
:
parent_
(
parent
)
{
// nop
}
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
;
});
return
visit
(
f
,
parent_
);
}
config_consumer
config_list_consumer
::
begin_map
()
{
return
config_consumer
{
options_
,
this
};
}
std
::
string
config_list_consumer
::
qualified_key
()
{
auto
f
=
make_overload
([](
config_value_consumer
*
)
{
return
std
::
string
{};
},
[](
auto
*
ptr
)
{
return
ptr
->
qualified_key
();
});
return
visit
(
f
,
parent_
);
}
// -- config_consumer ----------------------------------------------------------
config_consumer
::
config_consumer
(
const
config_option_set
*
options
,
config_consumer
*
parent
)
:
options_
(
options
),
parent_
(
parent
),
cfg_
(
new
settings
),
category_
(
parent
->
qualified_key
())
{
// nop
}
config_consumer
::
config_consumer
(
const
config_option_set
*
options
,
config_list_consumer
*
parent
)
:
options_
(
options
),
parent_
(
parent
),
cfg_
(
new
settings
),
category_
(
parent
->
qualified_key
())
{
// nop
}
config_consumer
::
config_consumer
(
const
config_option_set
&
options
,
settings
&
cfg
)
:
options_
(
&
options
),
cfg_
(
&
cfg
),
category_
(
"global"
)
{
// nop
}
config_consumer
::
config_consumer
(
settings
&
cfg
)
:
cfg_
(
&
cfg
)
{
// nop
}
config_consumer
::
config_consumer
(
config_consumer
&&
other
)
:
options_
(
other
.
options_
),
parent_
(
other
.
parent_
),
cfg_
(
other
.
cfg_
)
{
other
.
parent_
=
none
;
}
config_consumer
&
config_consumer
::
operator
=
(
config_consumer
&&
other
)
{
destroy
();
options_
=
other
.
options_
;
parent_
=
other
.
parent_
;
cfg_
=
other
.
cfg_
;
other
.
parent_
=
none
;
return
*
this
;
}
config_consumer
::~
config_consumer
()
{
destroy
();
}
void
config_consumer
::
destroy
()
{
if
(
!
holds_alternative
<
none_t
>
(
parent_
))
delete
cfg_
;
}
void
config_consumer
::
end_map
()
{
auto
f
=
[
this
](
auto
ptr
)
{
if
constexpr
(
std
::
is_same
<
none_t
,
decltype
(
ptr
)
>::
value
)
{
// nop
}
else
{
ptr
->
value
(
std
::
move
(
*
cfg_
));
}
};
visit
(
f
,
parent_
);
}
std
::
string
config_consumer
::
qualified_key
()
{
if
(
category_
.
empty
())
return
current_key_
;
auto
result
=
category_
;
result
+=
'.'
;
result
+=
current_key_
;
return
result
;
}
namespace
{
void
merge_into_place
(
settings
&
src
,
settings
&
dst
)
{
for
(
auto
&
[
key
,
value
]
:
src
)
{
if
(
auto
src_sub
=
get_if
<
config_value
::
dictionary
>
(
&
value
))
{
auto
i
=
dst
.
find
(
key
);
if
(
i
==
dst
.
end
()
||
!
holds_alternative
<
settings
>
(
i
->
second
))
dst
.
insert_or_assign
(
key
,
std
::
move
(
value
));
else
merge_into_place
(
*
src_sub
,
get
<
settings
>
(
i
->
second
));
}
else
{
dst
.
insert_or_assign
(
key
,
std
::
move
(
value
));
}
}
}
}
// 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
;
}
// Insert / replace value in the map.
if
(
auto
dict
=
get_if
<
settings
>
(
&
x
))
{
// Merge values into the destination, because it can already contain any
// number of unrelated entries.
auto
i
=
cfg_
->
find
(
current_key_
);
if
(
i
==
cfg_
->
end
()
||
!
holds_alternative
<
settings
>
(
i
->
second
))
cfg_
->
insert_or_assign
(
current_key_
,
std
::
move
(
x
));
else
merge_into_place
(
*
dict
,
get
<
settings
>
(
i
->
second
));
}
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
;
}
// -- config_value_consumer ----------------------------------------------------
config_list_consumer
config_value_consumer
::
begin_list
()
{
return
config_list_consumer
{
this
};
}
config_consumer
config_value_consumer
::
begin_map
()
{
return
config_consumer
{
result
.
as_dictionary
()};
}
}
// namespace caf::detail
libcaf_core/src/detail/parser/chars.cpp
View file @
d8035474
...
@@ -33,4 +33,6 @@ const char decimal_chars[11] = "0123456789";
...
@@ -33,4 +33,6 @@ const char decimal_chars[11] = "0123456789";
const
char
octal_chars
[
9
]
=
"01234567"
;
const
char
octal_chars
[
9
]
=
"01234567"
;
const
char
quote_marks
[
3
]
=
"
\"
'"
;
}
// namespace caf::detail::parser
}
// namespace caf::detail::parser
libcaf_core/src/pec_strings.cpp
View file @
d8035474
...
@@ -55,6 +55,8 @@ std::string to_string(pec x) {
...
@@ -55,6 +55,8 @@ std::string to_string(pec x) {
return
"missing_field"
;
return
"missing_field"
;
case
pec
:
:
invalid_range_expression
:
case
pec
:
:
invalid_range_expression
:
return
"invalid_range_expression"
;
return
"invalid_range_expression"
;
case
pec
:
:
invalid_state
:
return
"invalid_state"
;
};
};
}
}
...
...
libcaf_core/test/actor_system_config.cpp
View file @
d8035474
...
@@ -69,8 +69,8 @@ struct fixture {
...
@@ -69,8 +69,8 @@ struct fixture {
void
parse
(
const
char
*
file_content
,
string_list
args
=
{})
{
void
parse
(
const
char
*
file_content
,
string_list
args
=
{})
{
cfg
.
clear
();
cfg
.
clear
();
cfg
.
remainder
.
clear
();
cfg
.
remainder
.
clear
();
std
::
istringstream
ini
{
file_content
};
std
::
istringstream
conf
{
file_content
};
if
(
auto
err
=
cfg
.
parse
(
std
::
move
(
args
),
ini
))
if
(
auto
err
=
cfg
.
parse
(
std
::
move
(
args
),
conf
))
CAF_FAIL
(
"parse() failed: "
<<
err
);
CAF_FAIL
(
"parse() failed: "
<<
err
);
}
}
};
};
...
@@ -80,7 +80,7 @@ struct fixture {
...
@@ -80,7 +80,7 @@ struct fixture {
CAF_TEST_FIXTURE_SCOPE
(
actor_system_config_tests
,
fixture
)
CAF_TEST_FIXTURE_SCOPE
(
actor_system_config_tests
,
fixture
)
CAF_TEST
(
parsing
-
without
CLI
arguments
)
{
CAF_TEST
(
parsing
-
without
CLI
arguments
)
{
auto
text
=
"
[foo]
\n
bar=
\"
hello
\"
"
;
auto
text
=
"
foo{
\n
bar=
\"
hello
\"
}
"
;
options
(
"?foo"
).
add
<
std
::
string
>
(
"bar,b"
,
"some string parameter"
);
options
(
"?foo"
).
add
<
std
::
string
>
(
"bar,b"
,
"some string parameter"
);
parse
(
text
);
parse
(
text
);
CAF_CHECK
(
cfg
.
remainder
.
empty
());
CAF_CHECK
(
cfg
.
remainder
.
empty
());
...
@@ -88,7 +88,7 @@ CAF_TEST(parsing - without CLI arguments) {
...
@@ -88,7 +88,7 @@ CAF_TEST(parsing - without CLI arguments) {
}
}
CAF_TEST
(
parsing
-
without
CLI
cfg
.
remainder
)
{
CAF_TEST
(
parsing
-
without
CLI
cfg
.
remainder
)
{
auto
text
=
"
[foo]
\n
bar=
\"
hello
\"
"
;
auto
text
=
"
foo{
\n
bar=
\"
hello
\"
}
"
;
options
(
"?foo"
).
add
<
std
::
string
>
(
"bar,b"
,
"some string parameter"
);
options
(
"?foo"
).
add
<
std
::
string
>
(
"bar,b"
,
"some string parameter"
);
CAF_MESSAGE
(
"CLI long name"
);
CAF_MESSAGE
(
"CLI long name"
);
parse
(
text
,
{
"--foo.bar=test"
});
parse
(
text
,
{
"--foo.bar=test"
});
...
@@ -109,7 +109,7 @@ CAF_TEST(parsing - without CLI cfg.remainder) {
...
@@ -109,7 +109,7 @@ CAF_TEST(parsing - without CLI cfg.remainder) {
}
}
CAF_TEST
(
parsing
-
with
CLI
cfg
.
remainder
)
{
CAF_TEST
(
parsing
-
with
CLI
cfg
.
remainder
)
{
auto
text
=
"
[foo]
\n
bar=
\"
hello
\"
"
;
auto
text
=
"
foo{
\n
bar=
\"
hello
\"
}
"
;
options
(
"?foo"
).
add
<
std
::
string
>
(
"bar,b"
,
"some string parameter"
);
options
(
"?foo"
).
add
<
std
::
string
>
(
"bar,b"
,
"some string parameter"
);
CAF_MESSAGE
(
"valid cfg.remainder"
);
CAF_MESSAGE
(
"valid cfg.remainder"
);
parse
(
text
,
{
"-b"
,
"test"
,
"hello"
,
"world"
});
parse
(
text
,
{
"-b"
,
"test"
,
"hello"
,
"world"
});
...
...
libcaf_core/test/detail/config_consumer.cpp
0 → 100644
View file @
d8035474
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2018 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. *
******************************************************************************/
#define CAF_SUITE detail.config_consumer
#include "caf/detail/config_consumer.hpp"
#include "caf/test/dsl.hpp"
#include "caf/detail/parser/read_config.hpp"
using
std
::
string
;
using
namespace
caf
;
// List-of-strings.
using
ls
=
std
::
vector
<
std
::
string
>
;
namespace
{
constexpr
const
string_view
test_config1
=
R"(
is_server=true
port=4242
nodes=["sun", "venus", ]
logger{
file-name = "foobar.conf" # our file name
}
scheduler { # more settings
timing = 2us # using microsecond resolution
}
)"
;
constexpr
const
string_view
test_config2
=
R"(
is_server = true
logger = {
file-name = "foobar.conf"
}
port = 4242
scheduler : {
timing = 2us,
}
nodes = ["sun", "venus"]
)"
;
struct
fixture
{
config_option_set
options
;
settings
config
;
fixture
()
{
options
.
add
<
bool
>
(
"global"
,
"is_server"
,
"enables server mode"
)
.
add
<
uint16_t
>
(
"global"
,
"port"
,
"sets local or remote port"
)
.
add
<
ls
>
(
"global"
,
"nodes"
,
"list of remote nodes"
)
.
add
<
string
>
(
"logger"
,
"file-name"
,
"log output file"
)
.
add
<
int
>
(
"scheduler"
,
"padding"
,
"some integer"
)
.
add
<
timespan
>
(
"scheduler"
,
"timing"
,
"some timespan"
);
}
};
}
// namespace
CAF_TEST_FIXTURE_SCOPE
(
config_consumer_tests
,
fixture
)
CAF_TEST
(
config_consumer
)
{
string_view
str
=
test_config1
;
detail
::
config_consumer
consumer
{
options
,
config
};
string_parser_state
res
{
str
.
begin
(),
str
.
end
()};
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_TEST
(
simplified
syntax
)
{
CAF_MESSAGE
(
"read test_config"
);
{
detail
::
config_consumer
consumer
{
options
,
config
};
string_parser_state
res
{
test_config1
.
begin
(),
test_config1
.
end
()};
detail
::
parser
::
read_config
(
res
,
consumer
);
CAF_CHECK_EQUAL
(
res
.
code
,
pec
::
success
);
}
settings
config2
;
CAF_MESSAGE
(
"read test_config2"
);
{
detail
::
config_consumer
consumer
{
options
,
config2
};
string_parser_state
res
{
test_config2
.
begin
(),
test_config2
.
end
()};
detail
::
parser
::
read_config
(
res
,
consumer
);
CAF_CHECK_EQUAL
(
res
.
code
,
pec
::
success
);
}
CAF_CHECK_EQUAL
(
config
,
config2
);
}
CAF_TEST_FIXTURE_SCOPE_END
()
libcaf_core/test/detail/parser/read_config.cpp
0 → 100644
View file @
d8035474
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright 2011-2020 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. *
******************************************************************************/
#define CAF_SUITE detail.parser.read_config
#include "caf/detail/parser/read_config.hpp"
#include "caf/test/dsl.hpp"
#include "caf/config_value.hpp"
#include "caf/parser_state.hpp"
#include "caf/pec.hpp"
#include "caf/string_view.hpp"
using
namespace
caf
;
namespace
{
using
log_type
=
std
::
vector
<
std
::
string
>
;
struct
test_consumer
{
log_type
log
;
test_consumer
()
=
default
;
test_consumer
(
const
test_consumer
&
)
=
delete
;
test_consumer
&
operator
=
(
const
test_consumer
&
)
=
delete
;
test_consumer
&
begin_map
()
{
log
.
emplace_back
(
"{"
);
return
*
this
;
}
void
end_map
()
{
log
.
emplace_back
(
"}"
);
}
test_consumer
&
begin_list
()
{
log
.
emplace_back
(
"["
);
return
*
this
;
}
void
end_list
()
{
log
.
emplace_back
(
"]"
);
}
void
key
(
std
::
string
name
)
{
add_entry
(
"key: "
,
std
::
move
(
name
));
}
template
<
class
T
>
void
value
(
T
x
)
{
config_value
cv
{
std
::
move
(
x
)};
std
::
string
entry
=
"value ("
;
entry
+=
cv
.
type_name
();
entry
+=
"): "
;
entry
+=
to_string
(
cv
);
log
.
emplace_back
(
std
::
move
(
entry
));
}
void
add_entry
(
const
char
*
prefix
,
std
::
string
str
)
{
str
.
insert
(
0
,
prefix
);
log
.
emplace_back
(
std
::
move
(
str
));
}
};
struct
fixture
{
expected
<
log_type
>
parse
(
string_view
str
,
bool
expect_success
=
true
)
{
test_consumer
f
;
string_parser_state
res
{
str
.
begin
(),
str
.
end
()};
detail
::
parser
::
read_config
(
res
,
f
);
if
((
res
.
code
==
pec
::
success
)
!=
expect_success
)
{
CAF_MESSAGE
(
"unexpected parser result state: "
<<
res
.
code
);
CAF_MESSAGE
(
"input remainder: "
<<
std
::
string
(
res
.
i
,
res
.
e
));
}
return
std
::
move
(
f
.
log
);
}
};
template
<
class
...
Ts
>
log_type
make_log
(
Ts
&&
...
xs
)
{
return
log_type
{
std
::
forward
<
Ts
>
(
xs
)...};
}
// Tests basic functionality.
constexpr
const
string_view
conf0
=
R"(
"foo=bar" {
foo="bar"
}
1group {
1value=321
}
_foo {
_bar=11
}
logger {
"padding":10
file-name = "foobar.ini" # our file name
}
scheduler { # more settings
timing = 2us # using microsecond resolution
x_ =.123
some-bool=true
some-other-bool=false
}
some-list=[
# here we have some list entries
123,
1..3,
23 # twenty-three!
,2..4..2,
"abc", # some comment and a trailing comma
]
some-map:{
# here we have some list entries
entry1:123,
entry2=23 # twenty-three! btw, comma is not mandatory
entry3= "abc" , # some comment and a trailing comma
}
middleman {
preconnect=[<
tcp://localhost:8080
>,<udp://remotehost?trust=false>]
}
)"
;
constexpr
const
string_view
conf1
=
R"(
{
"foo" : {
"bar" : 1,
"baz" : 2
}
}
)"
;
// clang-format off
const
auto
conf0_log
=
make_log
(
"key: foo=bar"
,
"{"
,
"key: foo"
,
"value (string):
\"
bar
\"
"
,
"}"
,
"key: 1group"
,
"{"
,
"key: 1value"
,
"value (integer): 321"
,
"}"
,
"key: _foo"
,
"{"
,
"key: _bar"
,
"value (integer): 11"
,
"}"
,
"key: logger"
,
"{"
,
"key: padding"
,
"value (integer): 10"
,
"key: file-name"
,
"value (string):
\"
foobar.ini
\"
"
,
"}"
,
"key: scheduler"
,
"{"
,
"key: timing"
,
"value (timespan): 2us"
,
"key: x_"
,
"value (real): "
+
deep_to_string
(
.123
),
"key: some-bool"
,
"value (boolean): true"
,
"key: some-other-bool"
,
"value (boolean): false"
,
"}"
,
"key: some-list"
,
"["
,
"value (integer): 123"
,
"value (integer): 1"
,
"value (integer): 2"
,
"value (integer): 3"
,
"value (integer): 23"
,
"value (integer): 2"
,
"value (integer): 4"
,
"value (string):
\"
abc
\"
"
,
"]"
,
"key: some-map"
,
"{"
,
"key: entry1"
,
"value (integer): 123"
,
"key: entry2"
,
"value (integer): 23"
,
"key: entry3"
,
"value (string):
\"
abc
\"
"
,
"}"
,
"key: middleman"
,
"{"
,
"key: preconnect"
,
"["
,
"value (uri): tcp://localhost:8080"
,
"value (uri): udp://remotehost?trust=false"
,
"]"
,
"}"
);
const
auto
conf1_log
=
make_log
(
"key: foo"
,
"{"
,
"key: bar"
,
"value (integer): 1"
,
"key: baz"
,
"value (integer): 2"
,
"}"
);
// clang-format on
}
// namespace
CAF_TEST_FIXTURE_SCOPE
(
read_config_tests
,
fixture
)
CAF_TEST
(
read_config
feeds
into
a
consumer
)
{
CAF_CHECK_EQUAL
(
parse
(
conf0
),
conf0_log
);
CAF_CHECK_EQUAL
(
parse
(
conf1
),
conf1_log
);
}
CAF_TEST_FIXTURE_SCOPE_END
()
libcaf_core/test/detail/parser/read_ini.cpp
View file @
d8035474
...
@@ -81,14 +81,6 @@ struct test_consumer {
...
@@ -81,14 +81,6 @@ struct test_consumer {
}
}
};
};
struct
ini_consumer
{
using
inner_map
=
std
::
map
<
std
::
string
,
config_value
>
;
using
section_map
=
std
::
map
<
std
::
string
,
inner_map
>
;
section_map
sections
;
section_map
::
iterator
current_section
;
};
struct
fixture
{
struct
fixture
{
expected
<
log_type
>
parse
(
string_view
str
,
bool
expect_success
=
true
)
{
expected
<
log_type
>
parse
(
string_view
str
,
bool
expect_success
=
true
)
{
test_consumer
f
;
test_consumer
f
;
...
...
libcaf_core/test/detail/parser/read_string.cpp
View file @
d8035474
...
@@ -24,6 +24,7 @@
...
@@ -24,6 +24,7 @@
#include <string>
#include <string>
#include "caf/expected.hpp"
#include "caf/parser_state.hpp"
#include "caf/parser_state.hpp"
#include "caf/string_view.hpp"
#include "caf/string_view.hpp"
#include "caf/variant.hpp"
#include "caf/variant.hpp"
...
@@ -39,16 +40,14 @@ struct string_parser_consumer {
...
@@ -39,16 +40,14 @@ struct string_parser_consumer {
}
}
};
};
using
res_t
=
variant
<
pec
,
std
::
string
>
;
struct
string_parser
{
struct
string_parser
{
res_t
operator
()(
string_view
str
)
{
expected
<
std
::
string
>
operator
()(
string_view
str
)
{
string_parser_consumer
f
;
string_parser_consumer
f
;
string_parser_state
res
{
str
.
begin
(),
str
.
end
()};
string_parser_state
res
{
str
.
begin
(),
str
.
end
()};
detail
::
parser
::
read_string
(
res
,
f
);
detail
::
parser
::
read_string
(
res
,
f
);
if
(
res
.
code
==
pec
::
success
)
if
(
res
.
code
==
pec
::
success
)
return
f
.
x
;
return
f
.
x
;
return
res
.
code
;
return
make_error
(
res
.
code
,
res
.
column
,
std
::
string
{
res
.
i
,
res
.
e
})
;
}
}
};
};
...
@@ -75,18 +74,33 @@ CAF_TEST(empty string) {
...
@@ -75,18 +74,33 @@ CAF_TEST(empty string) {
CAF_CHECK_EQUAL
(
p
(
R"("" )"
),
""
_s
);
CAF_CHECK_EQUAL
(
p
(
R"("" )"
),
""
_s
);
CAF_CHECK_EQUAL
(
p
(
R"( "" )"
),
""
_s
);
CAF_CHECK_EQUAL
(
p
(
R"( "" )"
),
""
_s
);
CAF_CHECK_EQUAL
(
p
(
"
\t
\"\"
\t\t\t
"
),
""
_s
);
CAF_CHECK_EQUAL
(
p
(
"
\t
\"\"
\t\t\t
"
),
""
_s
);
CAF_CHECK_EQUAL
(
p
(
R"('')"
),
""
_s
);
CAF_CHECK_EQUAL
(
p
(
R"( '')"
),
""
_s
);
CAF_CHECK_EQUAL
(
p
(
R"( '')"
),
""
_s
);
CAF_CHECK_EQUAL
(
p
(
R"('' )"
),
""
_s
);
CAF_CHECK_EQUAL
(
p
(
R"('' )"
),
""
_s
);
CAF_CHECK_EQUAL
(
p
(
R"( '' )"
),
""
_s
);
CAF_CHECK_EQUAL
(
p
(
"
\t
''
\t\t\t
"
),
""
_s
);
}
}
CAF_TEST
(
non
-
empty
quoted
string
)
{
CAF_TEST
(
nonempty
quoted
string
)
{
CAF_CHECK_EQUAL
(
p
(
R"("abc")"
),
"abc"
_s
);
CAF_CHECK_EQUAL
(
p
(
R"("abc")"
),
"abc"
_s
);
CAF_CHECK_EQUAL
(
p
(
R"("a b c")"
),
"a b c"
_s
);
CAF_CHECK_EQUAL
(
p
(
R"("a b c")"
),
"a b c"
_s
);
CAF_CHECK_EQUAL
(
p
(
R"( "abcdefABCDEF" )"
),
"abcdefABCDEF"
_s
);
CAF_CHECK_EQUAL
(
p
(
R"( "abcdefABCDEF" )"
),
"abcdefABCDEF"
_s
);
CAF_CHECK_EQUAL
(
p
(
R"('abc')"
),
"abc"
_s
);
CAF_CHECK_EQUAL
(
p
(
R"('a b c')"
),
"a b c"
_s
);
CAF_CHECK_EQUAL
(
p
(
R"( 'abcdefABCDEF' )"
),
"abcdefABCDEF"
_s
);
}
}
CAF_TEST
(
quoted
string
with
escaped
characters
)
{
CAF_TEST
(
quoted
string
with
escaped
characters
)
{
CAF_CHECK_EQUAL
(
p
(
R"("a\tb\tc")"
),
"a
\t
b
\t
c"
_s
);
CAF_CHECK_EQUAL
(
p
(
R"("a\tb\tc")"
),
"a
\t
b
\t
c"
_s
);
CAF_CHECK_EQUAL
(
p
(
R"("a\nb\r\nc")"
),
"a
\n
b
\r\n
c"
_s
);
CAF_CHECK_EQUAL
(
p
(
R"("a\nb\r\nc")"
),
"a
\n
b
\r\n
c"
_s
);
CAF_CHECK_EQUAL
(
p
(
R"("a\\b")"
),
"a
\\
b"
_s
);
CAF_CHECK_EQUAL
(
p
(
R"("a\\b")"
),
"a
\\
b"
_s
);
CAF_CHECK_EQUAL
(
p
(
"
\"
'hello'
\\\"
world
\\\"\"
"
),
"'hello'
\"
world
\"
"
_s
);
CAF_CHECK_EQUAL
(
p
(
R"('a\tb\tc')"
),
"a
\t
b
\t
c"
_s
);
CAF_CHECK_EQUAL
(
p
(
R"('a\nb\r\nc')"
),
"a
\n
b
\r\n
c"
_s
);
CAF_CHECK_EQUAL
(
p
(
R"('a\\b')"
),
"a
\\
b"
_s
);
CAF_CHECK_EQUAL
(
p
(
R"('\'hello\' "world"')"
),
"'hello'
\"
world
\"
"
_s
);
}
}
CAF_TEST
(
unquoted
strings
)
{
CAF_TEST
(
unquoted
strings
)
{
...
@@ -97,8 +111,11 @@ CAF_TEST(unquoted strings) {
...
@@ -97,8 +111,11 @@ CAF_TEST(unquoted strings) {
CAF_TEST
(
invalid
strings
)
{
CAF_TEST
(
invalid
strings
)
{
CAF_CHECK_EQUAL
(
p
(
R"("abc)"
),
pec
::
unexpected_eof
);
CAF_CHECK_EQUAL
(
p
(
R"("abc)"
),
pec
::
unexpected_eof
);
CAF_CHECK_EQUAL
(
p
(
R"('abc)"
),
pec
::
unexpected_eof
);
CAF_CHECK_EQUAL
(
p
(
"
\"
ab
\n
c
\"
"
),
pec
::
unexpected_newline
);
CAF_CHECK_EQUAL
(
p
(
"
\"
ab
\n
c
\"
"
),
pec
::
unexpected_newline
);
CAF_CHECK_EQUAL
(
p
(
"'ab
\n
c'"
),
pec
::
unexpected_newline
);
CAF_CHECK_EQUAL
(
p
(
R"("abc" def)"
),
pec
::
trailing_character
);
CAF_CHECK_EQUAL
(
p
(
R"("abc" def)"
),
pec
::
trailing_character
);
CAF_CHECK_EQUAL
(
p
(
R"('abc' def)"
),
pec
::
trailing_character
);
CAF_CHECK_EQUAL
(
p
(
R"( 123, )"
),
pec
::
trailing_character
);
CAF_CHECK_EQUAL
(
p
(
R"( 123, )"
),
pec
::
trailing_character
);
}
}
...
...
libcaf_core/test/settings.cpp
View file @
d8035474
...
@@ -24,6 +24,8 @@
...
@@ -24,6 +24,8 @@
#include <string>
#include <string>
#include "caf/detail/config_consumer.hpp"
#include "caf/detail/parser/read_config.hpp"
#include "caf/none.hpp"
#include "caf/none.hpp"
#include "caf/optional.hpp"
#include "caf/optional.hpp"
...
@@ -204,4 +206,18 @@ CAF_TEST(custom type) {
...
@@ -204,4 +206,18 @@ CAF_TEST(custom type) {
CAF_CHECK_EQUAL
(
fb
.
bar
,
24
);
CAF_CHECK_EQUAL
(
fb
.
bar
,
24
);
}
}
CAF_TEST
(
read_config
accepts
the
to_string
output
of
settings
)
{
fill
();
auto
str
=
to_string
(
x
);
settings
y
;
config_option_set
dummy
;
detail
::
config_consumer
consumer
{
dummy
,
y
};
string_view
str_view
=
str
;
string_parser_state
res
{
str_view
.
begin
(),
str_view
.
end
()};
detail
::
parser
::
read_config
(
res
,
consumer
);
CAF_CHECK
(
res
.
i
==
res
.
e
);
CAF_CHECK_EQUAL
(
res
.
code
,
pec
::
success
);
CAF_CHECK_EQUAL
(
x
,
y
);
}
CAF_TEST_FIXTURE_SCOPE_END
()
CAF_TEST_FIXTURE_SCOPE_END
()
manual/ConfiguringActorApplications.rst
View file @
d8035474
...
@@ -124,13 +124,13 @@ object *before* initializing an actor system with it.
...
@@ -124,13 +124,13 @@ object *before* initializing an actor system with it.
.. _system-config-options:
.. _system-config-options:
Command Line Options and
INI
Configuration Files
Command Line Options and Configuration Files
--------------------------------------------
----
--------------------------------------------
CAF organizes program options in categories and parses CLI arguments as well as
CAF organizes program options in categories and parses CLI arguments as well as
INI files. CLI arguments override values in the INI file which overrid
e
configuration files. CLI arguments override values in the configuration fil
e
hard-coded defaults. Users can add any number of custom program options by
which override hard-coded defaults. Users can add any number of custom program
implementing a subtype of ``actor_system_config``. The example below
options by
implementing a subtype of ``actor_system_config``. The example below
adds three options to the ``global`` category.
adds three options to the ``global`` category.
.. literalinclude:: /examples/remoting/distributed_calculator.cpp
.. literalinclude:: /examples/remoting/distributed_calculator.cpp
...
@@ -166,37 +166,67 @@ CAF adds the program options ``help`` (with short names ``-h``
...
@@ -166,37 +166,67 @@ CAF adds the program options ``help`` (with short names ``-h``
and ``-?``) as well as ``long-help`` to the ``global``
and ``-?``) as well as ``long-help`` to the ``global``
category.
category.
The default name for the
INI file is ``caf-application.ini``. Users can
The default name for the
configuration file is ``caf-application.conf``. Users
change the file name and path by passing ``--config-file=<path>`` on the
c
an c
hange the file name and path by passing ``--config-file=<path>`` on the
command line.
command line.
INI files are organized in categories. No value is allowed outside of a category
The syntax for the configuration files provides a clean JSON-like grammar that
(no implicit ``global`` category). The parses uses the following syntax:
is similar to commonly used configuration formats such as the file format of
``lighttpd``. In a nutshell, instead of writing:
+------------------------+-----------------------------+
| ``key=true`` | is a boolean |
.. code-block:: JSON
+------------------------+-----------------------------+
| ``key=1`` | is an integer |
{
+------------------------+-----------------------------+
"my-category" : {
| ``key=1.0`` | is an floating point number |
"first" : 1,
+------------------------+-----------------------------+
"second" : 2
| ``key=1ms`` | is an timespan |
}
+------------------------+-----------------------------+
}
| ``key='foo'`` | is an atom |
+------------------------+-----------------------------+
you can reduce the noise by writing:
| ``key="foo"`` | is a string |
+------------------------+-----------------------------+
.. code-block:: none
| ``key=[0, 1, ...]`` | is as a list |
+------------------------+-----------------------------+
my-category {
| ``key={a=1, b=2, ...}``| is a dictionary (map) |
first = 1
+------------------------+-----------------------------+
second = 2
}
The following example INI file lists all standard options in CAF and their
default value. Note that some options such as ``scheduler.max-threads``
.. note::
CAF will accept both of the examples above and will produce the same result.
We recommend using the second style, mostly because it reduces syntax noise.
Unlike regular JSON, CAF's configuration format supports a couple of additional
syntax elements such as comments (comments start with ``#`` and end at the end
of the line) and, most notably, does *not* accept ``null``.
The parses uses the following syntax for writing key-value pairs:
+-------------------------+-----------------------------+
| ``key=true`` | is a boolean |
+-------------------------+-----------------------------+
| ``key=1`` | is an integer |
+-------------------------+-----------------------------+
| ``key=1.0`` | is an floating point number |
+-------------------------+-----------------------------+
| ``key=1ms`` | is an timespan |
+-------------------------+-----------------------------+
| ``key='foo'`` | is a string |
+-------------------------+-----------------------------+
| ``key="foo"`` | is a string |
+-------------------------+-----------------------------+
| ``key=[0, 1, ...]`` | is as a list |
+-------------------------+-----------------------------+
| ``key={a=1, b=2, ...}`` | is a dictionary (map) |
+-------------------------+-----------------------------+
The following example configuration file lists all standard options in CAF and
their default value. Note that some options such as ``scheduler.max-threads``
are usually detected at runtime and thus have no hard-coded default.
are usually detected at runtime and thus have no hard-coded default.
.. literalinclude:: /examples/caf-application.
ini
.. literalinclude:: /examples/caf-application.
conf
:language:
INI
:language:
none
.. _add-custom-message-type:
.. _add-custom-message-type:
...
...
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