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
1de110dd
Unverified
Commit
1de110dd
authored
Jan 13, 2021
by
Noir
Committed by
GitHub
Jan 13, 2021
Browse files
Options
Browse Files
Download
Plain Diff
Merge branch 'master' into issue/1195
parents
39cb34c8
c089c764
Changes
61
Show whitespace changes
Inline
Side-by-side
Showing
61 changed files
with
1184 additions
and
914 deletions
+1184
-914
CHANGELOG.md
CHANGELOG.md
+4
-0
CMakeLists.txt
CMakeLists.txt
+5
-14
doc/graffle/mailbox_element.graffle
doc/graffle/mailbox_element.graffle
+0
-0
examples/CMakeLists.txt
examples/CMakeLists.txt
+1
-1
examples/aout.cpp
examples/aout.cpp
+23
-20
examples/broker/simple_broker.cpp
examples/broker/simple_broker.cpp
+2
-2
examples/custom_type/custom_types_1.cpp
examples/custom_type/custom_types_1.cpp
+0
-3
examples/hello_world.cpp
examples/hello_world.cpp
+19
-19
examples/message_passing/calculator.cpp
examples/message_passing/calculator.cpp
+18
-11
examples/message_passing/cell.cpp
examples/message_passing/cell.cpp
+2
-2
examples/message_passing/dancing_kirby.cpp
examples/message_passing/dancing_kirby.cpp
+18
-14
examples/message_passing/delegating.cpp
examples/message_passing/delegating.cpp
+19
-22
examples/message_passing/divider.cpp
examples/message_passing/divider.cpp
+35
-8
examples/message_passing/fan_out_request.cpp
examples/message_passing/fan_out_request.cpp
+2
-4
examples/message_passing/fixed_stack.cpp
examples/message_passing/fixed_stack.cpp
+37
-0
examples/message_passing/promises.cpp
examples/message_passing/promises.cpp
+22
-28
examples/message_passing/request.cpp
examples/message_passing/request.cpp
+31
-16
examples/remoting/distributed_calculator.cpp
examples/remoting/distributed_calculator.cpp
+2
-0
examples/testing/ping_pong.cpp
examples/testing/ping_pong.cpp
+2
-2
libcaf_core/CMakeLists.txt
libcaf_core/CMakeLists.txt
+1
-0
libcaf_core/caf/all.hpp
libcaf_core/caf/all.hpp
+1
-0
libcaf_core/caf/byte.hpp
libcaf_core/caf/byte.hpp
+5
-7
libcaf_core/caf/caf_main.hpp
libcaf_core/caf/caf_main.hpp
+24
-0
libcaf_core/caf/config_value.hpp
libcaf_core/caf/config_value.hpp
+1
-1
libcaf_core/caf/detail/message_data.hpp
libcaf_core/caf/detail/message_data.hpp
+58
-1
libcaf_core/caf/detail/type_list.hpp
libcaf_core/caf/detail/type_list.hpp
+0
-17
libcaf_core/caf/exit_reason.hpp
libcaf_core/caf/exit_reason.hpp
+2
-0
libcaf_core/caf/fwd.hpp
libcaf_core/caf/fwd.hpp
+0
-1
libcaf_core/caf/message.hpp
libcaf_core/caf/message.hpp
+11
-0
libcaf_core/caf/response_promise.hpp
libcaf_core/caf/response_promise.hpp
+20
-10
libcaf_core/caf/response_type.hpp
libcaf_core/caf/response_type.hpp
+9
-2
libcaf_core/caf/sec.hpp
libcaf_core/caf/sec.hpp
+4
-2
libcaf_core/caf/string_view.hpp
libcaf_core/caf/string_view.hpp
+9
-16
libcaf_core/caf/type_id_list.hpp
libcaf_core/caf/type_id_list.hpp
+36
-0
libcaf_core/caf/typed_actor.hpp
libcaf_core/caf/typed_actor.hpp
+24
-16
libcaf_core/caf/typed_response_promise.hpp
libcaf_core/caf/typed_response_promise.hpp
+25
-27
libcaf_core/src/config_value.cpp
libcaf_core/src/config_value.cpp
+88
-5
libcaf_core/src/config_value_reader.cpp
libcaf_core/src/config_value_reader.cpp
+7
-12
libcaf_core/src/detail/message_data.cpp
libcaf_core/src/detail/message_data.cpp
+35
-3
libcaf_core/src/response_promise.cpp
libcaf_core/src/response_promise.cpp
+6
-1
libcaf_core/src/type_id_list.cpp
libcaf_core/src/type_id_list.cpp
+18
-0
libcaf_core/test/config_value.cpp
libcaf_core/test/config_value.cpp
+39
-0
libcaf_core/test/message.cpp
libcaf_core/test/message.cpp
+12
-0
libcaf_core/test/response_promise.cpp
libcaf_core/test/response_promise.cpp
+157
-0
libcaf_core/test/type_id_list.cpp
libcaf_core/test/type_id_list.cpp
+32
-0
libcaf_core/test/typed_response_promise.cpp
libcaf_core/test/typed_response_promise.cpp
+137
-168
libcaf_io/caf/io/basp/message_type.hpp
libcaf_io/caf/io/basp/message_type.hpp
+2
-2
libcaf_io/src/io/middleman.cpp
libcaf_io/src/io/middleman.cpp
+1
-1
manual/Actors.rst
manual/Actors.rst
+20
-7
manual/ConfiguringActorApplications.rst
manual/ConfiguringActorApplications.rst
+2
-1
manual/Error.rst
manual/Error.rst
+32
-27
manual/FAQ.rst
manual/FAQ.rst
+2
-16
manual/GroupCommunication.rst
manual/GroupCommunication.rst
+2
-4
manual/ManagingGroupsOfWorkers.rst
manual/ManagingGroupsOfWorkers.rst
+3
-4
manual/MessageHandlers.rst
manual/MessageHandlers.rst
+18
-33
manual/MessagePassing.rst
manual/MessagePassing.rst
+86
-86
manual/Messages.rst
manual/Messages.rst
+0
-253
manual/Overview.rst
manual/Overview.rst
+11
-23
manual/Testing.rst
manual/Testing.rst
+2
-1
manual/index.rst
manual/index.rst
+0
-1
manual/mailbox_element.png
manual/mailbox_element.png
+0
-0
No files found.
CHANGELOG.md
View file @
1de110dd
...
@@ -22,6 +22,8 @@ is based on [Keep a Changelog](https://keepachangelog.com).
...
@@ -22,6 +22,8 @@ is based on [Keep a Changelog](https://keepachangelog.com).
trying to parse the input of
`x`
if it contains a string. The function
trying to parse the input of
`x`
if it contains a string. The function
`get_or`
already existed for
`settings`
, but we have added new overloads for
`get_or`
already existed for
`settings`
, but we have added new overloads for
generalizing the function to
`config_value`
as well.
generalizing the function to
`config_value`
as well.
-
The
`typed_response_promise`
received additional member functions to mirror
the interface of the untyped
`response_promise`
.
### Deprecated
### Deprecated
...
@@ -84,6 +86,8 @@ is based on [Keep a Changelog](https://keepachangelog.com).
...
@@ -84,6 +86,8 @@ is based on [Keep a Changelog](https://keepachangelog.com).
-
Skipping high-priority messages resulted in CAF lowering the priority to
-
Skipping high-priority messages resulted in CAF lowering the priority to
normal. This unintentional demotion has been fixed (#1171).
normal. This unintentional demotion has been fixed (#1171).
-
Fix undefined behavior in the experimental datagram brokers (#1174).
-
Fix undefined behavior in the experimental datagram brokers (#1174).
-
Response promises no longer send empty messages in response to asynchronous
messages.
-
`CAF_ADD_TYPE_ID`
now works with types that live in namespaces that also exist
-
`CAF_ADD_TYPE_ID`
now works with types that live in namespaces that also exist
as nested namespace in CAF such as
`detail`
or
`io`
(#1195).
as nested namespace in CAF such as
`detail`
or
`io`
(#1195).
...
...
CMakeLists.txt
View file @
1de110dd
cmake_minimum_required
(
VERSION 3.5...3.18 FATAL_ERROR
)
cmake_minimum_required
(
VERSION 3.5...3.18 FATAL_ERROR
)
project
(
CAF CXX
)
project
(
CAF CXX
)
cmake_policy
(
PUSH
)
cmake_policy
(
VERSION 3.5...3.18
)
# -- includes ------------------------------------------------------------------
# -- includes ------------------------------------------------------------------
include
(
CMakeDependentOption
)
include
(
CMakeDependentOption
)
...
@@ -49,10 +46,7 @@ cmake_dependent_option(CAF_ENABLE_OPENSSL_MODULE "Build OpenSSL module" ON
...
@@ -49,10 +46,7 @@ cmake_dependent_option(CAF_ENABLE_OPENSSL_MODULE "Build OpenSSL module" ON
set
(
CAF_LOG_LEVEL
"QUIET"
CACHE STRING
"Set log verbosity of CAF components"
)
set
(
CAF_LOG_LEVEL
"QUIET"
CACHE STRING
"Set log verbosity of CAF components"
)
set
(
CAF_SANITIZERS
""
CACHE STRING
set
(
CAF_SANITIZERS
""
CACHE STRING
"Comma separated sanitizers, e.g., 'address,undefined'"
)
"Comma separated sanitizers, e.g., 'address,undefined'"
)
set
(
CAF_INSTALL_CMAKEDIR
set
(
CAF_BUILD_INFO_FILE_PATH
""
CACHE FILEPATH
"
${
CMAKE_INSTALL_FULL_LIBDIR
}
/cmake/CAF"
CACHE PATH
"Path for installing CMake files, enables 'find_package(CAF)'"
)
set
(
CAF_BUILD_INFO_FILE_PATH
""
CACHE FILEPATH
"Optional path for writing CMake and compiler version information"
)
"Optional path for writing CMake and compiler version information"
)
# -- macOS-specific options ----------------------------------------------------
# -- macOS-specific options ----------------------------------------------------
...
@@ -450,7 +444,7 @@ endif()
...
@@ -450,7 +444,7 @@ endif()
export
(
EXPORT CAFTargets FILE CAFTargets.cmake NAMESPACE CAF::
)
export
(
EXPORT CAFTargets FILE CAFTargets.cmake NAMESPACE CAF::
)
install
(
EXPORT CAFTargets
install
(
EXPORT CAFTargets
DESTINATION
"
${
C
AF_INSTALL_CMAKEDIR
}
"
DESTINATION
"
${
C
MAKE_INSTALL_LIBDIR
}
/cmake/CAF
"
NAMESPACE CAF::
)
NAMESPACE CAF::
)
write_basic_package_version_file
(
write_basic_package_version_file
(
...
@@ -461,15 +455,14 @@ write_basic_package_version_file(
...
@@ -461,15 +455,14 @@ write_basic_package_version_file(
configure_package_config_file
(
configure_package_config_file
(
"
${
CMAKE_CURRENT_SOURCE_DIR
}
/cmake/CAFConfig.cmake.in"
"
${
CMAKE_CURRENT_SOURCE_DIR
}
/cmake/CAFConfig.cmake.in"
"
${
CMAKE_CURRENT_BINARY_DIR
}
/CAFConfig.cmake"
"
${
CMAKE_CURRENT_BINARY_DIR
}
/CAFConfig.cmake"
INSTALL_DESTINATION
"
${
C
AF_INSTALL_CMAKEDIR
}
"
)
INSTALL_DESTINATION
"
${
C
MAKE_INSTALL_LIBDIR
}
/cmake/CAF
"
)
install
(
install
(
FILES
FILES
"
${
CMAKE_CURRENT_BINARY_DIR
}
/CAFConfig.cmake"
"
${
CMAKE_CURRENT_BINARY_DIR
}
/CAFConfig.cmake"
"
${
CMAKE_CURRENT_BINARY_DIR
}
/CAFConfigVersion.cmake"
"
${
CMAKE_CURRENT_BINARY_DIR
}
/CAFConfigVersion.cmake"
DESTINATION
DESTINATION
"
${
CAF_INSTALL_CMAKEDIR
}
"
)
"
${
CMAKE_INSTALL_LIBDIR
}
/cmake/CAF"
)
# -- extra file output (primarily for CAF CI) ----------------------------------
# -- extra file output (primarily for CAF CI) ----------------------------------
...
@@ -478,5 +471,3 @@ if(CAF_BUILD_INFO_FILE_PATH)
...
@@ -478,5 +471,3 @@ if(CAF_BUILD_INFO_FILE_PATH)
"
${
CAF_BUILD_INFO_FILE_PATH
}
"
"
${
CAF_BUILD_INFO_FILE_PATH
}
"
@ONLY
)
@ONLY
)
endif
()
endif
()
cmake_policy
(
POP
)
doc/graffle/mailbox_element.graffle
View file @
1de110dd
No preview for this file type
examples/CMakeLists.txt
View file @
1de110dd
...
@@ -45,7 +45,7 @@ add_core_example(custom_type custom_types_3)
...
@@ -45,7 +45,7 @@ add_core_example(custom_type custom_types_3)
# testing DSL
# testing DSL
add_example
(
testing ping_pong
)
add_example
(
testing ping_pong
)
target_link_libraries
(
ping_pong PRIVATE CAF::internal CAF::core CAF::test
)
target_link_libraries
(
ping_pong PRIVATE CAF::internal CAF::core CAF::test
)
add_test
(
NAME
"examples.ping-pong"
COMMAND ping_pong -r300 -n -v5
)
# -- examples for CAF::io ------------------------------------------------------
# -- examples for CAF::io ------------------------------------------------------
...
...
examples/aout.cpp
View file @
1de110dd
#include <random>
#include <chrono>
#include <chrono>
#include <cstdlib>
#include <cstdlib>
#include <iostream>
#include <iostream>
#include <random>
#include "caf/all.hpp"
#include "caf/actor_ostream.hpp"
#include "caf/actor_system.hpp"
#include "caf/caf_main.hpp"
#include "caf/event_based_actor.hpp"
using
namespace
caf
;
using
namespace
caf
;
using
std
::
endl
;
void
caf_main
(
actor_system
&
system
)
{
behavior
printer
(
event_based_actor
*
self
,
int32_t
num
,
int32_t
delay
)
{
for
(
int
i
=
1
;
i
<=
50
;
++
i
)
{
aout
(
self
)
<<
"Hi there! This is actor nr. "
<<
num
<<
"!"
<<
std
::
endl
;
system
.
spawn
([
i
](
blocking_actor
*
self
)
{
std
::
chrono
::
milliseconds
timeout
{
delay
};
aout
(
self
)
<<
"Hi there! This is actor nr. "
self
->
delayed_send
(
self
,
timeout
,
timeout_atom_v
);
<<
i
<<
"!"
<<
endl
;
return
{
[
=
](
timeout_atom
)
{
aout
(
self
)
<<
"Actor nr. "
<<
num
<<
" says goodbye after waiting for "
<<
delay
<<
"ms!"
<<
std
::
endl
;
},
};
}
void
caf_main
(
actor_system
&
sys
)
{
std
::
random_device
rd
;
std
::
random_device
rd
;
std
::
default_random_engine
re
(
rd
());
std
::
minstd_rand
re
(
rd
());
std
::
chrono
::
milliseconds
tout
{
re
()
%
10
};
std
::
uniform_int_distribution
<
int32_t
>
dis
{
1
,
99
};
self
->
delayed_send
(
self
,
tout
,
42
);
for
(
int32_t
i
=
1
;
i
<=
50
;
++
i
)
self
->
receive
(
sys
.
spawn
(
printer
,
i
,
dis
(
re
));
[
i
,
self
](
int
)
{
aout
(
self
)
<<
"Actor nr. "
<<
i
<<
" says goodbye!"
<<
endl
;
}
);
});
}
}
}
CAF_MAIN
()
CAF_MAIN
()
examples/broker/simple_broker.cpp
View file @
1de110dd
...
@@ -7,8 +7,6 @@
...
@@ -7,8 +7,6 @@
* - simple_broker -c localhost 4242 *
* - simple_broker -c localhost 4242 *
\******************************************************************************/
\******************************************************************************/
// Manual refs: 42-47 (Actors.tex)
#include "caf/config.hpp"
#include "caf/config.hpp"
#ifdef CAF_WINDOWS
#ifdef CAF_WINDOWS
...
@@ -38,12 +36,14 @@ using namespace caf::io;
...
@@ -38,12 +36,14 @@ using namespace caf::io;
namespace
{
namespace
{
// --(rst-attach-begin)--
// Utility function to print an exit message with custom name.
// Utility function to print an exit message with custom name.
void
print_on_exit
(
const
actor
&
hdl
,
const
std
::
string
&
name
)
{
void
print_on_exit
(
const
actor
&
hdl
,
const
std
::
string
&
name
)
{
hdl
->
attach_functor
([
=
](
const
error
&
reason
)
{
hdl
->
attach_functor
([
=
](
const
error
&
reason
)
{
cout
<<
name
<<
" exited: "
<<
to_string
(
reason
)
<<
endl
;
cout
<<
name
<<
" exited: "
<<
to_string
(
reason
)
<<
endl
;
});
});
}
}
// --(rst-attach-end)--
enum
class
op
:
uint8_t
{
enum
class
op
:
uint8_t
{
ping
,
ping
,
...
...
examples/custom_type/custom_types_1.cpp
View file @
1de110dd
// Showcases how to add custom POD message types.
// Showcases how to add custom POD message types.
// Manual refs: 24-27, 30-34, 75-78, 81-84 (ConfiguringActorApplications)
// 23-33 (TypeInspection)
#include <cassert>
#include <cassert>
#include <iostream>
#include <iostream>
#include <string>
#include <string>
...
...
examples/hello_world.cpp
View file @
1de110dd
#include <string>
#include <string>
#include <iostream>
#include <iostream>
#include "caf/a
ll
.hpp"
#include "caf/a
ctor_ostream
.hpp"
#include "caf/actor_system.hpp"
using
std
::
endl
;
#include "caf/caf_main.hpp"
using
std
::
string
;
#include "caf/event_based_actor.hpp"
using
namespace
caf
;
using
namespace
caf
;
...
@@ -13,32 +13,32 @@ behavior mirror(event_based_actor* self) {
...
@@ -13,32 +13,32 @@ behavior mirror(event_based_actor* self) {
return
{
return
{
// a handler for messages containing a single string
// a handler for messages containing a single string
// that replies with a string
// that replies with a string
[
=
](
const
st
ring
&
what
)
->
string
{
[
=
](
const
st
d
::
string
&
what
)
->
std
::
string
{
// prints "Hello World!" via aout (thread-safe cout wrapper)
// prints "Hello World!" via aout (thread-safe cout wrapper)
aout
(
self
)
<<
what
<<
endl
;
aout
(
self
)
<<
what
<<
std
::
endl
;
// reply "!dlroW olleH"
// reply "!dlroW olleH"
return
st
ring
(
what
.
rbegin
(),
what
.
rend
())
;
return
st
d
::
string
{
what
.
rbegin
(),
what
.
rend
()}
;
}
}
,
};
};
}
}
void
hello_world
(
event_based_actor
*
self
,
const
actor
&
buddy
)
{
void
hello_world
(
event_based_actor
*
self
,
const
actor
&
buddy
)
{
// send "Hello World!" to our buddy ...
// send "Hello World!" to our buddy ...
self
->
request
(
buddy
,
std
::
chrono
::
seconds
(
10
),
"Hello World!"
).
then
(
self
->
request
(
buddy
,
std
::
chrono
::
seconds
(
10
),
"Hello World!"
)
.
then
(
// ... wait up to 10s for a response ...
// ... wait up to 10s for a response ...
[
=
](
const
string
&
what
)
{
[
=
](
const
std
::
string
&
what
)
{
// ... and print it
// ... and print it
aout
(
self
)
<<
what
<<
endl
;
aout
(
self
)
<<
what
<<
std
::
endl
;
}
});
);
}
}
void
caf_main
(
actor_system
&
sys
tem
)
{
void
caf_main
(
actor_system
&
sys
)
{
// create a new actor that calls 'mirror()'
// create a new actor that calls 'mirror()'
auto
mirror_actor
=
sys
tem
.
spawn
(
mirror
);
auto
mirror_actor
=
sys
.
spawn
(
mirror
);
// create another actor that calls 'hello_world(mirror_actor)';
// create another actor that calls 'hello_world(mirror_actor)';
sys
tem
.
spawn
(
hello_world
,
mirror_actor
);
sys
.
spawn
(
hello_world
,
mirror_actor
);
//
system will wait until both actors are destroyed before leaving main
//
the system will wait until both actors are done before exiting the program
}
}
// creates a main function for us that calls our caf_main
// creates a main function for us that calls our caf_main
...
...
examples/message_passing/calculator.cpp
View file @
1de110dd
...
@@ -3,8 +3,6 @@
...
@@ -3,8 +3,6 @@
* for both the blocking and the event-based API. *
* for both the blocking and the event-based API. *
\******************************************************************************/
\******************************************************************************/
// Manual refs: lines 17-18, 21-26, 28-56, 58-92, 123-128 (Actor)
#include <iostream>
#include <iostream>
#include "caf/all.hpp"
#include "caf/all.hpp"
...
@@ -14,18 +12,22 @@ using namespace caf;
...
@@ -14,18 +12,22 @@ using namespace caf;
namespace
{
namespace
{
// --(rst-calculator-actor-begin)--
using
calculator_actor
using
calculator_actor
=
typed_actor
<
result
<
int32_t
>
(
add_atom
,
int32_t
,
int32_t
),
=
typed_actor
<
result
<
int32_t
>
(
add_atom
,
int32_t
,
int32_t
),
result
<
int32_t
>
(
sub_atom
,
int32_t
,
int32_t
)
>
;
result
<
int32_t
>
(
sub_atom
,
int32_t
,
int32_t
)
>
;
// --(rst-calculator-actor-end)--
//
prototypes and forward declarations
//
--(rst-prototypes-begin)--
behavior
calculator_fun
(
event_based_actor
*
self
);
behavior
calculator_fun
(
event_based_actor
*
self
);
void
blocking_calculator_fun
(
blocking_actor
*
self
);
void
blocking_calculator_fun
(
blocking_actor
*
self
);
calculator_actor
::
behavior_type
typed_calculator_fun
();
calculator_actor
::
behavior_type
typed_calculator_fun
();
class
calculator
;
class
calculator
;
class
blocking_calculator
;
class
blocking_calculator
;
class
typed_calculator
;
class
typed_calculator
;
// --(rst-prototypes-end)--
// --(rst-function-based-begin)--
// function-based, dynamically typed, event-based API
// function-based, dynamically typed, event-based API
behavior
calculator_fun
(
event_based_actor
*
)
{
behavior
calculator_fun
(
event_based_actor
*
)
{
return
{
return
{
...
@@ -55,7 +57,9 @@ calculator_actor::behavior_type typed_calculator_fun() {
...
@@ -55,7 +57,9 @@ calculator_actor::behavior_type typed_calculator_fun() {
[](
sub_atom
,
int32_t
a
,
int32_t
b
)
{
return
a
-
b
;
},
[](
sub_atom
,
int32_t
a
,
int32_t
b
)
{
return
a
-
b
;
},
};
};
}
}
// --(rst-function-based-end)--
// --(rst-class-based-begin)--
// class-based, dynamically typed, event-based API
// class-based, dynamically typed, event-based API
class
calculator
:
public
event_based_actor
{
class
calculator
:
public
event_based_actor
{
public:
public:
...
@@ -91,6 +95,7 @@ public:
...
@@ -91,6 +95,7 @@ public:
return
typed_calculator_fun
();
return
typed_calculator_fun
();
}
}
};
};
// --(rst-class-based-end)--
void
tester
(
scoped_actor
&
)
{
void
tester
(
scoped_actor
&
)
{
// end of recursion
// end of recursion
...
@@ -120,14 +125,16 @@ void tester(scoped_actor& self, const Handle& hdl, int32_t x, int32_t y,
...
@@ -120,14 +125,16 @@ void tester(scoped_actor& self, const Handle& hdl, int32_t x, int32_t y,
tester
(
self
,
std
::
forward
<
Ts
>
(
xs
)...);
tester
(
self
,
std
::
forward
<
Ts
>
(
xs
)...);
}
}
void
caf_main
(
actor_system
&
system
)
{
void
caf_main
(
actor_system
&
sys
)
{
auto
a1
=
system
.
spawn
(
blocking_calculator_fun
);
// --(rst-spawn-begin)--
auto
a2
=
system
.
spawn
(
calculator_fun
);
auto
a1
=
sys
.
spawn
(
blocking_calculator_fun
);
auto
a3
=
system
.
spawn
(
typed_calculator_fun
);
auto
a2
=
sys
.
spawn
(
calculator_fun
);
auto
a4
=
system
.
spawn
<
blocking_calculator
>
();
auto
a3
=
sys
.
spawn
(
typed_calculator_fun
);
auto
a5
=
system
.
spawn
<
calculator
>
();
auto
a4
=
sys
.
spawn
<
blocking_calculator
>
();
auto
a6
=
system
.
spawn
<
typed_calculator
>
();
auto
a5
=
sys
.
spawn
<
calculator
>
();
scoped_actor
self
{
system
};
auto
a6
=
sys
.
spawn
<
typed_calculator
>
();
// --(rst-spawn-end)--
scoped_actor
self
{
sys
};
tester
(
self
,
a1
,
1
,
2
,
a2
,
3
,
4
,
a3
,
5
,
6
,
a4
,
7
,
8
,
a5
,
9
,
10
,
a6
,
11
,
12
);
tester
(
self
,
a1
,
1
,
2
,
a2
,
3
,
4
,
a3
,
5
,
6
,
a4
,
7
,
8
,
a5
,
9
,
10
,
a6
,
11
,
12
);
self
->
send_exit
(
a1
,
exit_reason
::
user_shutdown
);
self
->
send_exit
(
a1
,
exit_reason
::
user_shutdown
);
self
->
send_exit
(
a4
,
exit_reason
::
user_shutdown
);
self
->
send_exit
(
a4
,
exit_reason
::
user_shutdown
);
...
...
examples/message_passing/cell.cpp
View file @
1de110dd
...
@@ -40,7 +40,7 @@ behavior unchecked_cell(stateful_actor<cell_state>* self) {
...
@@ -40,7 +40,7 @@ behavior unchecked_cell(stateful_actor<cell_state>* self) {
// --(rst-cell-end)--
// --(rst-cell-end)--
void
caf_main
(
actor_system
&
system
)
{
void
caf_main
(
actor_system
&
system
)
{
// --(rst-spawn-cell-
end
)--
// --(rst-spawn-cell-
begin
)--
// Create one cell for each implementation.
// Create one cell for each implementation.
auto
cell1
=
system
.
spawn
(
type_checked_cell
);
auto
cell1
=
system
.
spawn
(
type_checked_cell
);
auto
cell2
=
system
.
spawn
(
unchecked_cell
);
auto
cell2
=
system
.
spawn
(
unchecked_cell
);
...
@@ -50,7 +50,7 @@ void caf_main(actor_system& system) {
...
@@ -50,7 +50,7 @@ void caf_main(actor_system& system) {
f
(
put_atom_v
,
20
);
f
(
put_atom_v
,
20
);
cout
<<
"cell value (after setting to 20): "
<<
f
(
get_atom_v
)
<<
endl
;
cout
<<
"cell value (after setting to 20): "
<<
f
(
get_atom_v
)
<<
endl
;
// Get an unchecked cell and send it some garbage. Triggers an "unexpected
// Get an unchecked cell and send it some garbage. Triggers an "unexpected
// message" error.
// message" error
(and terminates cell2!)
.
anon_send
(
cell2
,
"hello there!"
);
anon_send
(
cell2
,
"hello there!"
);
}
}
...
...
examples/message_passing/dancing_kirby.cpp
View file @
1de110dd
...
@@ -55,11 +55,14 @@ void draw_kirby(const animation_step& animation) {
...
@@ -55,11 +55,14 @@ void draw_kirby(const animation_step& animation) {
cout
.
flush
();
cout
.
flush
();
}
}
// --(rst-delayed-send-begin)--
// uses a message-based loop to iterate over all animation steps
// uses a message-based loop to iterate over all animation steps
void
dancing_kirby
(
event_based_actor
*
self
)
{
behavior
dancing_kirby
(
event_based_actor
*
self
)
{
using
namespace
std
::
literals
::
chrono_literals
;
// let's get it started
// let's get it started
self
->
send
(
self
,
update_atom_v
,
size_t
{
0
});
self
->
send
(
self
,
update_atom_v
,
size_t
{
0
});
self
->
become
([
=
](
update_atom
,
size_t
step
)
{
return
{
[
=
](
update_atom
,
size_t
step
)
{
if
(
step
==
sizeof
(
animation_step
))
{
if
(
step
==
sizeof
(
animation_step
))
{
// we've printed all animation steps (done)
// we've printed all animation steps (done)
cout
<<
endl
;
cout
<<
endl
;
...
@@ -68,11 +71,12 @@ void dancing_kirby(event_based_actor* self) {
...
@@ -68,11 +71,12 @@ void dancing_kirby(event_based_actor* self) {
}
}
// print given step
// print given step
draw_kirby
(
animation_steps
[
step
]);
draw_kirby
(
animation_steps
[
step
]);
// animate next step in 150ms
// schedule next animation step
self
->
delayed_send
(
self
,
std
::
chrono
::
milliseconds
(
150
),
update_atom_v
,
self
->
delayed_send
(
self
,
150ms
,
update_atom_v
,
step
+
1
);
step
+
1
);
},
}
)
;
};
}
}
// --(rst-delayed-send-end)--
void
caf_main
(
actor_system
&
system
)
{
void
caf_main
(
actor_system
&
system
)
{
system
.
spawn
(
dancing_kirby
);
system
.
spawn
(
dancing_kirby
);
...
...
examples/message_passing/delegating.cpp
View file @
1de110dd
#include <iostream>
#include "caf/all.hpp"
#include "caf/all.hpp"
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 15-36 (MessagePassing.tex)
using
namespace
caf
;
using
namespace
caf
;
// using add_atom = atom_constant<atom("add")>; (defined in atom.hpp)
// --(rst-delegate-begin)--
using
adder_actor
=
typed_actor
<
result
<
int32_t
>
(
add_atom
,
int32_t
,
int32_t
)
>
;
using
calc
=
typed_actor
<
result
<
int32_t
>
(
add_atom
,
int32_t
,
int32_t
)
>
;
void
actor_a
(
event_based_actor
*
self
,
const
calc
&
worker
)
{
adder_actor
::
behavior_type
worker_impl
()
{
self
->
request
(
worker
,
std
::
chrono
::
seconds
(
10
),
add_atom_v
,
1
,
2
)
return
{
.
then
([
=
](
int32_t
result
)
{
//
[](
add_atom
,
int32_t
x
,
int32_t
y
)
{
return
x
+
y
;
},
aout
(
self
)
<<
"1 + 2 = "
<<
result
<<
std
::
endl
;
};
});
}
}
adder_actor
::
behavior_type
server_impl
(
adder_actor
::
pointer
self
,
calc
::
behavior_type
actor_b
(
calc
::
pointer
self
,
const
calc
&
worker
)
{
adder_actor
worker
)
{
return
{
return
{
[
=
](
add_atom
add
,
int32_t
x
,
int32_t
y
)
{
[
=
](
add_atom
add
,
int32_t
x
,
int32_t
y
)
{
return
self
->
delegate
(
worker
,
add
,
x
,
y
);
return
self
->
delegate
(
worker
,
add
,
x
,
y
);
...
@@ -27,14 +19,19 @@ calc::behavior_type actor_b(calc::pointer self, const calc& worker) {
...
@@ -27,14 +19,19 @@ calc::behavior_type actor_b(calc::pointer self, const calc& worker) {
};
};
}
}
calc
::
behavior_type
actor_c
()
{
void
client_impl
(
event_based_actor
*
self
,
adder_actor
adder
,
int32_t
x
,
return
{
int32_t
y
)
{
[](
add_atom
,
int32_t
x
,
int32_t
y
)
{
return
x
+
y
;
},
using
namespace
std
::
literals
::
chrono_literals
;
};
self
->
request
(
adder
,
10s
,
add_atom_v
,
x
,
y
).
then
([
=
](
int32_t
result
)
{
aout
(
self
)
<<
x
<<
" + "
<<
y
<<
" = "
<<
result
<<
std
::
endl
;
});
}
}
void
caf_main
(
actor_system
&
system
)
{
void
caf_main
(
actor_system
&
sys
)
{
system
.
spawn
(
actor_a
,
system
.
spawn
(
actor_b
,
system
.
spawn
(
actor_c
)));
auto
worker
=
sys
.
spawn
(
worker_impl
);
auto
server
=
sys
.
spawn
(
server_impl
,
sys
.
spawn
(
worker_impl
));
sys
.
spawn
(
client_impl
,
server
,
1
,
2
);
}
}
// --(rst-delegate-end)--
CAF_MAIN
()
CAF_MAIN
()
examples/message_passing/divider.cpp
View file @
1de110dd
...
@@ -2,18 +2,11 @@
...
@@ -2,18 +2,11 @@
* A very basic, interactive divider. *
* A very basic, interactive divider. *
\******************************************************************************/
\******************************************************************************/
// Manual refs: 17-19, 49-59, 70-76 (MessagePassing);
// 17-47 (Error)
#include <iostream>
#include <iostream>
#include "caf/all.hpp"
#include "caf/all.hpp"
using
std
::
cout
;
// --(rst-math-error-begin)--
using
std
::
endl
;
using
std
::
flush
;
using
namespace
caf
;
enum
class
math_error
:
uint8_t
{
enum
class
math_error
:
uint8_t
{
division_by_zero
=
1
,
division_by_zero
=
1
,
};
};
...
@@ -27,6 +20,29 @@ std::string to_string(math_error x) {
...
@@ -27,6 +20,29 @@ std::string to_string(math_error x) {
}
}
}
}
bool
from_string
(
caf
::
string_view
in
,
math_error
&
out
)
{
if
(
in
==
"division_by_zero"
)
{
out
=
math_error
::
division_by_zero
;
return
true
;
}
else
{
return
false
;
}
}
bool
from_integer
(
uint8_t
in
,
math_error
&
out
)
{
if
(
in
==
1
)
{
out
=
math_error
::
division_by_zero
;
return
true
;
}
else
{
return
false
;
}
}
template
<
class
Inspector
>
bool
inspect
(
Inspector
&
f
,
math_error
&
x
)
{
return
caf
::
default_enum_inspect
(
f
,
x
);
}
CAF_BEGIN_TYPE_ID_BLOCK
(
divider
,
first_custom_type_id
)
CAF_BEGIN_TYPE_ID_BLOCK
(
divider
,
first_custom_type_id
)
CAF_ADD_TYPE_ID
(
divider
,
(
math_error
))
CAF_ADD_TYPE_ID
(
divider
,
(
math_error
))
...
@@ -34,7 +50,15 @@ CAF_BEGIN_TYPE_ID_BLOCK(divider, first_custom_type_id)
...
@@ -34,7 +50,15 @@ CAF_BEGIN_TYPE_ID_BLOCK(divider, first_custom_type_id)
CAF_END_TYPE_ID_BLOCK
(
divider
)
CAF_END_TYPE_ID_BLOCK
(
divider
)
CAF_ERROR_CODE_ENUM
(
math_error
)
CAF_ERROR_CODE_ENUM
(
math_error
)
// --(rst-math-error-end)--
using
std
::
cout
;
using
std
::
endl
;
using
std
::
flush
;
using
namespace
caf
;
// --(rst-divider-begin)--
using
divider
=
typed_actor
<
result
<
double
>
(
div_atom
,
double
,
double
)
>
;
using
divider
=
typed_actor
<
result
<
double
>
(
div_atom
,
double
,
double
)
>
;
divider
::
behavior_type
divider_impl
()
{
divider
::
behavior_type
divider_impl
()
{
...
@@ -46,6 +70,7 @@ divider::behavior_type divider_impl() {
...
@@ -46,6 +70,7 @@ divider::behavior_type divider_impl() {
},
},
};
};
}
}
// --(rst-divider-end)--
void
caf_main
(
actor_system
&
system
)
{
void
caf_main
(
actor_system
&
system
)
{
double
x
;
double
x
;
...
@@ -54,6 +79,7 @@ void caf_main(actor_system& system) {
...
@@ -54,6 +79,7 @@ void caf_main(actor_system& system) {
std
::
cin
>>
x
;
std
::
cin
>>
x
;
cout
<<
"y: "
<<
flush
;
cout
<<
"y: "
<<
flush
;
std
::
cin
>>
y
;
std
::
cin
>>
y
;
// --(rst-request-begin)--
auto
div
=
system
.
spawn
(
divider_impl
);
auto
div
=
system
.
spawn
(
divider_impl
);
scoped_actor
self
{
system
};
scoped_actor
self
{
system
};
self
->
request
(
div
,
std
::
chrono
::
seconds
(
10
),
div_atom_v
,
x
,
y
)
self
->
request
(
div
,
std
::
chrono
::
seconds
(
10
),
div_atom_v
,
x
,
y
)
...
@@ -63,6 +89,7 @@ void caf_main(actor_system& system) {
...
@@ -63,6 +89,7 @@ void caf_main(actor_system& system) {
aout
(
self
)
<<
"*** cannot compute "
<<
x
<<
" / "
<<
y
<<
" => "
aout
(
self
)
<<
"*** cannot compute "
<<
x
<<
" / "
<<
y
<<
" => "
<<
to_string
(
err
)
<<
endl
;
<<
to_string
(
err
)
<<
endl
;
});
});
// --(rst-request-end)--
}
}
CAF_MAIN
(
id_block
::
divider
)
CAF_MAIN
(
id_block
::
divider
)
examples/message_passing/fan_out_request.cpp
View file @
1de110dd
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 86-98 (MessagePassing.tex)
#include <cassert>
#include <cassert>
#include <chrono>
#include <chrono>
#include <iomanip>
#include <iomanip>
...
@@ -101,6 +97,7 @@ matrix::behavior_type matrix_actor(matrix::stateful_pointer<matrix_state> self,
...
@@ -101,6 +97,7 @@ matrix::behavior_type matrix_actor(matrix::stateful_pointer<matrix_state> self,
[
=
](
error
&
err
)
mutable
{
rp
.
deliver
(
std
::
move
(
err
));
});
[
=
](
error
&
err
)
mutable
{
rp
.
deliver
(
std
::
move
(
err
));
});
return
rp
;
return
rp
;
},
},
// --(rst-fan-out-begin)--
[
=
](
get_atom
get
,
average_atom
,
column_atom
,
int
column
)
{
[
=
](
get_atom
get
,
average_atom
,
column_atom
,
int
column
)
{
assert
(
column
<
columns
);
assert
(
column
<
columns
);
std
::
vector
<
cell
>
columns
;
std
::
vector
<
cell
>
columns
;
...
@@ -118,6 +115,7 @@ matrix::behavior_type matrix_actor(matrix::stateful_pointer<matrix_state> self,
...
@@ -118,6 +115,7 @@ matrix::behavior_type matrix_actor(matrix::stateful_pointer<matrix_state> self,
[
=
](
error
&
err
)
mutable
{
rp
.
deliver
(
std
::
move
(
err
));
});
[
=
](
error
&
err
)
mutable
{
rp
.
deliver
(
std
::
move
(
err
));
});
return
rp
;
return
rp
;
},
},
// --(rst-fan-out-end)--
};
};
}
}
...
...
examples/message_passing/fixed_stack.cpp
View file @
1de110dd
...
@@ -19,6 +19,43 @@ CAF_END_TYPE_ID_BLOCK(fixed_stack)
...
@@ -19,6 +19,43 @@ CAF_END_TYPE_ID_BLOCK(fixed_stack)
CAF_ERROR_CODE_ENUM
(
fixed_stack_errc
)
CAF_ERROR_CODE_ENUM
(
fixed_stack_errc
)
std
::
string
to_string
(
fixed_stack_errc
x
)
{
switch
(
x
)
{
case
fixed_stack_errc
:
:
push_to_full
:
return
"push_to_full"
;
case
fixed_stack_errc
:
:
pop_from_empty
:
return
"pop_from_empty"
;
default:
return
"-unknown-error-"
;
}
}
bool
from_string
(
caf
::
string_view
in
,
fixed_stack_errc
&
out
)
{
if
(
in
==
"push_to_full"
)
{
out
=
fixed_stack_errc
::
push_to_full
;
return
true
;
}
else
if
(
in
==
"pop_from_empty"
)
{
out
=
fixed_stack_errc
::
pop_from_empty
;
return
true
;
}
else
{
return
false
;
}
}
bool
from_integer
(
uint8_t
in
,
fixed_stack_errc
&
out
)
{
if
(
in
>
0
&&
in
<
1
)
{
out
=
static_cast
<
fixed_stack_errc
>
(
in
);
return
true
;
}
else
{
return
false
;
}
}
template
<
class
Inspector
>
bool
inspect
(
Inspector
&
f
,
fixed_stack_errc
&
x
)
{
return
caf
::
default_enum_inspect
(
f
,
x
);
}
using
std
::
endl
;
using
std
::
endl
;
using
namespace
caf
;
using
namespace
caf
;
...
...
examples/message_passing/promises.cpp
View file @
1de110dd
/******************************************************************************\
* Illustrates response promises. *
\******************************************************************************/
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 18-43 (MessagePassing.tex)
#include <iostream>
#include "caf/all.hpp"
#include "caf/all.hpp"
using
std
::
cout
;
using
std
::
endl
;
using
namespace
caf
;
using
namespace
caf
;
using
adder
=
typed_actor
<
result
<
int32_t
>
(
add_atom
,
int32_t
,
int32_t
)
>
;
// --(rst-promise-begin)--
using
adder_actor
=
typed_actor
<
result
<
int32_t
>
(
add_atom
,
int32_t
,
int32_t
)
>
;
// function-based, statically typed, event-based API
adder_actor
::
behavior_type
worker_impl
()
{
adder
::
behavior_type
worker
()
{
return
{
return
{
[](
add_atom
,
int32_t
a
,
int32_t
b
)
{
return
a
+
b
;
},
[](
add_atom
,
int32_t
x
,
int32_t
y
)
{
return
x
+
y
;
},
};
};
}
}
adder_actor
::
behavior_type
server_impl
(
adder_actor
::
pointer
self
,
// function-based, statically typed, event-based API
adder_actor
worker
)
{
adder
::
behavior_type
calculator_master
(
adder
::
pointer
self
)
{
auto
w
=
self
->
spawn
(
worker
);
return
{
return
{
[
=
](
add_atom
x
,
int32_t
y
,
int32_t
z
)
->
result
<
int32_t
>
{
[
=
](
add_atom
,
int32_t
y
,
int32_t
z
)
{
auto
rp
=
self
->
make_response_promise
<
int32_t
>
();
auto
rp
=
self
->
make_response_promise
<
int32_t
>
();
self
->
request
(
w
,
infinite
,
x
,
y
,
z
).
then
([
=
](
int32_t
result
)
mutable
{
self
->
request
(
worker
,
infinite
,
add_atom_v
,
y
,
z
)
rp
.
deliver
(
result
);
.
then
([
rp
](
int32_t
result
)
mutable
{
rp
.
deliver
(
result
);
});
});
return
rp
;
return
rp
;
},
},
};
};
}
}
void
caf_main
(
actor_system
&
system
)
{
void
client_impl
(
event_based_actor
*
self
,
adder_actor
adder
,
int32_t
x
,
auto
f
=
make_function_view
(
system
.
spawn
(
calculator_master
));
int32_t
y
)
{
cout
<<
"12 + 13 = "
<<
f
(
add_atom_v
,
12
,
13
)
<<
endl
;
using
namespace
std
::
literals
::
chrono_literals
;
self
->
request
(
adder
,
10s
,
add_atom_v
,
x
,
y
).
then
([
=
](
int32_t
result
)
{
aout
(
self
)
<<
x
<<
" + "
<<
y
<<
" = "
<<
result
<<
std
::
endl
;
});
}
void
caf_main
(
actor_system
&
sys
)
{
auto
worker
=
sys
.
spawn
(
worker_impl
);
auto
server
=
sys
.
spawn
(
server_impl
,
sys
.
spawn
(
worker_impl
));
sys
.
spawn
(
client_impl
,
server
,
1
,
2
);
}
}
// --(rst-promise-end)--
CAF_MAIN
()
CAF_MAIN
()
examples/message_passing/request.cpp
View file @
1de110dd
...
@@ -2,10 +2,6 @@
...
@@ -2,10 +2,6 @@
* Illustrates semantics of request().{then|await|receive}. *
* Illustrates semantics of request().{then|await|receive}. *
\******************************************************************************/
\******************************************************************************/
// This file is partially included in the manual, do not modify
// without updating the references in the *.tex files!
// Manual references: lines 20-37, 39-51, 53-64, 67-69 (MessagePassing.tex)
#include <chrono>
#include <chrono>
#include <cstdint>
#include <cstdint>
#include <iostream>
#include <iostream>
...
@@ -18,22 +14,38 @@ using std::vector;
...
@@ -18,22 +14,38 @@ using std::vector;
using
std
::
chrono
::
seconds
;
using
std
::
chrono
::
seconds
;
using
namespace
caf
;
using
namespace
caf
;
// --(rst-cell-begin)--
using
cell
using
cell
=
typed_actor
<
result
<
void
>
(
put_atom
,
int32_t
),
result
<
int32_t
>
(
get_atom
)
>
;
=
typed_actor
<
result
<
void
>
(
put_atom
,
int32_t
),
// 'put' writes to the cell
result
<
int32_t
>
(
get_atom
)
>
;
// 'get 'reads from the cell
struct
cell_state
{
struct
cell_state
{
int32_t
value
=
0
;
static
constexpr
inline
const
char
*
name
=
"cell"
;
};
cell
::
pointer
self
;
int32_t
value
;
cell_state
(
cell
::
pointer
ptr
,
int32_t
val
)
:
self
(
ptr
),
value
(
val
)
{
// nop
}
cell
::
behavior_type
cell_impl
(
cell
::
stateful_pointer
<
cell_state
>
self
,
cell_state
(
const
cell_state
&
)
=
delete
;
int32_t
x0
)
{
self
->
state
.
value
=
x0
;
cell_state
&
operator
=
(
const
cell_state
&
)
=
delete
;
cell
::
behavior_type
make_behavior
()
{
return
{
return
{
[
=
](
put_atom
,
int32_t
val
)
{
self
->
state
.
value
=
val
;
},
[
=
](
put_atom
,
int32_t
val
)
{
value
=
val
;
},
[
=
](
get_atom
)
{
return
self
->
state
.
value
;
},
[
=
](
get_atom
)
{
return
value
;
},
};
};
}
}
};
using
cell_impl
=
cell
::
stateful_impl
<
cell_state
>
;
// --(rst-cell-end)--
// --(rst-testees-begin)--
void
waiting_testee
(
event_based_actor
*
self
,
vector
<
cell
>
cells
)
{
void
waiting_testee
(
event_based_actor
*
self
,
vector
<
cell
>
cells
)
{
for
(
auto
&
x
:
cells
)
for
(
auto
&
x
:
cells
)
self
->
request
(
x
,
seconds
(
1
),
get_atom_v
).
await
([
=
](
int32_t
y
)
{
self
->
request
(
x
,
seconds
(
1
),
get_atom_v
).
await
([
=
](
int32_t
y
)
{
...
@@ -59,11 +71,13 @@ void blocking_testee(blocking_actor* self, vector<cell> cells) {
...
@@ -59,11 +71,13 @@ void blocking_testee(blocking_actor* self, vector<cell> cells) {
aout
(
self
)
<<
"cell #"
<<
x
.
id
()
<<
" -> "
<<
to_string
(
err
)
<<
endl
;
aout
(
self
)
<<
"cell #"
<<
x
.
id
()
<<
" -> "
<<
to_string
(
err
)
<<
endl
;
});
});
}
}
// --(rst-testees-end)--
// --(rst-main-begin)--
void
caf_main
(
actor_system
&
system
)
{
void
caf_main
(
actor_system
&
system
)
{
vector
<
cell
>
cells
;
vector
<
cell
>
cells
;
for
(
auto
i
=
0
;
i
<
5
;
++
i
)
for
(
int32_t
i
=
0
;
i
<
5
;
++
i
)
cells
.
emplace_back
(
system
.
spawn
(
cell_impl
,
i
*
i
));
cells
.
emplace_back
(
system
.
spawn
<
cell_impl
>
(
i
*
i
));
scoped_actor
self
{
system
};
scoped_actor
self
{
system
};
aout
(
self
)
<<
"waiting_testee"
<<
endl
;
aout
(
self
)
<<
"waiting_testee"
<<
endl
;
auto
x1
=
self
->
spawn
(
waiting_testee
,
cells
);
auto
x1
=
self
->
spawn
(
waiting_testee
,
cells
);
...
@@ -74,5 +88,6 @@ void caf_main(actor_system& system) {
...
@@ -74,5 +88,6 @@ void caf_main(actor_system& system) {
aout
(
self
)
<<
"blocking_testee"
<<
endl
;
aout
(
self
)
<<
"blocking_testee"
<<
endl
;
system
.
spawn
(
blocking_testee
,
cells
);
system
.
spawn
(
blocking_testee
,
cells
);
}
}
// --(rst-main-end)--
CAF_MAIN
()
CAF_MAIN
()
examples/remoting/distributed_calculator.cpp
View file @
1de110dd
...
@@ -203,6 +203,7 @@ optional<int> toint(const string& str) {
...
@@ -203,6 +203,7 @@ optional<int> toint(const string& str) {
return
none
;
return
none
;
}
}
// --(rst-config-begin)--
class
config
:
public
actor_system_config
{
class
config
:
public
actor_system_config
{
public:
public:
uint16_t
port
=
0
;
uint16_t
port
=
0
;
...
@@ -216,6 +217,7 @@ public:
...
@@ -216,6 +217,7 @@ public:
.
add
(
server_mode
,
"server-mode,s"
,
"enable server mode"
);
.
add
(
server_mode
,
"server-mode,s"
,
"enable server mode"
);
}
}
};
};
// --(rst-config-end)--
void
client_repl
(
actor_system
&
system
,
const
config
&
cfg
)
{
void
client_repl
(
actor_system
&
system
,
const
config
&
cfg
)
{
// keeps track of requests and tries to reconnect on server failures
// keeps track of requests and tries to reconnect on server failures
...
...
examples/testing/ping_pong.cpp
View file @
1de110dd
// Manual refs: lines 12-60 (Testing)
#define CAF_SUITE ping_pong
#define CAF_SUITE ping_pong
#include "caf/test/dsl.hpp"
#include "caf/test/dsl.hpp"
...
@@ -9,6 +7,7 @@
...
@@ -9,6 +7,7 @@
using
namespace
caf
;
using
namespace
caf
;
// --(rst-ping-pong-begin)--
namespace
{
namespace
{
behavior
ping
(
event_based_actor
*
self
,
actor
pong_actor
,
int
n
)
{
behavior
ping
(
event_based_actor
*
self
,
actor
pong_actor
,
int
n
)
{
...
@@ -58,3 +57,4 @@ CAF_TEST(three pings) {
...
@@ -58,3 +57,4 @@ CAF_TEST(three pings) {
}
}
CAF_TEST_FIXTURE_SCOPE_END
()
CAF_TEST_FIXTURE_SCOPE_END
()
// --(rst-ping-pong-end)--
libcaf_core/CMakeLists.txt
View file @
1de110dd
...
@@ -308,6 +308,7 @@ caf_add_component(
...
@@ -308,6 +308,7 @@ caf_add_component(
policy.select_all
policy.select_all
policy.select_any
policy.select_any
request_timeout
request_timeout
response_promise
result
result
save_inspector
save_inspector
selective_streaming
selective_streaming
...
...
libcaf_core/caf/all.hpp
View file @
1de110dd
...
@@ -32,6 +32,7 @@
...
@@ -32,6 +32,7 @@
#include "caf/blocking_actor.hpp"
#include "caf/blocking_actor.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/byte_buffer.hpp"
#include "caf/byte_span.hpp"
#include "caf/byte_span.hpp"
#include "caf/caf_main.hpp"
#include "caf/config_option.hpp"
#include "caf/config_option.hpp"
#include "caf/config_option_adder.hpp"
#include "caf/config_option_adder.hpp"
#include "caf/config_value.hpp"
#include "caf/config_value.hpp"
...
...
libcaf_core/caf/byte.hpp
View file @
1de110dd
...
@@ -5,8 +5,6 @@
...
@@ -5,8 +5,6 @@
#include <cstdint>
#include <cstdint>
#include <type_traits>
#include <type_traits>
#include "caf/detail/type_traits.hpp"
#pragma once
#pragma once
namespace
caf
{
namespace
caf
{
...
@@ -15,31 +13,31 @@ namespace caf {
...
@@ -15,31 +13,31 @@ namespace caf {
enum
class
byte
:
uint8_t
{};
enum
class
byte
:
uint8_t
{};
template
<
class
IntegerType
,
template
<
class
IntegerType
,
class
=
detail
::
enable_if_tt
<
std
::
is_integral
<
IntegerType
>
>>
class
=
std
::
enable_if_t
<
std
::
is_integral
<
IntegerType
>
::
value
>>
constexpr
IntegerType
to_integer
(
byte
x
)
noexcept
{
constexpr
IntegerType
to_integer
(
byte
x
)
noexcept
{
return
static_cast
<
IntegerType
>
(
x
);
return
static_cast
<
IntegerType
>
(
x
);
}
}
template
<
class
IntegerType
,
template
<
class
IntegerType
,
class
E
=
detail
::
enable_if_tt
<
std
::
is_integral
<
IntegerType
>
>>
class
E
=
std
::
enable_if_t
<
std
::
is_integral
<
IntegerType
>
::
value
>>
constexpr
byte
&
operator
<<=
(
byte
&
x
,
IntegerType
shift
)
noexcept
{
constexpr
byte
&
operator
<<=
(
byte
&
x
,
IntegerType
shift
)
noexcept
{
return
x
=
static_cast
<
byte
>
(
to_integer
<
uint8_t
>
(
x
)
<<
shift
);
return
x
=
static_cast
<
byte
>
(
to_integer
<
uint8_t
>
(
x
)
<<
shift
);
}
}
template
<
class
IntegerType
,
template
<
class
IntegerType
,
class
E
=
detail
::
enable_if_tt
<
std
::
is_integral
<
IntegerType
>
>>
class
E
=
std
::
enable_if_t
<
std
::
is_integral
<
IntegerType
>
::
value
>>
constexpr
byte
operator
<<
(
byte
x
,
IntegerType
shift
)
noexcept
{
constexpr
byte
operator
<<
(
byte
x
,
IntegerType
shift
)
noexcept
{
return
static_cast
<
byte
>
(
to_integer
<
uint8_t
>
(
x
)
<<
shift
);
return
static_cast
<
byte
>
(
to_integer
<
uint8_t
>
(
x
)
<<
shift
);
}
}
template
<
class
IntegerType
,
template
<
class
IntegerType
,
class
E
=
detail
::
enable_if_tt
<
std
::
is_integral
<
IntegerType
>
>>
class
E
=
std
::
enable_if_t
<
std
::
is_integral
<
IntegerType
>
::
value
>>
constexpr
byte
&
operator
>>=
(
byte
&
x
,
IntegerType
shift
)
noexcept
{
constexpr
byte
&
operator
>>=
(
byte
&
x
,
IntegerType
shift
)
noexcept
{
return
x
=
static_cast
<
byte
>
(
to_integer
<
uint8_t
>
(
x
)
>>
shift
);
return
x
=
static_cast
<
byte
>
(
to_integer
<
uint8_t
>
(
x
)
>>
shift
);
}
}
template
<
class
IntegerType
,
template
<
class
IntegerType
,
class
E
=
detail
::
enable_if_tt
<
std
::
is_integral
<
IntegerType
>
>>
class
E
=
std
::
enable_if_t
<
std
::
is_integral
<
IntegerType
>
::
value
>>
constexpr
byte
operator
>>
(
byte
x
,
IntegerType
shift
)
noexcept
{
constexpr
byte
operator
>>
(
byte
x
,
IntegerType
shift
)
noexcept
{
return
static_cast
<
byte
>
(
static_cast
<
unsigned
char
>
(
x
)
>>
shift
);
return
static_cast
<
byte
>
(
static_cast
<
unsigned
char
>
(
x
)
>>
shift
);
}
}
...
...
libcaf_core/caf/caf_main.hpp
0 → 100644
View file @
1de110dd
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| 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
// For historic reasons, CAF_MAIN is implemented in exec_main.hpp. Eventually
// the implementation of the macro should move here.
#include "caf/exec_main.hpp"
libcaf_core/caf/config_value.hpp
View file @
1de110dd
...
@@ -64,7 +64,7 @@ CAF_ADD_CONFIG_VALUE_TYPE(dictionary<config_value>);
...
@@ -64,7 +64,7 @@ CAF_ADD_CONFIG_VALUE_TYPE(dictionary<config_value>);
template
<
class
T
>
template
<
class
T
>
constexpr
bool
is_config_value_type_v
=
is_config_value_type
<
T
>::
value
;
constexpr
bool
is_config_value_type_v
=
is_config_value_type
<
T
>::
value
;
}
;
// namespace caf::detail
}
// namespace caf::detail
namespace
caf
{
namespace
caf
{
...
...
libcaf_core/caf/detail/message_data.hpp
View file @
1de110dd
...
@@ -6,6 +6,7 @@
...
@@ -6,6 +6,7 @@
#include <atomic>
#include <atomic>
#include <cstdlib>
#include <cstdlib>
#include <new>
#include "caf/byte.hpp"
#include "caf/byte.hpp"
#include "caf/config.hpp"
#include "caf/config.hpp"
...
@@ -46,6 +47,8 @@ public:
...
@@ -46,6 +47,8 @@ public:
message_data
*
copy
()
const
;
message_data
*
copy
()
const
;
static
intrusive_ptr
<
message_data
>
make_uninitialized
(
type_id_list
types
);
// -- reference counting -----------------------------------------------------
// -- reference counting -----------------------------------------------------
/// Increases reference count by one.
/// Increases reference count by one.
...
@@ -110,9 +113,53 @@ public:
...
@@ -110,9 +113,53 @@ public:
init_impl
(
storage
(),
std
::
forward
<
Ts
>
(
xs
)...);
init_impl
(
storage
(),
std
::
forward
<
Ts
>
(
xs
)...);
}
}
byte
*
stepwise_init
(
byte
*
pos
)
{
return
pos
;
}
template
<
class
T
,
class
...
Ts
>
byte
*
stepwise_init
(
byte
*
pos
,
T
&&
x
,
Ts
&&
...
xs
)
{
using
type
=
strip_and_convert_t
<
T
>
;
new
(
pos
)
type
(
std
::
forward
<
T
>
(
x
));
++
constructed_elements_
;
return
stepwise_init
(
pos
+
padded_size_v
<
type
>
,
std
::
forward
<
Ts
>
(
xs
)...);
}
byte
*
stepwise_init_from
(
byte
*
pos
,
const
message
&
msg
);
byte
*
stepwise_init_from
(
byte
*
pos
,
const
message_data
*
other
);
template
<
class
Tuple
,
size_t
...
Is
>
byte
*
stepwise_init_from
(
byte
*
pos
,
Tuple
&&
tup
,
std
::
index_sequence
<
Is
...
>
)
{
return
stepwise_init
(
pos
,
std
::
get
<
Is
>
(
std
::
forward
<
Tuple
>
(
tup
))...);
}
template
<
class
...
Ts
>
byte
*
stepwise_init_from
(
byte
*
pos
,
std
::
tuple
<
Ts
...
>&&
tup
)
{
return
stepwise_init_from
(
pos
,
std
::
move
(
tup
),
std
::
make_index_sequence
<
sizeof
...(
Ts
)
>
{});
}
template
<
class
...
Ts
>
byte
*
stepwise_init_from
(
byte
*
pos
,
std
::
tuple
<
Ts
...
>&
tup
)
{
return
stepwise_init_from
(
pos
,
tup
,
std
::
make_index_sequence
<
sizeof
...(
Ts
)
>
{});
}
template
<
class
...
Ts
>
byte
*
stepwise_init_from
(
byte
*
pos
,
const
std
::
tuple
<
Ts
...
>&
tup
)
{
return
stepwise_init_from
(
pos
,
tup
,
std
::
make_index_sequence
<
sizeof
...(
Ts
)
>
{});
}
template
<
class
...
Ts
>
void
init_from
(
Ts
&&
...
xs
)
{
init_from_impl
(
storage
(),
std
::
forward
<
Ts
>
(
xs
)...);
}
private:
private:
void
init_impl
(
byte
*
)
{
void
init_impl
(
byte
*
)
{
//
nop
//
End of recursion.
}
}
template
<
class
T
,
class
...
Ts
>
template
<
class
T
,
class
...
Ts
>
...
@@ -123,6 +170,16 @@ private:
...
@@ -123,6 +170,16 @@ private:
init_impl
(
storage
+
padded_size_v
<
type
>
,
std
::
forward
<
Ts
>
(
xs
)...);
init_impl
(
storage
+
padded_size_v
<
type
>
,
std
::
forward
<
Ts
>
(
xs
)...);
}
}
void
init_from_impl
(
byte
*
)
{
// End of recursion.
}
template
<
class
T
,
class
...
Ts
>
void
init_from_impl
(
byte
*
pos
,
T
&&
x
,
Ts
&&
...
xs
)
{
init_from_impl
(
stepwise_init_from
(
pos
,
std
::
forward
<
T
>
(
x
)),
std
::
forward
<
Ts
>
(
xs
)...);
}
mutable
std
::
atomic
<
size_t
>
rc_
;
mutable
std
::
atomic
<
size_t
>
rc_
;
type_id_list
types_
;
type_id_list
types_
;
size_t
constructed_elements_
;
size_t
constructed_elements_
;
...
...
libcaf_core/caf/detail/type_list.hpp
View file @
1de110dd
...
@@ -12,8 +12,6 @@
...
@@ -12,8 +12,6 @@
#include "caf/detail/type_pair.hpp"
#include "caf/detail/type_pair.hpp"
#include "caf/fwd.hpp"
#include "caf/fwd.hpp"
#include "caf/none.hpp"
#include "caf/none.hpp"
#include "caf/type_id.hpp"
#include "caf/type_id_list.hpp"
#include "caf/unit.hpp"
#include "caf/unit.hpp"
namespace
caf
::
detail
{
namespace
caf
::
detail
{
...
@@ -36,21 +34,6 @@ struct strip_param<param<T>> {
...
@@ -36,21 +34,6 @@ struct strip_param<param<T>> {
using
type
=
T
;
using
type
=
T
;
};
};
template
<
class
List
>
struct
to_type_id_list_helper
;
template
<
class
...
Ts
>
struct
to_type_id_list_helper
<
type_list
<
Ts
...
>>
{
static
constexpr
type_id_list
get
()
{
return
make_type_id_list
<
typename
strip_param
<
Ts
>::
type
...
>
();
}
};
template
<
class
List
>
constexpr
type_id_list
to_type_id_list
()
{
return
to_type_id_list_helper
<
List
>::
get
();
}
/// Denotes the empty list.
/// Denotes the empty list.
using
empty_type_list
=
type_list
<>
;
using
empty_type_list
=
type_list
<>
;
...
...
libcaf_core/caf/exit_reason.hpp
View file @
1de110dd
...
@@ -19,6 +19,7 @@
...
@@ -19,6 +19,7 @@
namespace
caf
{
namespace
caf
{
// --(rst-exit-reason-begin)--
/// This error category represents fail conditions for actors.
/// This error category represents fail conditions for actors.
enum
class
exit_reason
:
uint8_t
{
enum
class
exit_reason
:
uint8_t
{
/// Indicates that an actor finished execution without error.
/// Indicates that an actor finished execution without error.
...
@@ -40,6 +41,7 @@ enum class exit_reason : uint8_t {
...
@@ -40,6 +41,7 @@ enum class exit_reason : uint8_t {
/// Indicates that an actor was killed because it became unreachable.
/// Indicates that an actor was killed because it became unreachable.
unreachable
unreachable
};
};
// --(rst-exit-reason-end)--
/// @relates exit_reason
/// @relates exit_reason
CAF_CORE_EXPORT
std
::
string
to_string
(
exit_reason
);
CAF_CORE_EXPORT
std
::
string
to_string
(
exit_reason
);
...
...
libcaf_core/caf/fwd.hpp
View file @
1de110dd
...
@@ -130,7 +130,6 @@ class string_view;
...
@@ -130,7 +130,6 @@ class string_view;
class
tracing_data
;
class
tracing_data
;
class
tracing_data_factory
;
class
tracing_data_factory
;
class
type_id_list
;
class
type_id_list
;
class
type_id_list_builder
;
class
uri
;
class
uri
;
class
uri_builder
;
class
uri_builder
;
class
uuid
;
class
uuid
;
...
...
libcaf_core/caf/message.hpp
View file @
1de110dd
...
@@ -43,6 +43,17 @@ public:
...
@@ -43,6 +43,17 @@ public:
message
&
operator
=
(
const
message
&
)
noexcept
=
default
;
message
&
operator
=
(
const
message
&
)
noexcept
=
default
;
// -- concatenation ----------------------------------------------------------
template
<
class
...
Ts
>
static
message
concat
(
Ts
&&
...
xs
)
{
static_assert
(
sizeof
...(
Ts
)
>=
2
);
auto
types
=
type_id_list
::
concat
(
types_of
(
xs
)...);
auto
ptr
=
detail
::
message_data
::
make_uninitialized
(
types
);
ptr
->
init_from
(
std
::
forward
<
Ts
>
(
xs
)...);
return
message
{
data_ptr
{
ptr
.
release
(),
false
}};
}
// -- properties -------------------------------------------------------------
// -- properties -------------------------------------------------------------
auto
types
()
const
noexcept
{
auto
types
()
const
noexcept
{
...
...
libcaf_core/caf/response_promise.hpp
View file @
1de110dd
...
@@ -53,27 +53,31 @@ public:
...
@@ -53,27 +53,31 @@ public:
"mixing expected<T> with regular values is not supported"
);
"mixing expected<T> with regular values is not supported"
);
if
constexpr
(
sizeof
...(
Ts
)
==
0
if
constexpr
(
sizeof
...(
Ts
)
==
0
&&
std
::
is_same
<
message
,
std
::
decay_t
<
T
>>::
value
)
&&
std
::
is_same
<
message
,
std
::
decay_t
<
T
>>::
value
)
return
deliver_impl
(
std
::
forward
<
T
>
(
x
));
deliver_impl
(
std
::
forward
<
T
>
(
x
));
else
else
return
deliver_impl
(
deliver_impl
(
make_message
(
std
::
forward
<
T
>
(
x
),
std
::
forward
<
Ts
>
(
xs
)...));
make_message
(
std
::
forward
<
T
>
(
x
),
std
::
forward
<
Ts
>
(
xs
)...));
}
}
/// Satisfies the promise by sending either an error or a non-error response
/// Satisfies the promise by sending either an error or a non-error response
/// message.
/// message.
template
<
class
T
>
template
<
class
T
>
void
deliver
(
expected
<
T
>
x
)
{
void
deliver
(
expected
<
T
>
x
)
{
if
(
x
)
if
(
x
)
{
return
deliver
(
std
::
move
(
*
x
));
if
constexpr
(
std
::
is_same
<
T
,
void
>::
value
)
return
deliver
(
std
::
move
(
x
.
error
()));
deliver
();
else
deliver
(
std
::
move
(
*
x
));
}
else
{
deliver
(
std
::
move
(
x
.
error
()));
}
}
}
/// Satisfies the promise by delegating to another actor.
/// Satisfies the promise by delegating to another actor.
template
<
message_priority
P
=
message_priority
::
normal
,
class
Handle
=
actor
,
template
<
message_priority
P
=
message_priority
::
normal
,
class
Handle
=
actor
,
class
...
Ts
>
class
...
Ts
>
typename
response_type
<
typename
Handle
::
signatures
,
delegated_response_type_t
<
detail
::
implicit_conversions_t
<
typename
Handle
::
signatures
,
typename
std
::
decay
<
Ts
>::
type
>
...
>::
delegated_type
detail
::
implicit_conversions_t
<
typename
std
::
decay
<
Ts
>::
type
>
...
>
delegate
(
const
Handle
&
dest
,
Ts
&&
...
xs
)
{
delegate
(
const
Handle
&
dest
,
Ts
&&
...
xs
)
{
static_assert
(
sizeof
...(
Ts
)
>
0
,
"nothing to delegate"
);
static_assert
(
sizeof
...(
Ts
)
>
0
,
"nothing to delegate"
);
using
token
=
detail
::
type_list
<
typename
detail
::
implicit_conversions
<
using
token
=
detail
::
type_list
<
typename
detail
::
implicit_conversions
<
...
@@ -96,7 +100,13 @@ public:
...
@@ -96,7 +100,13 @@ public:
/// Satisfies the promise by sending an empty message if this promise has a
/// Satisfies the promise by sending an empty message if this promise has a
/// valid message ID, i.e., `async() == false`.
/// valid message ID, i.e., `async() == false`.
void
deliver
(
unit_t
x
);
void
deliver
();
/// Satisfies the promise by sending an empty message if this promise has a
/// valid message ID, i.e., `async() == false`.
void
deliver
(
unit_t
)
{
deliver
();
}
/// Returns whether this response promise replies to an asynchronous message.
/// Returns whether this response promise replies to an asynchronous message.
bool
async
()
const
;
bool
async
()
const
;
...
...
libcaf_core/caf/response_type.hpp
View file @
1de110dd
...
@@ -58,11 +58,18 @@ struct response_type<detail::type_list<Out(In...), Fs...>, In...> {
...
@@ -58,11 +58,18 @@ struct response_type<detail::type_list<Out(In...), Fs...>, In...> {
using
delegated_type
=
delegated
<
Out
>
;
using
delegated_type
=
delegated
<
Out
>
;
};
};
/// Computes the response message
for input `In...` from the list of message
/// Computes the response message
type for input `In...` from the list of
/// passing interfaces `Fs`.
///
message
passing interfaces `Fs`.
template
<
class
Fs
,
class
...
In
>
template
<
class
Fs
,
class
...
In
>
using
response_type_t
=
typename
response_type
<
Fs
,
In
...
>::
type
;
using
response_type_t
=
typename
response_type
<
Fs
,
In
...
>::
type
;
/// Computes the response message type for input `In...` from the list of
/// message passing interfaces `Fs` and returns the corresponding
/// `delegated<T>`.
template
<
class
Fs
,
class
...
In
>
using
delegated_response_type_t
=
typename
response_type
<
Fs
,
In
...
>::
delegated_type
;
/// Unboxes `Xs` and calls `response_type`.
/// Unboxes `Xs` and calls `response_type`.
template
<
class
Ts
,
class
Xs
>
template
<
class
Ts
,
class
Xs
>
struct
response_type_unbox
;
struct
response_type_unbox
;
...
...
libcaf_core/caf/sec.hpp
View file @
1de110dd
...
@@ -19,6 +19,7 @@
...
@@ -19,6 +19,7 @@
namespace
caf
{
namespace
caf
{
// --(rst-sec-begin)--
/// SEC stands for "System Error Code". This enum contains error codes for
/// SEC stands for "System Error Code". This enum contains error codes for
/// ::actor_system and its modules.
/// ::actor_system and its modules.
enum
class
sec
:
uint8_t
{
enum
class
sec
:
uint8_t
{
...
@@ -159,6 +160,7 @@ enum class sec : uint8_t {
...
@@ -159,6 +160,7 @@ enum class sec : uint8_t {
/// A key lookup failed.
/// A key lookup failed.
no_such_key
=
65
,
no_such_key
=
65
,
};
};
// --(rst-sec-end)--
/// @relates sec
/// @relates sec
CAF_CORE_EXPORT
std
::
string
to_string
(
sec
);
CAF_CORE_EXPORT
std
::
string
to_string
(
sec
);
...
@@ -170,8 +172,8 @@ CAF_CORE_EXPORT bool from_string(string_view, sec&);
...
@@ -170,8 +172,8 @@ CAF_CORE_EXPORT bool from_string(string_view, sec&);
CAF_CORE_EXPORT
bool
from_integer
(
std
::
underlying_type_t
<
sec
>
,
sec
&
);
CAF_CORE_EXPORT
bool
from_integer
(
std
::
underlying_type_t
<
sec
>
,
sec
&
);
/// @relates sec
/// @relates sec
template
<
class
Ins
s
ector
>
template
<
class
Ins
p
ector
>
bool
inspect
(
Ins
s
ector
&
f
,
sec
&
x
)
{
bool
inspect
(
Ins
p
ector
&
f
,
sec
&
x
)
{
return
default_enum_inspect
(
f
,
x
);
return
default_enum_inspect
(
f
,
x
);
}
}
...
...
libcaf_core/caf/string_view.hpp
View file @
1de110dd
...
@@ -27,16 +27,15 @@ struct is_string_like {
...
@@ -27,16 +27,15 @@ struct is_string_like {
template
<
class
U
>
template
<
class
U
>
static
bool
sfinae
(
static
bool
sfinae
(
const
U
*
x
,
const
U
*
x
,
// check if `
(*x)[0]` returns `const char&`
// check if `
x->data()` returns const char*
typename
std
::
enable_if
<
std
::
enable_if_t
<
std
::
is_same
<
const
char
&
,
decltype
((
*
x
)[
0
])
>::
value
>::
type
*
=
nullptr
,
std
::
is_same
<
const
char
*
,
decltype
(
x
->
data
())
>::
value
>
*
=
nullptr
,
// check if `x->size()` returns an integer
// check if `x->size()` returns an integer
typename
std
::
enable_if
<
std
::
enable_if_t
<
std
::
is_integral
<
decltype
(
x
->
size
())
>::
value
>*
=
nullptr
,
std
::
is_integral
<
decltype
(
x
->
size
())
>::
value
>::
type
*
=
nullptr
,
// check if `x->find('?', 0)` is well-formed and returns an integer
// check if `x->find('?', 0)` is well-formed and returns an integer
// (distinguishes vectors from strings)
// (distinguishes vectors from strings)
typename
std
::
enable_if
<
std
::
enable_if_t
<
std
::
is_integral
<
decltype
(
x
->
find
(
'?'
,
0
))
>::
value
>
::
type
*
=
nullptr
);
std
::
is_integral
<
decltype
(
x
->
find
(
'?'
,
0
))
>::
value
>*
=
nullptr
);
// SFINAE fallback.
// SFINAE fallback.
static
void
sfinae
(
void
*
);
static
void
sfinae
(
void
*
);
...
@@ -105,15 +104,9 @@ public:
...
@@ -105,15 +104,9 @@ public:
template
<
class
T
,
class
=
typename
std
::
enable_if
<
template
<
class
T
,
class
=
typename
std
::
enable_if
<
detail
::
is_string_like
<
T
>
::
value
>::
type
>
detail
::
is_string_like
<
T
>
::
value
>::
type
>
string_view
(
const
T
&
str
)
noexcept
{
constexpr
string_view
(
const
T
&
str
)
noexcept
auto
len
=
str
.
size
();
:
data_
(
str
.
data
()),
size_
(
str
.
size
())
{
if
(
len
==
0
)
{
// nop
data_
=
nullptr
;
size_
=
0
;
}
else
{
data_
=
&
(
str
[
0
]);
size_
=
str
.
size
();
}
}
}
string_view
&
operator
=
(
const
string_view
&
)
noexcept
=
default
;
string_view
&
operator
=
(
const
string_view
&
)
noexcept
=
default
;
...
...
libcaf_core/caf/type_id_list.hpp
View file @
1de110dd
...
@@ -11,6 +11,8 @@
...
@@ -11,6 +11,8 @@
#include "caf/detail/comparable.hpp"
#include "caf/detail/comparable.hpp"
#include "caf/detail/core_export.hpp"
#include "caf/detail/core_export.hpp"
#include "caf/detail/implicit_conversions.hpp"
#include "caf/span.hpp"
#include "caf/type_id.hpp"
#include "caf/type_id.hpp"
namespace
caf
{
namespace
caf
{
...
@@ -78,6 +80,17 @@ public:
...
@@ -78,6 +80,17 @@ public:
/// type-erased tuple for the element types stored in this list.
/// type-erased tuple for the element types stored in this list.
size_t
data_size
()
const
noexcept
;
size_t
data_size
()
const
noexcept
;
/// Concatenates all `lists` into a single type ID list.
static
type_id_list
concat
(
span
<
type_id_list
>
lists
);
/// Concatenates all `lists` into a single type ID list.
template
<
class
...
Ts
>
static
type_id_list
concat
(
type_id_list
list1
,
type_id_list
list2
,
Ts
...
lists
)
{
type_id_list
arr
[]
=
{
list1
,
list2
,
lists
...};
return
concat
(
arr
);
}
private:
private:
pointer
data_
;
pointer
data_
;
};
};
...
@@ -99,6 +112,14 @@ constexpr type_id_list make_type_id_list() {
...
@@ -99,6 +112,14 @@ constexpr type_id_list make_type_id_list() {
/// @relates type_id_list
/// @relates type_id_list
CAF_CORE_EXPORT
std
::
string
to_string
(
type_id_list
xs
);
CAF_CORE_EXPORT
std
::
string
to_string
(
type_id_list
xs
);
/// @relates type_id_list
CAF_CORE_EXPORT
type_id_list
types_of
(
const
message
&
msg
);
template
<
class
...
Ts
>
type_id_list
types_of
(
const
std
::
tuple
<
Ts
...
>&
)
{
return
make_type_id_list
<
detail
::
strip_and_convert_t
<
Ts
>
...
>
();
}
}
// namespace caf
}
// namespace caf
namespace
caf
::
detail
{
namespace
caf
::
detail
{
...
@@ -118,4 +139,19 @@ type_id_list make_argument_type_id_list() {
...
@@ -118,4 +139,19 @@ type_id_list make_argument_type_id_list() {
return
argument_type_id_list_factory
<
F
>::
make
();
return
argument_type_id_list_factory
<
F
>::
make
();
}
}
template
<
class
List
>
struct
to_type_id_list_helper
;
template
<
class
...
Ts
>
struct
to_type_id_list_helper
<
type_list
<
Ts
...
>>
{
static
constexpr
type_id_list
get
()
{
return
make_type_id_list
<
typename
strip_param
<
Ts
>::
type
...
>
();
}
};
template
<
class
List
>
constexpr
type_id_list
to_type_id_list
()
{
return
to_type_id_list_helper
<
List
>::
get
();
}
}
// namespace caf::detail
}
// namespace caf::detail
libcaf_core/caf/typed_actor.hpp
View file @
1de110dd
...
@@ -54,6 +54,9 @@ public:
...
@@ -54,6 +54,9 @@ public:
// tell actor_cast which semantic this type uses
// tell actor_cast which semantic this type uses
static
constexpr
bool
has_weak_ptr_semantics
=
false
;
static
constexpr
bool
has_weak_ptr_semantics
=
false
;
/// Stores the template parameter pack.
using
signatures
=
detail
::
type_list
<
Sigs
...
>
;
/// Creates a new `typed_actor` type by extending this one with `Es...`.
/// Creates a new `typed_actor` type by extending this one with `Es...`.
template
<
class
...
Es
>
template
<
class
...
Es
>
using
extend
=
typed_actor
<
Sigs
...,
Es
...
>
;
using
extend
=
typed_actor
<
Sigs
...,
Es
...
>
;
...
@@ -68,15 +71,31 @@ public:
...
@@ -68,15 +71,31 @@ public:
/// for their behavior stack.
/// for their behavior stack.
using
behavior_type
=
typed_behavior
<
Sigs
...
>
;
using
behavior_type
=
typed_behavior
<
Sigs
...
>
;
/// The default, event-based type for implementing this messaging interface.
using
impl
=
typed_event_based_actor
<
Sigs
...
>
;
/// Identifies pointers to instances of this kind of actor.
/// Identifies pointers to instances of this kind of actor.
using
pointer
=
typed_event_based_actor
<
Sigs
...
>
*
;
using
pointer
=
impl
*
;
/// A
llows a view to an actor implementing
this messaging interface without
/// A
view to an actor that implements
this messaging interface without
/// knowledge of the actual type.
.
/// knowledge of the actual type.
using
pointer_view
=
typed_actor_pointer
<
Sigs
...
>
;
using
pointer_view
=
typed_actor_pointer
<
Sigs
...
>
;
/// Identifies the base class for this kind of actor.
/// A class type suitable as base type class-based implementations.
using
base
=
typed_event_based_actor
<
Sigs
...
>
;
using
base
=
impl
;
/// The default, event-based type for implementing this messaging interface as
/// a stateful actor.
template
<
class
State
>
using
stateful_impl
=
stateful_actor
<
State
,
impl
>
;
template
<
class
State
>
using
stateful_base
[[
deprecated
(
"use stateful_impl instead"
)]]
=
stateful_actor
<
State
,
base
>
;
/// Convenience alias for `stateful_impl<State>*`.
template
<
class
State
>
using
stateful_pointer
=
stateful_impl
<
State
>*
;
/// Identifies pointers to brokers implementing this interface.
/// Identifies pointers to brokers implementing this interface.
using
broker_pointer
=
io
::
typed_broker
<
Sigs
...
>*
;
using
broker_pointer
=
io
::
typed_broker
<
Sigs
...
>*
;
...
@@ -84,17 +103,6 @@ public:
...
@@ -84,17 +103,6 @@ public:
/// Identifies the base class of brokers implementing this interface.
/// Identifies the base class of brokers implementing this interface.
using
broker_base
=
io
::
typed_broker
<
Sigs
...
>
;
using
broker_base
=
io
::
typed_broker
<
Sigs
...
>
;
/// Stores the template parameter pack.
using
signatures
=
detail
::
type_list
<
Sigs
...
>
;
/// Identifies the base class for this kind of actor with actor.
template
<
class
State
>
using
stateful_base
=
stateful_actor
<
State
,
base
>
;
/// Identifies the base class for this kind of actor with actor.
template
<
class
State
>
using
stateful_pointer
=
stateful_actor
<
State
,
base
>*
;
/// Identifies the broker_base class for this kind of actor with actor.
/// Identifies the broker_base class for this kind of actor with actor.
template
<
class
State
>
template
<
class
State
>
using
stateful_broker_base
=
stateful_actor
<
State
,
broker_base
>
;
using
stateful_broker_base
=
stateful_actor
<
State
,
broker_base
>
;
...
...
libcaf_core/caf/typed_response_promise.hpp
View file @
1de110dd
...
@@ -4,9 +4,11 @@
...
@@ -4,9 +4,11 @@
#pragma once
#pragma once
#include
"caf/response_promise.hpp"
#include
<type_traits>
#include "caf/detail/type_list.hpp"
#include "caf/detail/type_list.hpp"
#include "caf/make_message.hpp"
#include "caf/response_promise.hpp"
namespace
caf
{
namespace
caf
{
...
@@ -48,42 +50,38 @@ public:
...
@@ -48,42 +50,38 @@ public:
}
}
/// Satisfies the promise by sending a non-error response message.
/// Satisfies the promise by sending a non-error response message.
template
<
class
U
,
class
...
Us
>
template
<
class
...
Us
>
typename
std
::
enable_if
<
(
sizeof
...(
Us
)
>
0
)
std
::
enable_if_t
<
(
std
::
is_constructible
<
Ts
,
Us
>::
value
&&
...)
>
||
!
std
::
is_convertible
<
U
,
error
>::
value
,
deliver
(
Us
...
xs
)
{
typed_response_promise
>::
type
promise_
.
deliver
(
make_message
(
Ts
{
std
::
forward
<
Us
>
(
xs
)}...));
deliver
(
U
&&
x
,
Us
&&
...
xs
)
{
}
static_assert
(
std
::
is_same
<
detail
::
type_list
<
Ts
...
>
,
/// Satisfies the promise by sending an empty response message.
detail
::
type_list
<
typename
std
::
decay
<
U
>::
type
,
template
<
class
L
=
detail
::
type_list
<
Ts
...>
>
typename
std
::
decay
<
Us
>::
type
...
>>::
value
,
std
::
enable_if_t
<
std
::
is_same
<
L
,
detail
::
type_list
<
void
>>::
value
>
deliver
()
{
"typed_response_promise: message type mismatched"
);
promise_
.
deliver
();
promise_
.
deliver
(
std
::
forward
<
U
>
(
x
),
std
::
forward
<
Us
>
(
xs
)...);
}
return
*
this
;
/// Satisfies the promise by sending an error response message.
/// For non-requests, nothing is done.
void
deliver
(
error
x
)
{
promise_
.
deliver
(
std
::
move
(
x
));
}
}
/// Satisfies the promise by sending either an error or a non-error response
/// Satisfies the promise by sending either an error or a non-error response
/// message.
/// message.
template
<
class
T
>
template
<
class
T
>
void
deliver
(
expected
<
T
>
x
)
{
std
::
enable_if_t
<
if
(
x
)
std
::
is_same
<
detail
::
type_list
<
T
>
,
detail
::
type_list
<
Ts
...
>>::
value
>
return
deliver
(
std
::
move
(
*
x
));
deliver
(
expected
<
T
>
x
)
{
return
deliver
(
std
::
move
(
x
.
error
()
));
promise_
.
deliver
(
std
::
move
(
x
));
}
}
/// Satisfies the promise by delegating to another actor.
/// Satisfies the promise by delegating to another actor.
template
<
message_priority
P
=
message_priority
::
normal
,
class
Handle
=
actor
,
template
<
message_priority
P
=
message_priority
::
normal
,
class
Handle
=
actor
,
class
...
Us
>
class
...
Us
>
typed_response_promise
delegate
(
const
Handle
&
dest
,
Us
&&
...
xs
)
{
auto
delegate
(
const
Handle
&
dest
,
Us
&&
...
xs
)
{
promise_
.
template
delegate
<
P
>(
dest
,
std
::
forward
<
Us
>
(
xs
)...);
return
promise_
.
template
delegate
<
P
>(
dest
,
std
::
forward
<
Us
>
(
xs
)...);
return
*
this
;
}
/// Satisfies the promise by sending an error response message.
/// For non-requests, nothing is done.
typed_response_promise
deliver
(
error
x
)
{
promise_
.
deliver
(
std
::
move
(
x
));
return
*
this
;
}
}
/// Returns whether this response promise replies to an asynchronous message.
/// Returns whether this response promise replies to an asynchronous message.
...
...
libcaf_core/src/config_value.cpp
View file @
1de110dd
...
@@ -220,7 +220,7 @@ expected<bool> config_value::to_boolean() const {
...
@@ -220,7 +220,7 @@ expected<bool> config_value::to_boolean() const {
using
result_type
=
expected
<
bool
>
;
using
result_type
=
expected
<
bool
>
;
auto
f
=
detail
::
make_overload
(
auto
f
=
detail
::
make_overload
(
no_conversions
<
bool
,
none_t
,
integer
,
real
,
timespan
,
uri
,
no_conversions
<
bool
,
none_t
,
integer
,
real
,
timespan
,
uri
,
config_value
::
list
,
config_value
::
dictionary
>
(),
config_value
::
list
>
(),
[](
boolean
x
)
{
return
result_type
{
x
};
},
[](
boolean
x
)
{
return
result_type
{
x
};
},
[](
const
std
::
string
&
x
)
{
[](
const
std
::
string
&
x
)
{
if
(
x
==
"true"
)
{
if
(
x
==
"true"
)
{
...
@@ -233,6 +233,31 @@ expected<bool> config_value::to_boolean() const {
...
@@ -233,6 +233,31 @@ expected<bool> config_value::to_boolean() const {
msg
+=
" to a boolean"
;
msg
+=
" to a boolean"
;
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
}
}
},
[](
const
dictionary
&
x
)
{
if
(
auto
i
=
x
.
find
(
"@type"
);
i
!=
x
.
end
()
&&
holds_alternative
<
std
::
string
>
(
i
->
second
))
{
const
auto
&
tn
=
get
<
std
::
string
>
(
i
->
second
);
if
(
tn
==
type_name_v
<
bool
>
)
{
if
(
auto
j
=
x
.
find
(
"value"
);
j
!=
x
.
end
())
{
return
j
->
second
.
to_boolean
();
}
else
{
std
::
string
msg
=
"missing value for object of type "
;
msg
+=
tn
;
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
}
}
else
{
std
::
string
msg
=
"cannot convert "
;
msg
+=
tn
;
msg
+=
" to a boolean"
;
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
}
}
else
{
std
::
string
msg
=
"cannot convert a dictionary to a boolean"
;
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
}
});
});
return
visit
(
f
,
data_
);
return
visit
(
f
,
data_
);
}
}
...
@@ -240,8 +265,7 @@ expected<bool> config_value::to_boolean() const {
...
@@ -240,8 +265,7 @@ expected<bool> config_value::to_boolean() const {
expected
<
config_value
::
integer
>
config_value
::
to_integer
()
const
{
expected
<
config_value
::
integer
>
config_value
::
to_integer
()
const
{
using
result_type
=
expected
<
integer
>
;
using
result_type
=
expected
<
integer
>
;
auto
f
=
detail
::
make_overload
(
auto
f
=
detail
::
make_overload
(
no_conversions
<
integer
,
none_t
,
bool
,
timespan
,
uri
,
config_value
::
list
,
no_conversions
<
integer
,
none_t
,
bool
,
timespan
,
uri
,
config_value
::
list
>
(),
config_value
::
dictionary
>
(),
[](
integer
x
)
{
return
result_type
{
x
};
},
[](
integer
x
)
{
return
result_type
{
x
};
},
[](
real
x
)
{
[](
real
x
)
{
using
limits
=
std
::
numeric_limits
<
config_value
::
integer
>
;
using
limits
=
std
::
numeric_limits
<
config_value
::
integer
>
;
...
@@ -269,6 +293,37 @@ expected<config_value::integer> config_value::to_integer() const {
...
@@ -269,6 +293,37 @@ expected<config_value::integer> config_value::to_integer() const {
detail
::
print_escaped
(
msg
,
x
);
detail
::
print_escaped
(
msg
,
x
);
msg
+=
" to an integer"
;
msg
+=
" to an integer"
;
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
},
[](
const
dictionary
&
x
)
{
if
(
auto
i
=
x
.
find
(
"@type"
);
i
!=
x
.
end
()
&&
holds_alternative
<
std
::
string
>
(
i
->
second
))
{
const
auto
&
tn
=
get
<
std
::
string
>
(
i
->
second
);
string_view
valid_types
[]
=
{
type_name_v
<
int16_t
>
,
type_name_v
<
int32_t
>
,
type_name_v
<
int64_t
>
,
type_name_v
<
int8_t
>
,
type_name_v
<
uint16_t
>
,
type_name_v
<
uint32_t
>
,
type_name_v
<
uint64_t
>
,
type_name_v
<
uint8_t
>
};
auto
eq
=
[
&
tn
](
string_view
x
)
{
return
x
==
tn
;
};
if
(
std
::
any_of
(
std
::
begin
(
valid_types
),
std
::
end
(
valid_types
),
eq
))
{
if
(
auto
j
=
x
.
find
(
"value"
);
j
!=
x
.
end
())
{
return
j
->
second
.
to_integer
();
}
else
{
std
::
string
msg
=
"missing value for object of type "
;
msg
+=
tn
;
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
}
}
else
{
std
::
string
msg
=
"cannot convert "
;
msg
+=
tn
;
msg
+=
" to an integer"
;
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
}
}
else
{
std
::
string
msg
=
"cannot convert a dictionary to an integer"
;
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
}
});
});
return
visit
(
f
,
data_
);
return
visit
(
f
,
data_
);
}
}
...
@@ -276,8 +331,7 @@ expected<config_value::integer> config_value::to_integer() const {
...
@@ -276,8 +331,7 @@ expected<config_value::integer> config_value::to_integer() const {
expected
<
config_value
::
real
>
config_value
::
to_real
()
const
{
expected
<
config_value
::
real
>
config_value
::
to_real
()
const
{
using
result_type
=
expected
<
real
>
;
using
result_type
=
expected
<
real
>
;
auto
f
=
detail
::
make_overload
(
auto
f
=
detail
::
make_overload
(
no_conversions
<
real
,
none_t
,
bool
,
timespan
,
uri
,
config_value
::
list
,
no_conversions
<
real
,
none_t
,
bool
,
timespan
,
uri
,
config_value
::
list
>
(),
config_value
::
dictionary
>
(),
[](
integer
x
)
{
[](
integer
x
)
{
// This cast may lose precision on the value. We could try and check that,
// This cast may lose precision on the value. We could try and check that,
// but refusing to convert on loss of precision could also be unexpected
// but refusing to convert on loss of precision could also be unexpected
...
@@ -293,6 +347,35 @@ expected<config_value::real> config_value::to_real() const {
...
@@ -293,6 +347,35 @@ expected<config_value::real> config_value::to_real() const {
detail
::
print_escaped
(
msg
,
x
);
detail
::
print_escaped
(
msg
,
x
);
msg
+=
" to a floating point number"
;
msg
+=
" to a floating point number"
;
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
},
[](
const
dictionary
&
x
)
{
if
(
auto
i
=
x
.
find
(
"@type"
);
i
!=
x
.
end
()
&&
holds_alternative
<
std
::
string
>
(
i
->
second
))
{
const
auto
&
tn
=
get
<
std
::
string
>
(
i
->
second
);
string_view
valid_types
[]
=
{
type_name_v
<
float
>
,
type_name_v
<
double
>
,
type_name_v
<
long
double
>
};
auto
eq
=
[
&
tn
](
string_view
x
)
{
return
x
==
tn
;
};
if
(
std
::
any_of
(
std
::
begin
(
valid_types
),
std
::
end
(
valid_types
),
eq
))
{
if
(
auto
j
=
x
.
find
(
"value"
);
j
!=
x
.
end
())
{
return
j
->
second
.
to_real
();
}
else
{
std
::
string
msg
=
"missing value for object of type "
;
msg
+=
tn
;
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
}
}
else
{
std
::
string
msg
=
"cannot convert "
;
msg
+=
tn
;
msg
+=
" to a floating point number"
;
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
}
}
else
{
std
::
string
msg
=
"cannot convert a dictionary to a floating point number"
;
return
result_type
{
make_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
))};
}
});
});
return
visit
(
f
,
data_
);
return
visit
(
f
,
data_
);
}
}
...
...
libcaf_core/src/config_value_reader.cpp
View file @
1de110dd
...
@@ -381,7 +381,7 @@ bool config_value_reader::begin_associative_array(size_t& size) {
...
@@ -381,7 +381,7 @@ bool config_value_reader::begin_associative_array(size_t& size) {
st_
.
top
()
=
associative_array
{
dict
->
begin
(),
dict
->
end
()};
st_
.
top
()
=
associative_array
{
dict
->
begin
(),
dict
->
end
()};
return
true
;
return
true
;
}
}
std
::
string
msg
=
"expected a dictionary, got a "
;
std
::
string
msg
=
"
begin_associative_array:
expected a dictionary, got a "
;
msg
+=
top
->
type_name
();
msg
+=
top
->
type_name
();
emplace_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
));
emplace_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
));
return
false
;
return
false
;
...
@@ -419,13 +419,10 @@ bool pull(config_value_reader& reader, T& x) {
...
@@ -419,13 +419,10 @@ bool pull(config_value_reader& reader, T& x) {
reader
.
pop
();
reader
.
pop
();
return
true
;
return
true
;
}
else
{
}
else
{
std
::
string
msg
=
"expected a dictionary, got a "
;
reader
.
set_error
(
std
::
move
(
val
.
error
()));
msg
+=
to_string
(
type_name_v
<
T
>
);
reader
.
emplace_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
));
return
false
;
return
false
;
}
}
}
}
else
if
(
holds_alternative
<
config_value_reader
::
sequence
>
(
top
))
{
if
(
holds_alternative
<
config_value_reader
::
sequence
>
(
top
))
{
auto
&
seq
=
get
<
config_value_reader
::
sequence
>
(
top
);
auto
&
seq
=
get
<
config_value_reader
::
sequence
>
(
top
);
if
(
seq
.
at_end
())
{
if
(
seq
.
at_end
())
{
reader
.
emplace_error
(
sec
::
runtime_error
,
"value: sequence out of bounds"
);
reader
.
emplace_error
(
sec
::
runtime_error
,
"value: sequence out of bounds"
);
...
@@ -437,13 +434,10 @@ bool pull(config_value_reader& reader, T& x) {
...
@@ -437,13 +434,10 @@ bool pull(config_value_reader& reader, T& x) {
seq
.
advance
();
seq
.
advance
();
return
true
;
return
true
;
}
else
{
}
else
{
std
::
string
msg
=
"expected a dictionary, got a "
;
reader
.
set_error
(
std
::
move
(
val
.
error
()));
msg
+=
to_string
(
type_name_v
<
T
>
);
reader
.
emplace_error
(
sec
::
conversion_failed
,
std
::
move
(
msg
));
return
false
;
return
false
;
}
}
}
}
else
if
(
holds_alternative
<
config_value_reader
::
key_ptr
>
(
top
))
{
if
(
holds_alternative
<
config_value_reader
::
key_ptr
>
(
top
))
{
auto
ptr
=
get
<
config_value_reader
::
key_ptr
>
(
top
);
auto
ptr
=
get
<
config_value_reader
::
key_ptr
>
(
top
);
if
constexpr
(
std
::
is_same
<
std
::
string
,
T
>::
value
)
{
if
constexpr
(
std
::
is_same
<
std
::
string
,
T
>::
value
)
{
x
=
*
ptr
;
x
=
*
ptr
;
...
@@ -453,10 +447,11 @@ bool pull(config_value_reader& reader, T& x) {
...
@@ -453,10 +447,11 @@ bool pull(config_value_reader& reader, T& x) {
if
(
auto
err
=
detail
::
parse
(
*
ptr
,
x
))
{
if
(
auto
err
=
detail
::
parse
(
*
ptr
,
x
))
{
reader
.
set_error
(
std
::
move
(
err
));
reader
.
set_error
(
std
::
move
(
err
));
return
false
;
return
false
;
}
}
else
{
return
true
;
return
true
;
}
}
}
}
}
reader
.
emplace_error
(
sec
::
conversion_failed
,
reader
.
emplace_error
(
sec
::
conversion_failed
,
"expected a value, sequence, or key"
);
"expected a value, sequence, or key"
);
return
false
;
return
false
;
...
...
libcaf_core/src/detail/message_data.cpp
View file @
1de110dd
...
@@ -10,6 +10,7 @@
...
@@ -10,6 +10,7 @@
#include "caf/detail/meta_object.hpp"
#include "caf/detail/meta_object.hpp"
#include "caf/error.hpp"
#include "caf/error.hpp"
#include "caf/error_code.hpp"
#include "caf/error_code.hpp"
#include "caf/message.hpp"
#include "caf/raise_error.hpp"
#include "caf/raise_error.hpp"
#include "caf/sec.hpp"
#include "caf/sec.hpp"
#include "caf/span.hpp"
#include "caf/span.hpp"
...
@@ -48,18 +49,30 @@ message_data* message_data::copy() const {
...
@@ -48,18 +49,30 @@ message_data* message_data::copy() const {
auto
vptr
=
malloc
(
total_size
);
auto
vptr
=
malloc
(
total_size
);
if
(
vptr
==
nullptr
)
if
(
vptr
==
nullptr
)
CAF_RAISE_ERROR
(
std
::
bad_alloc
,
"bad_alloc"
);
CAF_RAISE_ERROR
(
std
::
bad_alloc
,
"bad_alloc"
);
auto
ptr
=
new
(
vptr
)
message_data
(
types_
)
;
intrusive_ptr
<
message_data
>
ptr
{
new
(
vptr
)
message_data
(
types_
),
false
}
;
auto
src
=
storage
();
auto
src
=
storage
();
auto
dst
=
ptr
->
storage
();
auto
dst
=
ptr
->
storage
();
for
(
auto
id
:
types_
)
{
for
(
auto
id
:
types_
)
{
auto
&
meta
=
gmos
[
id
];
auto
&
meta
=
gmos
[
id
];
// TODO: exception handling.
meta
.
copy_construct
(
dst
,
src
);
meta
.
copy_construct
(
dst
,
src
);
++
ptr
->
constructed_elements_
;
++
ptr
->
constructed_elements_
;
src
+=
meta
.
padded_size
;
src
+=
meta
.
padded_size
;
dst
+=
meta
.
padded_size
;
dst
+=
meta
.
padded_size
;
}
}
return
ptr
;
return
ptr
.
release
();
}
intrusive_ptr
<
message_data
>
message_data
::
make_uninitialized
(
type_id_list
types
)
{
auto
gmos
=
global_meta_objects
();
size_t
storage_size
=
0
;
for
(
auto
id
:
types
)
storage_size
+=
gmos
[
id
].
padded_size
;
auto
total_size
=
sizeof
(
message_data
)
+
storage_size
;
auto
vptr
=
malloc
(
total_size
);
if
(
vptr
==
nullptr
)
CAF_RAISE_ERROR
(
std
::
bad_alloc
,
"bad_alloc"
);
return
{
new
(
vptr
)
message_data
(
types
),
false
};
}
}
byte
*
message_data
::
at
(
size_t
index
)
noexcept
{
byte
*
message_data
::
at
(
size_t
index
)
noexcept
{
...
@@ -82,4 +95,23 @@ const byte* message_data::at(size_t index) const noexcept {
...
@@ -82,4 +95,23 @@ const byte* message_data::at(size_t index) const noexcept {
return
ptr
;
return
ptr
;
}
}
byte
*
message_data
::
stepwise_init_from
(
byte
*
pos
,
const
message
&
msg
)
{
return
stepwise_init_from
(
pos
,
msg
.
cptr
());
}
byte
*
message_data
::
stepwise_init_from
(
byte
*
pos
,
const
message_data
*
other
)
{
CAF_ASSERT
(
other
!=
nullptr
);
CAF_ASSERT
(
other
!=
this
);
auto
gmos
=
global_meta_objects
();
auto
src
=
other
->
storage
();
for
(
auto
id
:
other
->
types
())
{
auto
&
meta
=
gmos
[
id
];
meta
.
copy_construct
(
pos
,
src
);
++
constructed_elements_
;
src
+=
meta
.
padded_size
;
pos
+=
meta
.
padded_size
;
}
return
pos
;
}
}
// namespace caf::detail
}
// namespace caf::detail
libcaf_core/src/response_promise.cpp
View file @
1de110dd
...
@@ -46,7 +46,7 @@ void response_promise::deliver(error x) {
...
@@ -46,7 +46,7 @@ void response_promise::deliver(error x) {
deliver_impl
(
make_message
(
std
::
move
(
x
)));
deliver_impl
(
make_message
(
std
::
move
(
x
)));
}
}
void
response_promise
::
deliver
(
unit_t
)
{
void
response_promise
::
deliver
()
{
deliver_impl
(
make_message
());
deliver_impl
(
make_message
());
}
}
...
@@ -75,6 +75,11 @@ void response_promise::deliver_impl(message msg) {
...
@@ -75,6 +75,11 @@ void response_promise::deliver_impl(message msg) {
CAF_LOG_DEBUG
(
"drop response: invalid promise"
);
CAF_LOG_DEBUG
(
"drop response: invalid promise"
);
return
;
return
;
}
}
if
(
msg
.
empty
()
&&
id_
.
is_async
())
{
CAF_LOG_DEBUG
(
"drop response: empty response to asynchronous input"
);
self_
.
reset
();
return
;
}
auto
dptr
=
self_dptr
();
auto
dptr
=
self_dptr
();
if
(
!
stages_
.
empty
())
{
if
(
!
stages_
.
empty
())
{
auto
next
=
std
::
move
(
stages_
.
back
());
auto
next
=
std
::
move
(
stages_
.
back
());
...
...
libcaf_core/src/type_id_list.cpp
View file @
1de110dd
...
@@ -5,6 +5,8 @@
...
@@ -5,6 +5,8 @@
#include "caf/type_id_list.hpp"
#include "caf/type_id_list.hpp"
#include "caf/detail/meta_object.hpp"
#include "caf/detail/meta_object.hpp"
#include "caf/detail/type_id_list_builder.hpp"
#include "caf/message.hpp"
namespace
caf
{
namespace
caf
{
...
@@ -35,4 +37,20 @@ std::string to_string(type_id_list xs) {
...
@@ -35,4 +37,20 @@ std::string to_string(type_id_list xs) {
return
result
;
return
result
;
}
}
type_id_list
type_id_list
::
concat
(
span
<
type_id_list
>
lists
)
{
auto
total_size
=
size_t
{
0
};
for
(
auto
ls
:
lists
)
total_size
+=
ls
.
size
();
detail
::
type_id_list_builder
builder
;
builder
.
reserve
(
total_size
);
for
(
auto
ls
:
lists
)
for
(
auto
id
:
ls
)
builder
.
push_back
(
id
);
return
builder
.
move_to_list
();
}
type_id_list
types_of
(
const
message
&
msg
)
{
return
msg
.
types
();
}
}
// namespace caf
}
// namespace caf
libcaf_core/test/config_value.cpp
View file @
1de110dd
...
@@ -97,6 +97,16 @@ SCENARIO("get_as can convert config values to boolean") {
...
@@ -97,6 +97,16 @@ SCENARIO("get_as can convert config values to boolean") {
}
}
}
}
}
}
GIVEN
(
"a config value with type annotation 'bool' and the value
\"
true
\"
"
)
{
config_value
x
;
x
.
as_dictionary
().
emplace
(
"@type"
,
"bool"
);
x
.
as_dictionary
().
emplace
(
"value"
,
"true"
);
WHEN
(
"using get_as with bool"
)
{
THEN
(
"conversion succeeds"
)
{
CHECK_EQ
(
get_as
<
bool
>
(
x
),
true
);
}
}
}
GIVEN
(
"non-boolean config_values"
)
{
GIVEN
(
"non-boolean config_values"
)
{
WHEN
(
"using get_as with bool"
)
{
WHEN
(
"using get_as with bool"
)
{
THEN
(
"conversion fails"
)
{
THEN
(
"conversion fails"
)
{
...
@@ -161,6 +171,23 @@ SCENARIO("get_as can convert config values to integers") {
...
@@ -161,6 +171,23 @@ SCENARIO("get_as can convert config values to integers") {
}
}
}
}
}
}
GIVEN
(
"a config value x with type annotation 'int32_t' and the value 50"
)
{
config_value
x
;
x
.
as_dictionary
().
emplace
(
"@type"
,
"int32_t"
);
x
.
as_dictionary
().
emplace
(
"value"
,
50
);
WHEN
(
"using get_as with integer types"
)
{
THEN
(
"CAF parses the integer and performs a bound check"
)
{
CHECK_EQ
(
get_as
<
uint64_t
>
(
x
),
50u
);
CHECK_EQ
(
get_as
<
int64_t
>
(
x
),
50
);
CHECK_EQ
(
get_as
<
uint32_t
>
(
x
),
50u
);
CHECK_EQ
(
get_as
<
int32_t
>
(
x
),
50
);
CHECK_EQ
(
get_as
<
uint16_t
>
(
x
),
50u
);
CHECK_EQ
(
get_as
<
int16_t
>
(
x
),
50
);
CHECK_EQ
(
get_as
<
uint8_t
>
(
x
),
50u
);
CHECK_EQ
(
get_as
<
int8_t
>
(
x
),
50
);
}
}
}
GIVEN
(
"a config value x with value 50.0"
)
{
GIVEN
(
"a config value x with value 50.0"
)
{
auto
x
=
config_value
{
50.0
};
auto
x
=
config_value
{
50.0
};
WHEN
(
"using get_as with integer types"
)
{
WHEN
(
"using get_as with integer types"
)
{
...
@@ -278,6 +305,18 @@ SCENARIO("get_as can convert config values to floating point numbers") {
...
@@ -278,6 +305,18 @@ SCENARIO("get_as can convert config values to floating point numbers") {
}
}
}
}
}
}
GIVEN
(
"a config value x with type annotation 'float' and the value 50"
)
{
config_value
x
;
x
.
as_dictionary
().
emplace
(
"@type"
,
"float"
);
x
.
as_dictionary
().
emplace
(
"value"
,
123.0
);
WHEN
(
"using get_as with floating point types"
)
{
THEN
(
"CAF parses the value and performs a bound check"
)
{
CHECK_EQ
(
get_as
<
long
double
>
(
x
),
123.0
);
CHECK_EQ
(
get_as
<
double
>
(
x
),
123.0
);
CHECK_EQ
(
get_as
<
float
>
(
x
),
123.
f
);
}
}
}
GIVEN
(
"config_values of null, URI, boolean, list or dictionary"
)
{
GIVEN
(
"config_values of null, URI, boolean, list or dictionary"
)
{
WHEN
(
"using get_as with integer types"
)
{
WHEN
(
"using get_as with integer types"
)
{
THEN
(
"conversion fails"
)
{
THEN
(
"conversion fails"
)
{
...
...
libcaf_core/test/message.cpp
View file @
1de110dd
...
@@ -121,3 +121,15 @@ CAF_TEST(match_elements exposes element types) {
...
@@ -121,3 +121,15 @@ CAF_TEST(match_elements exposes element types) {
CAF_CHECK
((
msg
.
match_element
<
int64_t
>
(
2
)));
CAF_CHECK
((
msg
.
match_element
<
int64_t
>
(
2
)));
CAF_CHECK
((
msg
.
match_elements
<
put_atom
,
string
,
int64_t
>
()));
CAF_CHECK
((
msg
.
match_elements
<
put_atom
,
string
,
int64_t
>
()));
}
}
CAF_TEST
(
messages
are
concatenable
)
{
using
std
::
make_tuple
;
CHECK
(
message
::
concat
(
make_tuple
(
int16_t
{
1
}),
make_tuple
(
uint8_t
{
2
}))
.
matches
(
int16_t
{
1
},
uint8_t
{
2
}));
CHECK
(
message
::
concat
(
make_message
(
int16_t
{
1
}),
make_message
(
uint8_t
{
2
}))
.
matches
(
int16_t
{
1
},
uint8_t
{
2
}));
CHECK
(
message
::
concat
(
make_message
(
int16_t
{
1
}),
make_tuple
(
uint8_t
{
2
}))
.
matches
(
int16_t
{
1
},
uint8_t
{
2
}));
CHECK
(
message
::
concat
(
make_tuple
(
int16_t
{
1
}),
make_message
(
uint8_t
{
2
}))
.
matches
(
int16_t
{
1
},
uint8_t
{
2
}));
}
libcaf_core/test/response_promise.cpp
0 → 100644
View file @
1de110dd
// This file is part of CAF, the C++ Actor Framework. See the file LICENSE in
// the main distribution directory for license terms and copyright or visit
// https://github.com/actor-framework/actor-framework/blob/master/LICENSE.
#define CAF_SUITE response_promise
#include "caf/response_promise.hpp"
#include "core-test.hpp"
using
namespace
caf
;
namespace
{
behavior
adder
()
{
return
{
[](
int
x
,
int
y
)
{
return
x
+
y
;
},
[](
ok_atom
)
{},
};
}
behavior
delegator
(
event_based_actor
*
self
,
actor
worker
)
{
return
{
[
=
](
int
x
,
int
y
)
{
auto
promise
=
self
->
make_response_promise
();
return
promise
.
delegate
(
worker
,
x
,
y
);
},
[
=
](
ok_atom
)
{
auto
promise
=
self
->
make_response_promise
();
return
promise
.
delegate
(
worker
,
ok_atom_v
);
},
};
}
behavior
requester_v1
(
event_based_actor
*
self
,
actor
worker
)
{
return
{
[
=
](
int
x
,
int
y
)
{
auto
rp
=
self
->
make_response_promise
();
self
->
request
(
worker
,
infinite
,
x
,
y
)
.
then
(
[
rp
](
int
result
)
mutable
{
CHECK
(
rp
.
pending
());
rp
.
deliver
(
result
);
},
[
rp
](
error
err
)
mutable
{
CHECK
(
rp
.
pending
());
rp
.
deliver
(
std
::
move
(
err
));
});
return
rp
;
},
[
=
](
ok_atom
)
{
auto
rp
=
self
->
make_response_promise
();
self
->
request
(
worker
,
infinite
,
ok_atom_v
)
.
then
(
[
rp
]()
mutable
{
CHECK
(
rp
.
pending
());
rp
.
deliver
();
},
[
rp
](
error
err
)
mutable
{
CHECK
(
rp
.
pending
());
rp
.
deliver
(
std
::
move
(
err
));
});
return
rp
;
},
};
}
behavior
requester_v2
(
event_based_actor
*
self
,
actor
worker
)
{
return
{
[
=
](
int
x
,
int
y
)
{
auto
rp
=
self
->
make_response_promise
();
auto
deliver
=
[
rp
](
expected
<
int
>
x
)
mutable
{
CHECK
(
rp
.
pending
());
rp
.
deliver
(
std
::
move
(
x
));
};
self
->
request
(
worker
,
infinite
,
x
,
y
)
.
then
([
deliver
](
int
result
)
mutable
{
deliver
(
result
);
},
[
deliver
](
error
err
)
mutable
{
deliver
(
std
::
move
(
err
));
});
return
rp
;
},
[
=
](
ok_atom
)
{
auto
rp
=
self
->
make_response_promise
();
auto
deliver
=
[
rp
](
expected
<
void
>
x
)
mutable
{
CHECK
(
rp
.
pending
());
rp
.
deliver
(
std
::
move
(
x
));
};
self
->
request
(
worker
,
infinite
,
ok_atom_v
)
.
then
([
deliver
]()
mutable
{
deliver
({});
},
[
deliver
](
error
err
)
mutable
{
deliver
(
std
::
move
(
err
));
});
return
rp
;
},
};
}
}
// namespace
CAF_TEST_FIXTURE_SCOPE
(
response_promise_tests
,
test_coordinator_fixture
<>
)
SCENARIO
(
"response promises allow delaying of response messages"
)
{
auto
adder_hdl
=
sys
.
spawn
(
adder
);
std
::
map
<
std
::
string
,
actor
>
impls
;
impls
[
"with a value or an error"
]
=
sys
.
spawn
(
requester_v1
,
adder_hdl
);
impls
[
"with an expected<T>"
]
=
sys
.
spawn
(
requester_v2
,
adder_hdl
);
for
(
auto
&
[
desc
,
hdl
]
:
impls
)
{
GIVEN
(
"a dispatcher that calls deliver "
<<
desc
<<
" on its promise"
)
{
WHEN
(
"sending a request with two integers to the dispatcher"
)
{
inject
((
int
,
int
),
from
(
self
).
to
(
hdl
).
with
(
3
,
4
));
THEN
(
"clients receive the response from the dispatcher"
)
{
expect
((
int
,
int
),
from
(
hdl
).
to
(
adder_hdl
).
with
(
3
,
4
));
expect
((
int
),
from
(
adder_hdl
).
to
(
hdl
).
with
(
7
));
expect
((
int
),
from
(
hdl
).
to
(
self
).
with
(
7
));
}
}
WHEN
(
"sending ok_atom to the dispatcher synchronously"
)
{
auto
res
=
self
->
request
(
hdl
,
infinite
,
ok_atom_v
);
auto
fetch_result
=
[
&
]
{
message
result
;
res
.
receive
([]
{},
// void result
[
&
](
const
error
&
reason
)
{
result
=
make_message
(
reason
);
});
return
result
;
};
THEN
(
"clients receive an empty response from the dispatcher"
)
{
expect
((
ok_atom
),
from
(
self
).
to
(
hdl
));
expect
((
ok_atom
),
from
(
hdl
).
to
(
adder_hdl
));
expect
((
void
),
from
(
adder_hdl
).
to
(
hdl
));
CHECK
(
fetch_result
().
empty
());
}
}
WHEN
(
"sending ok_atom to the dispatcher asynchronously"
)
{
THEN
(
"clients receive no response from the dispatcher"
)
{
inject
((
ok_atom
),
from
(
self
).
to
(
hdl
).
with
(
ok_atom_v
));
expect
((
ok_atom
),
from
(
hdl
).
to
(
adder_hdl
));
expect
((
void
),
from
(
adder_hdl
).
to
(
hdl
));
CHECK
(
self
->
mailbox
().
empty
());
}
}
}
}
}
SCENARIO
(
"response promises allow delegation"
)
{
GIVEN
(
"a dispatcher that calls delegate on its promise"
)
{
auto
adder_hdl
=
sys
.
spawn
(
adder
);
auto
hdl
=
sys
.
spawn
(
delegator
,
adder_hdl
);
WHEN
(
"sending a request to the dispatcher"
)
{
inject
((
int
,
int
),
from
(
self
).
to
(
hdl
).
with
(
3
,
4
));
THEN
(
"clients receive the response from the adder"
)
{
expect
((
int
,
int
),
from
(
self
).
to
(
adder_hdl
).
with
(
3
,
4
));
expect
((
int
),
from
(
adder_hdl
).
to
(
self
).
with
(
7
));
}
}
}
}
CAF_TEST_FIXTURE_SCOPE_END
()
libcaf_core/test/type_id_list.cpp
View file @
1de110dd
...
@@ -83,3 +83,35 @@ CAF_TEST(type ID lists are convertible to strings) {
...
@@ -83,3 +83,35 @@ CAF_TEST(type ID lists are convertible to strings) {
auto
xs
=
make_type_id_list
<
uint16_t
,
bool
,
float
,
long
double
>
();
auto
xs
=
make_type_id_list
<
uint16_t
,
bool
,
float
,
long
double
>
();
CAF_CHECK_EQUAL
(
to_string
(
xs
),
"[uint16_t, bool, float, ldouble]"
);
CAF_CHECK_EQUAL
(
to_string
(
xs
),
"[uint16_t, bool, float, ldouble]"
);
}
}
CAF_TEST
(
type
ID
lists
are
concatenable
)
{
// 1 + 0
CHECK_EQ
((
make_type_id_list
<
int8_t
>
()),
type_id_list
::
concat
(
make_type_id_list
<
int8_t
>
(),
make_type_id_list
<>
()));
CHECK_EQ
((
make_type_id_list
<
int8_t
>
()),
type_id_list
::
concat
(
make_type_id_list
<>
(),
make_type_id_list
<
int8_t
>
()));
// 1 + 1
CHECK_EQ
((
make_type_id_list
<
int8_t
,
int16_t
>
()),
type_id_list
::
concat
(
make_type_id_list
<
int8_t
>
(),
make_type_id_list
<
int16_t
>
()));
// 2 + 0
CHECK_EQ
((
make_type_id_list
<
int8_t
,
int16_t
>
()),
type_id_list
::
concat
(
make_type_id_list
<
int8_t
,
int16_t
>
(),
make_type_id_list
<>
()));
CHECK_EQ
((
make_type_id_list
<
int8_t
,
int16_t
>
()),
type_id_list
::
concat
(
make_type_id_list
<>
(),
make_type_id_list
<
int8_t
,
int16_t
>
()));
// 2 + 1
CHECK_EQ
((
make_type_id_list
<
int8_t
,
int16_t
,
int32_t
>
()),
type_id_list
::
concat
(
make_type_id_list
<
int8_t
,
int16_t
>
(),
make_type_id_list
<
int32_t
>
()));
CHECK_EQ
((
make_type_id_list
<
int8_t
,
int16_t
,
int32_t
>
()),
type_id_list
::
concat
(
make_type_id_list
<
int8_t
>
(),
make_type_id_list
<
int16_t
,
int32_t
>
()));
// 2 + 2
CHECK_EQ
((
make_type_id_list
<
int8_t
,
int16_t
,
int32_t
,
int64_t
>
()),
type_id_list
::
concat
(
make_type_id_list
<
int8_t
,
int16_t
>
(),
make_type_id_list
<
int32_t
,
int64_t
>
()));
}
libcaf_core/test/typed_response_promise.cpp
View file @
1de110dd
...
@@ -4,190 +4,159 @@
...
@@ -4,190 +4,159 @@
#define CAF_SUITE typed_response_promise
#define CAF_SUITE typed_response_promise
#include "core-test.hpp"
#include "caf/typed_response_promise.hpp"
#include <map>
#include "c
af/all
.hpp"
#include "c
ore-test
.hpp"
using
namespace
caf
;
using
namespace
caf
;
namespace
{
namespace
{
using
promise_actor
=
typed_actor
<
using
testee_actor
=
typed_actor
<
result
<
int
>
(
int
,
int
),
result
<
void
>
(
ok_atom
)
>
;
replies_to
<
int
>::
with
<
int
>
,
replies_to
<
get_atom
,
int
>::
with
<
int
>
,
replies_to
<
get_atom
,
int
,
int
>::
with
<
int
,
int
>
,
replies_to
<
get_atom
,
double
>::
with
<
double
>
,
replies_to
<
get_atom
,
double
,
double
>::
with
<
double
,
double
>
,
reacts_to
<
put_atom
,
int
,
int
>
,
reacts_to
<
put_atom
,
int
,
int
,
int
>>
;
using
foo_promise
=
typed_response_promise
<
int
>
;
using
foo2_promise
=
typed_response_promise
<
int
,
int
>
;
using
foo3_promise
=
typed_response_promise
<
double
>
;
using
get1_helper
=
typed_actor
<
replies_to
<
int
,
int
>::
with
<
put_atom
,
int
,
int
>>
;
testee_actor
::
behavior_type
adder
()
{
using
get2_helper
return
{
=
typed_actor
<
replies_to
<
int
,
int
,
int
>::
with
<
put_atom
,
int
,
int
,
int
>>
;
[](
int
x
,
int
y
)
{
return
x
+
y
;
},
[](
ok_atom
)
{},
class
promise_actor_impl
:
public
promise_actor
::
base
{
};
public:
}
promise_actor_impl
(
actor_config
&
cfg
)
:
promise_actor
::
base
(
cfg
)
{
// nop
}
behavior_type
make_behavior
()
override
{
testee_actor
::
behavior_type
delegator
(
testee_actor
::
pointer
self
,
testee_actor
worker
)
{
return
{
return
{
[
=
](
int
x
)
->
foo_promise
{
[
=
](
int
x
,
int
y
)
{
auto
resp
=
response
(
x
*
2
);
auto
promise
=
self
->
make_response_promise
<
int
>
();
CAF_CHECK
(
!
resp
.
pending
());
return
promise
.
delegate
(
worker
,
x
,
y
);
return
resp
.
deliver
(
x
*
4
);
// has no effect
},
},
[
=
](
get_atom
,
int
x
)
->
foo_promise
{
[
=
](
ok_atom
)
{
auto
calculator
=
spawn
([]()
->
get1_helper
::
behavior_type
{
auto
promise
=
self
->
make_response_promise
<
void
>
();
return
{[](
int
promise_id
,
int
value
)
->
result
<
put_atom
,
int
,
int
>
{
return
promise
.
delegate
(
worker
,
ok_atom_v
);
return
{
put_atom_v
,
promise_id
,
value
*
2
};
}};
});
send
(
calculator
,
next_id_
,
x
);
auto
&
entry
=
promises_
[
next_id_
++
];
entry
=
make_response_promise
<
foo_promise
>
();
return
entry
;
},
},
[
=
](
get_atom
,
int
x
,
int
y
)
->
foo2_promise
{
};
auto
calculator
=
spawn
([]()
->
get2_helper
::
behavior_type
{
}
return
{[](
int
promise_id
,
int
v0
,
int
v1
)
->
result
<
put_atom
,
int
,
int
,
int
>
{
testee_actor
::
behavior_type
requester_v1
(
testee_actor
::
pointer
self
,
return
{
put_atom_v
,
promise_id
,
v0
*
2
,
v1
*
2
};
testee_actor
worker
)
{
}};
return
{
});
[
=
](
int
x
,
int
y
)
{
send
(
calculator
,
next_id_
,
x
,
y
);
auto
rp
=
self
->
make_response_promise
<
int
>
();
auto
&
entry
=
promises2_
[
next_id_
++
];
self
->
request
(
worker
,
infinite
,
x
,
y
)
entry
=
make_response_promise
<
foo2_promise
>
();
.
then
(
// verify move semantics
[
rp
](
int
result
)
mutable
{
CAF_CHECK
(
entry
.
pending
());
CHECK
(
rp
.
pending
());
foo2_promise
tmp
(
std
::
move
(
entry
));
rp
.
deliver
(
result
);
CAF_CHECK
(
!
entry
.
pending
());
CAF_CHECK
(
tmp
.
pending
());
entry
=
std
::
move
(
tmp
);
CAF_CHECK
(
entry
.
pending
());
CAF_CHECK
(
!
tmp
.
pending
());
return
entry
;
},
},
[
=
](
get_atom
,
double
)
->
foo3_promise
{
[
rp
](
error
err
)
mutable
{
auto
resp
=
make_response_promise
<
double
>
();
CHECK
(
rp
.
pending
());
return
resp
.
deliver
(
make_error
(
sec
::
unexpected_message
));
rp
.
deliver
(
std
::
move
(
err
));
});
return
rp
;
},
},
[
=
](
get_atom
,
double
x
,
double
y
)
{
return
response
(
x
*
2
,
y
*
2
);
},
[
=
](
ok_atom
)
{
[
=
](
put_atom
,
int
promise_id
,
int
x
)
{
auto
rp
=
self
->
make_response_promise
<
void
>
();
auto
i
=
promises_
.
find
(
promise_id
);
self
->
request
(
worker
,
infinite
,
ok_atom_v
)
if
(
i
==
promises_
.
end
())
.
then
(
return
;
[
rp
]()
mutable
{
i
->
second
.
deliver
(
x
);
CHECK
(
rp
.
pending
()
);
promises_
.
erase
(
i
);
rp
.
deliver
(
);
},
},
[
=
](
put_atom
,
int
promise_id
,
int
x
,
int
y
)
{
[
rp
](
error
err
)
mutable
{
auto
i
=
promises2_
.
find
(
promise_id
);
CHECK
(
rp
.
pending
());
if
(
i
==
promises2_
.
end
())
rp
.
deliver
(
std
::
move
(
err
));
return
;
});
i
->
second
.
deliver
(
x
,
y
);
return
rp
;
promises2_
.
erase
(
i
);
},
},
};
};
}
private:
int
next_id_
=
0
;
std
::
map
<
int
,
foo_promise
>
promises_
;
std
::
map
<
int
,
foo2_promise
>
promises2_
;
};
struct
fixture
{
fixture
()
:
system
(
cfg
),
self
(
system
,
true
),
foo
(
system
.
spawn
<
promise_actor_impl
>
())
{
// nop
}
actor_system_config
cfg
;
actor_system
system
;
scoped_actor
self
;
promise_actor
foo
;
};
}
// namespace
CAF_TEST_FIXTURE_SCOPE
(
typed_spawn_tests
,
fixture
)
CAF_TEST
(
typed_response_promise
)
{
typed_response_promise
<
int
>
resp
;
CAF_MESSAGE
(
"trigger 'invalid response promise' error"
);
resp
.
deliver
(
1
);
// delivers on an invalid promise has no effect
auto
f
=
make_function_view
(
foo
);
CAF_CHECK_EQUAL
(
f
(
get_atom_v
,
42
),
84
);
CAF_CHECK_EQUAL
(
f
(
get_atom_v
,
42
,
52
),
std
::
make_tuple
(
84
,
104
));
CAF_CHECK_EQUAL
(
f
(
get_atom_v
,
3.14
,
3.14
),
std
::
make_tuple
(
6.28
,
6.28
));
}
}
CAF_TEST
(
typed_response_promise_chained
)
{
testee_actor
::
behavior_type
requester_v2
(
testee_actor
::
pointer
self
,
auto
f
=
make_function_view
(
foo
*
foo
*
foo
);
testee_actor
worker
)
{
CAF_CHECK_EQUAL
(
f
(
1
),
8
);
return
{
[
=
](
int
x
,
int
y
)
{
auto
rp
=
self
->
make_response_promise
<
int
>
();
auto
deliver
=
[
rp
](
expected
<
int
>
x
)
mutable
{
CHECK
(
rp
.
pending
());
rp
.
deliver
(
std
::
move
(
x
));
};
self
->
request
(
worker
,
infinite
,
x
,
y
)
.
then
([
deliver
](
int
result
)
mutable
{
deliver
(
result
);
},
[
deliver
](
error
err
)
mutable
{
deliver
(
std
::
move
(
err
));
});
return
rp
;
},
[
=
](
ok_atom
)
{
auto
rp
=
self
->
make_response_promise
<
void
>
();
auto
deliver
=
[
rp
](
expected
<
void
>
x
)
mutable
{
CHECK
(
rp
.
pending
());
rp
.
deliver
(
std
::
move
(
x
));
};
self
->
request
(
worker
,
infinite
,
ok_atom_v
)
.
then
([
deliver
]()
mutable
{
deliver
({});
},
[
deliver
](
error
err
)
mutable
{
deliver
(
std
::
move
(
err
));
});
return
rp
;
},
};
}
}
// verify that only requests get an error response message
}
// namespace
CAF_TEST
(
error_response_message
)
{
auto
f
=
make_function_view
(
foo
);
CAF_CHECK_EQUAL
(
f
(
get_atom_v
,
3.14
),
sec
::
unexpected_message
);
self
->
send
(
foo
,
get_atom_v
,
42
);
self
->
receive
([](
int
x
)
{
CAF_CHECK_EQUAL
(
x
,
84
);
},
[](
double
x
)
{
CAF_ERROR
(
"unexpected ordinary response message received: "
<<
x
);
});
self
->
send
(
foo
,
get_atom_v
,
3.14
);
self
->
receive
([
&
](
error
&
err
)
{
CAF_CHECK_EQUAL
(
err
,
sec
::
unexpected_message
);
self
->
send
(
self
,
message
{});
});
}
// verify that delivering to a satisfied promise has no effect
CAF_TEST_FIXTURE_SCOPE
(
typed_response_promise_tests
,
test_coordinator_fixture
<>
)
CAF_TEST
(
satisfied_promise
)
{
self
->
send
(
foo
,
1
);
SCENARIO
(
"response promises allow delaying of response messages"
)
{
self
->
send
(
foo
,
get_atom_v
,
3.14
,
3.14
);
auto
adder_hdl
=
sys
.
spawn
(
adder
);
int
i
=
0
;
std
::
map
<
std
::
string
,
testee_actor
>
impls
;
self
->
receive_for
(
i
,
2
)([](
int
x
)
{
CAF_CHECK_EQUAL
(
x
,
1
*
2
);
},
impls
[
"with a value or an error"
]
=
sys
.
spawn
(
requester_v1
,
adder_hdl
);
[](
double
x
,
double
y
)
{
impls
[
"with an expected<T>"
]
=
sys
.
spawn
(
requester_v2
,
adder_hdl
);
CAF_CHECK_EQUAL
(
x
,
3.14
*
2
);
for
(
auto
&
[
desc
,
hdl
]
:
impls
)
{
CAF_CHECK_EQUAL
(
y
,
3.14
*
2
);
GIVEN
(
"a dispatcher that calls deliver "
<<
desc
<<
" on its promise"
)
{
WHEN
(
"sending a request with two integers to the dispatcher"
)
{
inject
((
int
,
int
),
from
(
self
).
to
(
hdl
).
with
(
3
,
4
));
THEN
(
"clients receive the response from the dispatcher"
)
{
expect
((
int
,
int
),
from
(
hdl
).
to
(
adder_hdl
).
with
(
3
,
4
));
expect
((
int
),
from
(
adder_hdl
).
to
(
hdl
).
with
(
7
));
expect
((
int
),
from
(
hdl
).
to
(
self
).
with
(
7
));
}
}
WHEN
(
"sending ok_atom to the dispatcher synchronously"
)
{
auto
res
=
self
->
request
(
hdl
,
infinite
,
ok_atom_v
);
auto
fetch_result
=
[
&
]
{
message
result
;
res
.
receive
([]
{},
// void result
[
&
](
const
error
&
reason
)
{
result
=
make_message
(
reason
);
});
});
return
result
;
};
THEN
(
"clients receive an empty response from the dispatcher"
)
{
expect
((
ok_atom
),
from
(
self
).
to
(
hdl
));
expect
((
ok_atom
),
from
(
hdl
).
to
(
adder_hdl
));
expect
((
void
),
from
(
adder_hdl
).
to
(
hdl
));
CHECK
(
fetch_result
().
empty
());
}
}
WHEN
(
"sending ok_atom to the dispatcher asynchronously"
)
{
THEN
(
"clients receive no response from the dispatcher"
)
{
inject
((
ok_atom
),
from
(
self
).
to
(
hdl
).
with
(
ok_atom_v
));
expect
((
ok_atom
),
from
(
hdl
).
to
(
adder_hdl
));
expect
((
void
),
from
(
adder_hdl
).
to
(
hdl
));
CHECK
(
self
->
mailbox
().
empty
());
}
}
}
}
}
}
CAF_TEST
(
delegating_promises
)
{
SCENARIO
(
"response promises allow delegation"
)
{
using
task
=
std
::
pair
<
typed_response_promise
<
int
>
,
int
>
;
GIVEN
(
"a dispatcher that calls delegate on its promise"
)
{
struct
state
{
auto
adder_hdl
=
sys
.
spawn
(
adder
);
std
::
vector
<
task
>
tasks
;
auto
hdl
=
sys
.
spawn
(
delegator
,
adder_hdl
);
};
WHEN
(
"sending a request to the dispatcher"
)
{
using
bar_actor
=
typed_actor
<
replies_to
<
int
>::
with
<
int
>
,
reacts_to
<
ok_atom
>>
;
inject
((
int
,
int
),
from
(
self
).
to
(
hdl
).
with
(
3
,
4
));
auto
bar_fun
=
[](
bar_actor
::
stateful_pointer
<
state
>
self
,
THEN
(
"clients receive the response from the adder"
)
{
promise_actor
worker
)
->
bar_actor
::
behavior_type
{
expect
((
int
,
int
),
from
(
self
).
to
(
adder_hdl
).
with
(
3
,
4
));
return
{
expect
((
int
),
from
(
adder_hdl
).
to
(
self
).
with
(
7
));
[
=
](
int
x
)
->
typed_response_promise
<
int
>
{
}
auto
&
tasks
=
self
->
state
.
tasks
;
}
tasks
.
emplace_back
(
self
->
make_response_promise
<
int
>
(),
x
);
self
->
send
(
self
,
ok_atom_v
);
return
tasks
.
back
().
first
;
},
[
=
](
ok_atom
)
{
auto
&
tasks
=
self
->
state
.
tasks
;
if
(
!
tasks
.
empty
())
{
auto
&
task
=
tasks
.
back
();
task
.
first
.
delegate
(
worker
,
task
.
second
);
tasks
.
pop_back
();
}
}
},
};
};
auto
f
=
make_function_view
(
system
.
spawn
(
bar_fun
,
foo
));
CAF_CHECK_EQUAL
(
f
(
42
),
84
);
}
}
CAF_TEST_FIXTURE_SCOPE_END
()
CAF_TEST_FIXTURE_SCOPE_END
()
libcaf_io/caf/io/basp/message_type.hpp
View file @
1de110dd
...
@@ -69,8 +69,8 @@ CAF_IO_EXPORT bool from_string(string_view, message_type&);
...
@@ -69,8 +69,8 @@ CAF_IO_EXPORT bool from_string(string_view, message_type&);
CAF_IO_EXPORT
bool
from_integer
(
std
::
underlying_type_t
<
message_type
>
,
CAF_IO_EXPORT
bool
from_integer
(
std
::
underlying_type_t
<
message_type
>
,
message_type
&
);
message_type
&
);
template
<
class
Ins
s
ector
>
template
<
class
Ins
p
ector
>
bool
inspect
(
Ins
s
ector
&
f
,
message_type
&
x
)
{
bool
inspect
(
Ins
p
ector
&
f
,
message_type
&
x
)
{
return
default_enum_inspect
(
f
,
x
);
return
default_enum_inspect
(
f
,
x
);
}
}
...
...
libcaf_io/src/io/middleman.cpp
View file @
1de110dd
...
@@ -391,7 +391,7 @@ strong_actor_ptr middleman::remote_lookup(std::string name,
...
@@ -391,7 +391,7 @@ strong_actor_ptr middleman::remote_lookup(std::string name,
make_message
(
registry_lookup_atom_v
,
std
::
move
(
name
)));
make_message
(
registry_lookup_atom_v
,
std
::
move
(
name
)));
self
->
receive
(
self
->
receive
(
[
&
](
strong_actor_ptr
&
addr
)
{
result
=
std
::
move
(
addr
);
},
[
&
](
strong_actor_ptr
&
addr
)
{
result
=
std
::
move
(
addr
);
},
others
>>
[](
message
&
msg
)
->
skippable_result
{
others
>>
[](
[[
maybe_unused
]]
message
&
msg
)
->
skippable_result
{
CAF_LOG_ERROR
(
CAF_LOG_ERROR
(
"middleman received unexpected remote_lookup result:"
<<
msg
);
"middleman received unexpected remote_lookup result:"
<<
msg
);
return
message
{};
return
message
{};
...
...
manual/Actors.rst
View file @
1de110dd
...
@@ -250,14 +250,15 @@ messaging interface for a simple calculator.
...
@@ -250,14 +250,15 @@ messaging interface for a simple calculator.
.. literalinclude:: /examples/message_passing/calculator.cpp
.. literalinclude:: /examples/message_passing/calculator.cpp
:language: C++
:language: C++
:lines: 17-18
:start-after: --(rst-calculator-actor-begin)--
:end-before: --(rst-calculator-actor-end)--
It is not required to create a type alias such as ``calculator_actor``,
It is not required to create a type alias such as ``calculator_actor``,
but it makes dealing with statically typed actors much easier. Also, a central
but it makes dealing with statically typed actors much easier. Also, a central
alias definition eases refactoring later on.
alias definition eases refactoring later on.
Interfaces have set semantics. This means the following two type aliases
Interfaces have set semantics. This means the following two type aliases
``i1`` and ``i2`` are
equal
:
``i1`` and ``i2`` are
considered equal by CAF
:
.. code-block:: C++
.. code-block:: C++
...
@@ -300,13 +301,15 @@ parameter. For example, the following functions and classes represent actors.
...
@@ -300,13 +301,15 @@ parameter. For example, the following functions and classes represent actors.
.. literalinclude:: /examples/message_passing/calculator.cpp
.. literalinclude:: /examples/message_passing/calculator.cpp
:language: C++
:language: C++
:lines: 21-26
:start-after: --(rst-prototypes-begin)--
:end-before: --(rst-prototypes-end)--
Spawning an actor for each implementation is illustrated below.
Spawning an actor for each implementation is illustrated below.
.. literalinclude:: /examples/message_passing/calculator.cpp
.. literalinclude:: /examples/message_passing/calculator.cpp
:language: C++
:language: C++
:lines: 123-128
:start-after: --(rst-spawn-begin)--
:end-before: --(rst-spawn-end)--
Additional arguments to ``spawn`` are passed to the constructor of a
Additional arguments to ``spawn`` are passed to the constructor of a
class or used as additional function arguments, respectively. In the example
class or used as additional function arguments, respectively. In the example
...
@@ -344,7 +347,8 @@ dynamically typed).
...
@@ -344,7 +347,8 @@ dynamically typed).
.. literalinclude:: /examples/message_passing/calculator.cpp
.. literalinclude:: /examples/message_passing/calculator.cpp
:language: C++
:language: C++
:lines: 28-56
:start-after: --(rst-function-based-begin)--
:end-before: --(rst-function-based-end)--
.. _class-based:
.. _class-based:
...
@@ -365,6 +369,14 @@ simple management of state via member variables. However, composing states via
...
@@ -365,6 +369,14 @@ simple management of state via member variables. However, composing states via
inheritance can get quite tedious. For dynamically typed actors, composing
inheritance can get quite tedious. For dynamically typed actors, composing
states is particularly hard, because the compiler cannot provide much help.
states is particularly hard, because the compiler cannot provide much help.
The following three classes implement the prototypes shown in spawn_ by
delegating to the function-based implementations we have seen before:
.. literalinclude:: /examples/message_passing/calculator.cpp
:language: C++
:start-after: --(rst-class-based-begin)--
:end-before: --(rst-class-based-end)--
.. _stateful-actor:
.. _stateful-actor:
Stateful Actors
Stateful Actors
...
@@ -405,7 +417,8 @@ printing a custom string on exit.
...
@@ -405,7 +417,8 @@ printing a custom string on exit.
.. literalinclude:: /examples/broker/simple_broker.cpp
.. literalinclude:: /examples/broker/simple_broker.cpp
:language: C++
:language: C++
:lines: 42-47
:start-after: --(rst-attach-begin)--
:end-before: --(rst-attach-end)--
It is possible to attach code to remote actors. However, the cleanup code will
It is possible to attach code to remote actors. However, the cleanup code will
run on the local machine.
run on the local machine.
...
@@ -461,7 +474,7 @@ before the optional timeout, as shown in the example below.
...
@@ -461,7 +474,7 @@ before the optional timeout, as shown in the example below.
[&](const exit_msg& x) {
[&](const exit_msg& x) {
// ...
// ...
},
},
others >> [](message
_view& x) -> result<message>
{
others >> [](message
& x) -> skippable_result
{
// report unexpected message back to client
// report unexpected message back to client
return sec::unexpected_message;
return sec::unexpected_message;
}
}
...
...
manual/ConfiguringActorApplications.rst
View file @
1de110dd
...
@@ -135,7 +135,8 @@ adds three options to the ``global`` category.
...
@@ -135,7 +135,8 @@ adds three options to the ``global`` category.
.. literalinclude:: /examples/remoting/distributed_calculator.cpp
.. literalinclude:: /examples/remoting/distributed_calculator.cpp
:language: C++
:language: C++
:lines: 206-218
:begin-after: --(rst-config-begin)--
:end-before: --(rst-config-end)--
We create a new ``global`` category in ``custom_options_``. Each following call
We create a new ``global`` category in ``custom_options_``. Each following call
to ``add`` then appends individual options to the category. The first argument
to ``add`` then appends individual options to the category. The first argument
...
...
manual/Error.rst
View file @
1de110dd
...
@@ -5,9 +5,7 @@ Errors
...
@@ -5,9 +5,7 @@ Errors
Errors in CAF have a code and a category, similar to ``std::error_code`` and
Errors in CAF have a code and a category, similar to ``std::error_code`` and
``std::error_condition``. Unlike its counterparts from the C++ standard library,
``std::error_condition``. Unlike its counterparts from the C++ standard library,
``error`` is plattform-neutral and serializable. Instead of using category
``error`` is plattform-neutral and serializable.
singletons, CAF stores categories as atoms (see :ref:`atom`). Errors can also
include a message to provide additional context information.
Class Interface
Class Interface
---------------
---------------
...
@@ -15,19 +13,17 @@ Class Interface
...
@@ -15,19 +13,17 @@ Class Interface
+-----------------------------------------+--------------------------------------------------------------------+
+-----------------------------------------+--------------------------------------------------------------------+
| **Constructors** | |
| **Constructors** | |
+-----------------------------------------+--------------------------------------------------------------------+
+-----------------------------------------+--------------------------------------------------------------------+
| ``(Enum
x)`` | Construct error by calling ``make_error(x)``
|
| ``(Enum
code)`` | Constructs an error with given error code.
|
+-----------------------------------------+--------------------------------------------------------------------+
+-----------------------------------------+--------------------------------------------------------------------+
| ``(uint8_t x, atom_value y)`` | Construct error with code ``x`` and category ``y`` |
| ``(Enum code, messag context)`` | Constructs an error with given error code and additional context. |
+-----------------------------------------+--------------------------------------------------------------------+
| ``(uint8_t x, atom_value y, message z)``| Construct error with code ``x``, category ``y``, and context ``z`` |
+-----------------------------------------+--------------------------------------------------------------------+
+-----------------------------------------+--------------------------------------------------------------------+
| | |
| | |
+-----------------------------------------+--------------------------------------------------------------------+
+-----------------------------------------+--------------------------------------------------------------------+
| **Observers** | |
| **Observers** | |
+-----------------------------------------+--------------------------------------------------------------------+
+-----------------------------------------+--------------------------------------------------------------------+
| ``uint8_t code()`` | Returns the error code
|
| ``uint8_t code()`` | Returns the error code
as 8-bit integer.
|
+-----------------------------------------+--------------------------------------------------------------------+
+-----------------------------------------+--------------------------------------------------------------------+
| ``
atom_value category()`` | Returns the error category
|
| ``
type_id_t category()`` | Returns the type ID of the Enum type used to construct this error.
|
+-----------------------------------------+--------------------------------------------------------------------+
+-----------------------------------------+--------------------------------------------------------------------+
| ``message context()`` | Returns additional context information |
| ``message context()`` | Returns additional context information |
+-----------------------------------------+--------------------------------------------------------------------+
+-----------------------------------------+--------------------------------------------------------------------+
...
@@ -39,43 +35,52 @@ Class Interface
...
@@ -39,43 +35,52 @@ Class Interface
Add Custom Error Categories
Add Custom Error Categories
---------------------------
---------------------------
Adding custom error categories requires three steps: (1) declare an enum class
Adding custom error categories requires these steps:
of type ``uint8_t`` with the first value starting at 1, (2) specialize
``error_category`` to give your type a custom ID (value 0-99 are
* Declare an enum class of type ``uint8_t`` with error codes starting at 1. CAF
reserved by CAF), and (3) add your error category to the actor system config.
always interprets the value 0 as *no error*.
The following example adds custom error codes for math errors.
* Assign a type ID to your enum type.
* Specialize ``caf::is_error_code_enum`` for your enum type. For this step, CAF
offers the macro ``CAF_ERROR_CODE_ENUM`` to generate the boilerplate code
necessary.
The following example illustrates all these steps for a custom error code enum
called ``math_error``.
.. literalinclude:: /examples/message_passing/divider.cpp
.. literalinclude:: /examples/message_passing/divider.cpp
:language: C++
:language: C++
:lines: 17-47
:start-after: --(rst-math-error-begin)--
:end-before: --(rst-math-error-end)--
.. _sec:
.. _sec:
System
Error Codes
Default
Error Codes
------------------
------------------
-
System Error Codes (SECs) use the error category ``"system"``. They
The enum type ``sec`` (for System Error Code) provides many error codes for
represent errors in the actor system or one of its modules and are defined as
common failures in actor systems:
follows.
.. literalinclude:: /libcaf_core/caf/sec.hpp
.. literalinclude:: /libcaf_core/caf/sec.hpp
:language: C++
:language: C++
:lines: 32-117
:start-after: --(rst-sec-begin)--
:end-before: --(rst-sec-end)--
.. _exit-reason:
.. _exit-reason:
Default Exit Reasons
Default Exit Reasons
--------------------
--------------------
CAF uses the error category ``"exit"`` for default exit reasons. These errors
A special kind of error codes are exit reasons of actors. These errors are
are
usually fail states set by the actor system itself. The two exceptions are
usually fail states set by the actor system itself. The two exceptions are
``exit_reason::user_shutdown`` and ``exit_reason::kill``. The former is used in
``exit_reason::user_shutdown`` and ``exit_reason::kill``. The former is used in
CAF to signalize orderly, user-requested shutdown and can be used by programmers
CAF to signalize orderly, user-requested shutdown and can be used by programmers
in the same way. The latter terminates an actor unconditionally when used in
in the same way. The latter terminates an actor unconditionally when used in
``send_exit``, even
if the default handler for exit messages
(see
``send_exit``, even
for actors that override the default handler
(see
:ref:`exit-message`)
is overridden
.
:ref:`exit-message`).
.. literalinclude:: /libcaf_core/caf/exit_reason.hpp
.. literalinclude:: /libcaf_core/caf/exit_reason.hpp
:language: C++
:language: C++
:
lines: 29-49
:
start-after: --(rst-exit-reason-begin)--
:end-before: --(rst-exit-reason-end)--
manual/FAQ.rst
View file @
1de110dd
...
@@ -36,19 +36,5 @@ generation, CAF also offers ``message_builder``:
...
@@ -36,19 +36,5 @@ generation, CAF also offers ``message_builder``:
What Debugging Tools Exist?
What Debugging Tools Exist?
---------------------------
---------------------------
The ``scripts/`` and ``tools/`` directories contain some useful tools to aid in
The ``scripts/`` directory contains some useful tools to aid in analyzing CAF
development and debugging.
log output.
``scripts/atom.py`` converts integer atom values back into strings.
``scripts/demystify.py`` replaces cryptic ``typed_mpi<...>``
templates with ``replies_to<...>::with<...>`` and
``atom_constant<...>`` with a human-readable representation of the
actual atom.
``scripts/caf-prof`` is an R script that generates plots from CAF
profiler output.
``caf-vec`` is a (highly) experimental tool that annotates CAF logs
with vector timestamps. It gives you happens-before relations and a nice
visualization via `ShiViz <https://bestchai.bitbucket.io/shiviz/>`_.
manual/GroupCommunication.rst
View file @
1de110dd
...
@@ -10,10 +10,8 @@ name, joining, and leaving.
...
@@ -10,10 +10,8 @@ name, joining, and leaving.
.. code-block:: C++
.. code-block:: C++
std::string module = "local";
auto expected_grp = system.groups().get("local", "foo");
std::string id = "foo";
if (!expected_grp) {
auto expected_grp = system.groups().get(module, id);
if (! expected_grp) {
std::cerr << "*** cannot load group: " << to_string(expected_grp.error())
std::cerr << "*** cannot load group: " << to_string(expected_grp.error())
<< std::endl;
<< std::endl;
return;
return;
...
...
manual/ManagingGroupsOfWorkers.rst
View file @
1de110dd
...
@@ -85,10 +85,9 @@ This policy models split/join or scatter/gather work flows, where a work item
...
@@ -85,10 +85,9 @@ This policy models split/join or scatter/gather work flows, where a work item
is split into as many tasks as workers are available and then the individuals
is split into as many tasks as workers are available and then the individuals
results are joined together before sending the full result back to the client.
results are joined together before sending the full result back to the client.
The join function is responsible for ``glueing'' all result messages together
The join function is responsible for "glueing" all result messages together to
to create a single result. The function is called with the result object
create a single result. The function is called with the result object (initialed
(initialed using ``init``) and the current result messages from a
using ``init``) and the current result messages from a worker.
worker.
The first argument of a split function is a mapping from actors (workers) to
The first argument of a split function is a mapping from actors (workers) to
tasks (messages). The second argument is the input message. The default split
tasks (messages). The second argument is the input message. The default split
...
...
manual/MessageHandlers.rst
View file @
1de110dd
...
@@ -17,9 +17,9 @@ change its behavior when not receiving message after a certain amount of time.
...
@@ -17,9 +17,9 @@ change its behavior when not receiving message after a certain amount of time.
.. code-block:: C++
.. code-block:: C++
message_handler x1{
message_handler x1{
[](int i) { /*...*/ },
[](int
32_t
i) { /*...*/ },
[](double db) { /*...*/ },
[](double db) { /*...*/ },
[](int
a, int b, in
t c) { /*...*/ }
[](int
32_t a, int32_t b, int32_
t c) { /*...*/ }
};
};
In our first example, ``x1`` models a behavior accepting messages that consist
In our first example, ``x1`` models a behavior accepting messages that consist
...
@@ -70,48 +70,33 @@ introduced an approach to use non-numerical constants, so-called
...
@@ -70,48 +70,33 @@ introduced an approach to use non-numerical constants, so-called
*atoms*, which have an unambiguous, special-purpose type and do not have
*atoms*, which have an unambiguous, special-purpose type and do not have
the runtime overhead of string constants.
the runtime overhead of string constants.
Atoms in CAF are mapped to integer values at compile time. This mapping is
Atoms in CAF are tag types, i.e., usually defined as en empty ``struct``. These
guaranteed to be collision-free and invertible, but limits atom literals to ten
types carry no data on their own and only exist to annotate messages. For
characters and prohibits special characters. Legal characters are
example, we could create the two tag types ``add_atom`` and ``multiply_atom``
``_0-9A-Za-z`` and the whitespace character. Atoms are created using
for implementing a simple math actor like this:
the ``constexpr`` function ``atom``, as the following example
illustrates.
.. code-block:: C++
.. code-block:: C++
atom_value a1 = atom("add");
CAF_BEGIN_TYPE_ID_BLOCK(my_project, caf::first_custom_type_id)
atom_value a2 = atom("multiply");
**Warning**: The compiler cannot enforce the restrictions at compile time,
CAF_ADD_ATOM(my_project, add_atom)
except for a length check. The assertion ``atom("!?") != atom("?!")``
CAF_ADD_ATOM(my_project, multiply_atom)
is not true, because each invalid character translates to a whitespace
character.
While the ``atom_value`` is computed at compile time, it is not
CAF_END_TYPE_ID_BLOCK(my_project)
uniquely typed and thus cannot be used in the signature of a callback. To
accomplish this, CAF offers compile-time *atom constants*.
.. code-block:: C++
using add_atom = atom_constant<atom("add")>;
using multiply_atom = atom_constant<atom("multiply")>;
Using these constants, we can now define message passing interfaces in a
convenient way:
.. code-block:: C++
behavior do_math{
behavior do_math{
[](add_atom, int
a, in
t b) {
[](add_atom, int
32_t a, int32_
t b) {
return a + b;
return a + b;
},
},
[](multiply_atom, int
a, in
t b) {
[](multiply_atom, int
32_t a, int32_
t b) {
return a * b;
return a * b;
}
}
};
};
// caller side: send(math_actor, add_atom::value, 1, 2)
Atom constants define a static member ``value``. Please note that this
// caller side: send(math_actor, add_atom_v, int32_t{1}, int32_t{2})
static ``value`` member does *not* have the type
``atom_value``, unlike ``std::integral_constant`` for example.
The macro ``CAF_ADD_ATOM`` defined an empty ``struct`` with the given name as
well as a ``constexpr`` variable for conveniently creating a value of that type
that uses the type name plus a ``_v`` suffix. In the example above,
``atom_value`` is the type name and ``atom_value_v`` is the constant.
manual/MessagePassing.rst
View file @
1de110dd
...
@@ -3,18 +3,12 @@
...
@@ -3,18 +3,12 @@
Message Passing
Message Passing
===============
===============
Message passing in CAF is always asynchronous. Further, CAF neither guarantees
message delivery nor message ordering in a distributed setting. CAF uses TCP
per default, but also enables nodes to send messages to other nodes without
having a direct connection. In this case, messages are forwarded by
intermediate nodes and can get lost if one of the forwarding nodes fails.
Likewise, forwarding paths can change dynamically and thus cause messages to
arrive out of order.
The messaging layer of CAF has three primitives for sending messages: ``send``,
The messaging layer of CAF has three primitives for sending messages: ``send``,
``request``, and ``delegate``. The former simply enqueues a message to the
``request``, and ``delegate``. The former simply enqueues a message to the
mailbox the receiver. The latter two are discussed in more detail in
mailbox the receiver. The latter two are discussed in more detail in
:ref:`request` and :ref:`delegate`.
:ref:`request` and :ref:`delegate`. Before we go into the details of the message
passing API itself, we first discuss the building blocks that enable message
passing in the first place.
.. _mailbox-element:
.. _mailbox-element:
...
@@ -37,17 +31,12 @@ request. The ``stages`` vector stores the path of the message. Response
...
@@ -37,17 +31,12 @@ request. The ``stages`` vector stores the path of the message. Response
messages, i.e., the returned values of a message handler, are sent to
messages, i.e., the returned values of a message handler, are sent to
``stages.back()`` after calling ``stages.pop_back()``. This allows CAF to build
``stages.back()`` after calling ``stages.pop_back()``. This allows CAF to build
pipelines of arbitrary size. If no more stage is left, the response reaches the
pipelines of arbitrary size. If no more stage is left, the response reaches the
sender. Finally, ``content()`` grants access to the type-erased tuple storing
sender. Finally, ``payload`` is the actual content of the message.
the message itself.
Mailbox elements are created by CAF automatically and are usually invisible to
Mailbox elements are created by CAF automatically and are usually invisible to
the programmer. However, understanding how messages are processed internally
the programmer. However, understanding how messages are processed internally
helps understanding the behavior of the message passing layer.
helps understanding the behavior of the message passing layer.
It is worth mentioning that CAF usually wraps the mailbox element and its
content into a single object in order to reduce the number of memory
allocations.
.. _copy-on-write:
.. _copy-on-write:
Copy on Write
Copy on Write
...
@@ -65,19 +54,15 @@ Requirements for Message Types
...
@@ -65,19 +54,15 @@ Requirements for Message Types
Message types in CAF must meet the following requirements:
Message types in CAF must meet the following requirements:
1.
Serializable or i
nspectable (see :ref:`type-inspection`)
1.
I
nspectable (see :ref:`type-inspection`)
2. Default constructible
2. Default constructible
3. Copy constructible
3. Copy constructible
A type is serializable if it provides free function
A type ``T`` is inspectable if it provides a free function
``serialize(Serializer&, T&)`` or
``inspect(Inspector&, T&)`` or specializes ``inspector_access``.
``serialize(Serializer&, T&, const unsigned int)``. Accordingly, a type is
inspectable if it provides a free function ``inspect(Inspector&, T&)``.
Requirement 2 is a consequence of requirement 1, because CAF needs to be able to
Requirement 2 is a consequence of requirement 1, because CAF needs to be able to
create an object of a type before it can call ``serialize`` or ``inspect`` on
create an object for ``T`` when deserializing incoming messages. Requirement 3
it. Requirement 3 allows CAF to implement Copy on Write (see
allows CAF to implement Copy on Write (see :ref:`copy-on-write`).
:ref:`copy-on-write`).
.. _special-handler:
.. _special-handler:
...
@@ -158,12 +143,12 @@ Requests
...
@@ -158,12 +143,12 @@ Requests
--------
--------
A main feature of CAF is its ability to couple input and output types via the
A main feature of CAF is its ability to couple input and output types via the
type system. For example, a ``typed_actor<re
plies_to<int>::with<int>
>``
type system. For example, a ``typed_actor<re
sult<int32_t>(int32_t)
>``
essentially behaves like a function. It receives a single ``int
`` as
essentially behaves like a function. It receives a single ``int
32_t`` as input
input and responds with another ``int``. CAF embraces this functional
and responds with another ``int32_t``. CAF embraces this functional take on
take on actors by simply creating response messages from the result of message
actors by simply creating response messages from the result of message handlers.
handlers. This allows CAF to match *request* to *response* messages
This allows CAF to match *request* to *response* messages and to provide a
and to provide a
convenient API for this style of communication.
convenient API for this style of communication.
.. _handling-response:
.. _handling-response:
...
@@ -181,42 +166,31 @@ responses arrive and handles requests in LIFO order. Blocking actors always use
...
@@ -181,42 +166,31 @@ responses arrive and handles requests in LIFO order. Blocking actors always use
Actors receive a ``sec::request_timeout`` (see :ref:`sec`) error message (see
Actors receive a ``sec::request_timeout`` (see :ref:`sec`) error message (see
:ref:`error-message`) if a timeout occurs. Users can set the timeout to
:ref:`error-message`) if a timeout occurs. Users can set the timeout to
``infinite`` for unbound operations. This is only recommended if the receiver is
``infinite`` for unbound operations. This is only recommended if the receiver is
running locally.
known to run locally.
In our following example, we use the simple cell actors shown below as
communication endpoints.
.. literalinclude:: /examples/message_passing/request.cpp
:language: C++
:lines: 20-37
The first part of the example illustrates how event-based actors can use either
In our following example, we use the simple cell actor shown below as
``then`` or ``await``
.
communication endpoint
.
.. literalinclude:: /examples/message_passing/request.cpp
.. literalinclude:: /examples/message_passing/request.cpp
:language: C++
:language: C++
:lines: 39-51
:start-after: --(rst-cell-begin)--
:end-before: --(rst-cell-end)--
The second half of the example shows a blocking actor making use of
To showcase the slight differences in API and processing order, we implement
``receive``. Note that blocking actors have no special-purpose handler
three testee actors that all operate on a list of cell actors.
for error messages and therefore are required to pass a callback for error
messages when handling response messages.
.. literalinclude:: /examples/message_passing/request.cpp
.. literalinclude:: /examples/message_passing/request.cpp
:language: C++
:language: C++
:lines: 53-64
:start-after: --(rst-testees-begin)--
:end-before: --(rst-testees-end)--
We spawn five cells and assign the values 0, 1, 4, 9, and 16.
.. literalinclude:: /examples/message_passing/request.cpp
Our ``caf_main`` for the examples spawns five cells and assign the initial
:language: C++
values 0, 1, 4, 9, and 16. Then it spawns one instance for each of our testee
:lines: 67-69
implementations and we can observe the different outputs.
When passing the ``cells`` vector to our three different
Our ``waiting_testee`` actor will always print:
implementations, we observe three outputs. Our ``waiting_testee`` actor
will always print:
..
::
..
code-block:: none
cell #9 -> 16
cell #9 -> 16
cell #8 -> 9
cell #8 -> 9
...
@@ -225,15 +199,17 @@ will always print:
...
@@ -225,15 +199,17 @@ will always print:
cell #5 -> 0
cell #5 -> 0
This is because ``await`` puts the one-shots handlers onto a stack and
This is because ``await`` puts the one-shots handlers onto a stack and
enforces LIFO order by re-ordering incoming response messages.
enforces LIFO order by re-ordering incoming response messages
as necessary
.
The ``multiplexed_testee`` implementation does not print its results in
The ``multiplexed_testee`` implementation does not print its results in
a predicable order. Response messages arrive in arbitrary order and are handled
a predicable order. Response messages arrive in arbitrary order and are handled
immediately.
immediately.
Finally, the ``blocking_testee`` implementation will always print:
Finally, the ``blocking_testee`` has a deterministic output again. This is
because it blocks on each request until receiving the result before making the
next request.
..
::
..
code-block:: none
cell #5 -> 0
cell #5 -> 0
cell #6 -> 1
cell #6 -> 1
...
@@ -254,17 +230,17 @@ manager fans-out the request to all of its workers and then collects the
...
@@ -254,17 +230,17 @@ manager fans-out the request to all of its workers and then collects the
results. The function ``fan_out_request`` combined with the merge policy
results. The function ``fan_out_request`` combined with the merge policy
``select_all`` streamlines this exact use case.
``select_all`` streamlines this exact use case.
In the following snippet, we have a matrix actor (``self``) that stores
In the following snippet, we have a matrix actor ``self`` that stores worker
worker actors for each cell (each simply storing an integer). For computing the
actors for each cell (each simply storing an integer). For computing the average
average over a row, we use ``fan_out_request``. The result handler
over a row, we use ``fan_out_request``. The result handler passed to ``then``
passed to ``then`` now gets called only once with a ``vector``
now gets called only once with a ``vector`` holding all collected results. Using
holding all collected results. Using a response promise promise_ further
a response promise promise_ further allows us to delay responding to the client
allows us to delay responding to the client until we have collected all worker
until we have collected all worker results.
results.
.. literalinclude:: /examples/message_passing/fan_out_request.cpp
.. literalinclude:: /examples/message_passing/fan_out_request.cpp
:language: C++
:language: C++
:lines: 86-98
:start-after: --(rst-fan-out-begin)--
:end-before: --(rst-fan-out-end)--
The policy ``select_any`` models a second common use case: sending a
The policy ``select_any`` models a second common use case: sending a
request to multiple receivers but only caring for the first arriving response.
request to multiple receivers but only caring for the first arriving response.
...
@@ -286,26 +262,32 @@ by zero. This examples uses a custom error category (see :ref:`error`).
...
@@ -286,26 +262,32 @@ by zero. This examples uses a custom error category (see :ref:`error`).
.. literalinclude:: /examples/message_passing/divider.cpp
.. literalinclude:: /examples/message_passing/divider.cpp
:language: C++
:language: C++
:lines: 17-19,49-59
:start-after: --(rst-divider-begin)--
:end-before: --(rst-divider-end)--
When sending requests to the divider, we
use a custom error handlers to report
When sending requests to the divider, we
can use a custom error handlers to
errors to the user.
report errors to the user like this:
.. literalinclude:: /examples/message_passing/divider.cpp
.. literalinclude:: /examples/message_passing/divider.cpp
:language: C++
:language: C++
:lines: 70-76
:start-after: --(rst-request-begin)--
:end-before: --(rst-request-end)--
.. _delay-message:
.. _delay-message:
Delaying Messages
Delaying Messages
-----------------
-----------------
Messages can be delayed by using the function ``delayed_send``, as
Messages can be delayed by using the function ``delayed_send``, as
illustrated
i
llustrated i
n the following time-based loop example.
in the following time-based loop example.
.. literalinclude:: /examples/message_passing/dancing_kirby.cpp
.. literalinclude:: /examples/message_passing/dancing_kirby.cpp
:language: C++
:language: C++
:lines: 58-75
:start-after: --(rst-delayed-send-begin)--
:end-before: --(rst-delayed-send-end)--
Delayed send schedules messages based on relative timeouts. For absolute
timeouts, use ``scheduled_send`` instead.
.. _delegate:
.. _delegate:
...
@@ -318,7 +300,7 @@ by returning a value from its message handler---and the original sender of the
...
@@ -318,7 +300,7 @@ by returning a value from its message handler---and the original sender of the
message will receive the response. The following diagram illustrates request
message will receive the response. The following diagram illustrates request
delegation from actor B to actor C.
delegation from actor B to actor C.
..
::
..
code-block:: none
A B C
A B C
| | |
| | |
...
@@ -330,11 +312,6 @@ delegation from actor B to actor C.
...
@@ -330,11 +312,6 @@ delegation from actor B to actor C.
| |<--/
| |<--/
| <-------------(reply)-------------- |
| <-------------(reply)-------------- |
| X
| X
|---\
| | handle
| | response
|<--/
|
X
X
Returning the result of ``delegate(...)`` from a message handler, as
Returning the result of ``delegate(...)`` from a message handler, as
...
@@ -343,7 +320,8 @@ the compiler to check the result type when using statically typed actors.
...
@@ -343,7 +320,8 @@ the compiler to check the result type when using statically typed actors.
.. literalinclude:: /examples/message_passing/delegating.cpp
.. literalinclude:: /examples/message_passing/delegating.cpp
:language: C++
:language: C++
:lines: 15-36
:start-after: --(rst-delegate-begin)--
:end-before: --(rst-delegate-end)--
.. _promise:
.. _promise:
...
@@ -360,14 +338,36 @@ a promise, an actor can fulfill it by calling the member function
...
@@ -360,14 +338,36 @@ a promise, an actor can fulfill it by calling the member function
.. literalinclude:: /examples/message_passing/promises.cpp
.. literalinclude:: /examples/message_passing/promises.cpp
:language: C++
:language: C++
:lines: 18-43
:start-after: --(rst-promise-begin)--
:end-before: --(rst-promise-end)--
This example is almost identical to the example for delegating messages.
However, there is a big difference in the flow of messages. In our first
version, the worker (C) directly responded to the client (A). This time, the
worker sends the result to the server (B), which then fulfills the promise and
thereby sends the result to the client:
.. code-block:: none
A B C
| | |
| ---(request)---> | |
| | ---(request)---> |
| | |---\
| | | | compute
| | | | result
| | |<--/
| | <----(reply)---- |
| | X
| <----(reply)---- |
| X
X
Message Priorities
Message Priorities
------------------
------------------
By default, all messages have the default priority, i.e.,
By default, all messages have the default priority, i.e.,
``message_priority::normal``. Actors can send urgent messages by
``message_priority::normal``. Actors can send urgent messages by setting the
setting the priority explicitly:
priority explicitly: ``send<message_priority::high>(dst, ...)``. Urgent messages
``send<message_priority::high>(dst,...)``. Urgent messages are put into
are put into a different queue of the receiver's mailbox. Hence, long wait
a different queue of the receiver's mailbox. Hence, long wait delays can be
delays can be avoided for urgent communication.
avoided for urgent communication.
manual/Messages.rst
deleted
100644 → 0
View file @
39cb34c8
.. _message:
Type-Erased Tuples, Messages and Message Views
==============================================
Messages in CAF are stored in type-erased tuples. The actual message type
itself is usually hidden, as actors use pattern matching to decompose messages
automatically. However, the classes ``message`` and
``message_builder`` allow more advanced use cases than only sending
data from one actor to another.
The interface ``type_erased_tuple`` encapsulates access to arbitrary
data. This data can be stored on the heap or on the stack. A
``message`` is a type-erased tuple that is always heap-allocated and
uses copy-on-write semantics. When dealing with "plain" type-erased tuples,
users are required to check if a tuple is referenced by others via
``type_erased_tuple::shared`` before modifying its content.
The convenience class ``message_view`` holds a reference to either a
stack-located ``type_erased_tuple`` or a ``message``. The
content of the data can be access via ``message_view::content`` in both
cases, which returns a ``type_erased_tuple&``. The content of the view
can be forced into a message object by calling
``message_view::move_content_to_message``. This member function either
returns the stored message object or moves the content of a stack-allocated
tuple into a new message.
RTTI and Type Numbers
---------------------
All builtin types in CAF have a non-zero 6-bit *type number*. All
user-defined types are mapped to 0. When querying the run-time type information
(RTTI) for individual message or tuple elements, CAF returns a pair consisting
of an integer and a pointer to ``std::type_info``. The first value is
the 6-bit type number. If the type number is non-zero, the second value is a
pointer to the C++ type info, otherwise the second value is null. Additionally,
CAF generates 32 bit *type tokens*. These tokens are *type hints*
that summarizes all types in a type-erased tuple. Two type-erased tuples are of
different type if they have different type tokens (the reverse is not true).
Class ``type_erased_tuple``
---------------------------
**Note**: Calling modifiers on a shared type-erased tuple is undefined
behavior.
+----------------------------------------+------------------------------------------------------------+
| **Observers** | |
+----------------------------------------+------------------------------------------------------------+
| ``bool empty()`` | Returns whether this message is empty. |
+----------------------------------------+------------------------------------------------------------+
| ``size_t size()`` | Returns the size of this message. |
+----------------------------------------+------------------------------------------------------------+
| ``rtti_pair type(size_t pos)`` | Returns run-time type information for the nth element. |
+----------------------------------------+------------------------------------------------------------+
| ``error save(serializer& x)`` | Writes the tuple to ``x``. |
+----------------------------------------+------------------------------------------------------------+
| ``error save(size_t n, serializer& x)``| Writes the nth element to ``x``. |
+----------------------------------------+------------------------------------------------------------+
| ``const void* get(size_t n)`` | Returns a const pointer to the nth element. |
+----------------------------------------+------------------------------------------------------------+
| ``std::string stringify()`` | Returns a string representation of the tuple. |
+----------------------------------------+------------------------------------------------------------+
| ``std::string stringify(size_t n)`` | Returns a string representation of the nth element. |
+----------------------------------------+------------------------------------------------------------+
| ``bool matches(size_t n, rtti_pair)`` | Checks whether the nth element has given type. |
+----------------------------------------+------------------------------------------------------------+
| ``bool shared()`` | Checks whether more than one reference to the data exists. |
+----------------------------------------+------------------------------------------------------------+
| ``bool match_element<T>(size_t n)`` | Checks whether element ``n`` has type ``T``. |
+----------------------------------------+------------------------------------------------------------+
| ``bool match_elements<Ts...>()`` | Checks whether this message has the types ``Ts...``. |
+----------------------------------------+------------------------------------------------------------+
| ``const T& get_as<T>(size_t n)`` | Returns a const reference to the nth element. |
+----------------------------------------+------------------------------------------------------------+
| | |
+----------------------------------------+------------------------------------------------------------+
| **Modifiers** | |
+----------------------------------------+------------------------------------------------------------+
| ``void* get_mutable(size_t n)`` | Returns a mutable pointer to the nth element. |
+----------------------------------------+------------------------------------------------------------+
| ``T& get_mutable_as<T>(size_t n)`` | Returns a mutable reference to the nth element. |
+----------------------------------------+------------------------------------------------------------+
| ``void load(deserializer& x)`` | Reads the tuple from ``x``. |
+----------------------------------------+------------------------------------------------------------+
Class ``message``
-----------------
The class ``message`` includes all member functions of
``type_erased_tuple``. However, calling modifiers is always guaranteed
to be safe. A ``message`` automatically detaches its content by copying
it from the shared data on mutable access. The class further adds the following
member functions over ``type_erased_tuple``. Note that
``apply`` only detaches the content if a callback takes mutable
references as arguments.
+-----------------------------------------------+------------------------------------------------------------+
| **Observers** | |
+-----------------------------------------------+------------------------------------------------------------+
| ``message drop(size_t n)`` | Creates a new message with all but the first ``n`` values. |
+-----------------------------------------------+------------------------------------------------------------+
| ``message drop_right(size_t n)`` | Creates a new message with all but the last ``n`` values. |
+-----------------------------------------------+------------------------------------------------------------+
| ``message take(size_t n)`` | Creates a new message from the first ``n`` values. |
+-----------------------------------------------+------------------------------------------------------------+
| ``message take_right(size_t n)`` | Creates a new message from the last ``n`` values. |
+-----------------------------------------------+------------------------------------------------------------+
| ``message slice(size_t p, size_t n)`` | Creates a new message from ``[p, p + n)``. |
+-----------------------------------------------+------------------------------------------------------------+
| ``message extract(message_handler)`` | See extract_. |
+-----------------------------------------------+------------------------------------------------------------+
| ``message extract_opts(...)`` | See extract-opts_. |
+-----------------------------------------------+------------------------------------------------------------+
| | |
+-----------------------------------------------+------------------------------------------------------------+
| **Modifiers** | |
+-----------------------------------------------+------------------------------------------------------------+
| ``optional<message> apply(message_handler f)``| Returns ``f(*this)``. |
+-----------------------------------------------+------------------------------------------------------------+
| | |
+-----------------------------------------------+------------------------------------------------------------+
| **Operators** | |
+-----------------------------------------------+------------------------------------------------------------+
| ``message operator+(message x, message y)`` | Concatenates ``x`` and ``y``. |
+-----------------------------------------------+------------------------------------------------------------+
| ``message& operator+=(message& x, message y)``| Concatenates ``x`` and ``y``. |
+-----------------------------------------------+------------------------------------------------------------+
Class ``message_builder``
-------------------------
+---------------------------------------------------+-----------------------------------------------------+
| **Constructors** | |
+---------------------------------------------------+-----------------------------------------------------+
| ``(void)`` | Creates an empty message builder. |
+---------------------------------------------------+-----------------------------------------------------+
| ``(Iter first, Iter last)`` | Adds all elements from range ``[first, last)``. |
+---------------------------------------------------+-----------------------------------------------------+
| | |
+---------------------------------------------------+-----------------------------------------------------+
| **Observers** | |
+---------------------------------------------------+-----------------------------------------------------+
| ``bool empty()`` | Returns whether this message is empty. |
+---------------------------------------------------+-----------------------------------------------------+
| ``size_t size()`` | Returns the size of this message. |
+---------------------------------------------------+-----------------------------------------------------+
| ``message to_message( )`` | Converts the buffer to an actual message object. |
+---------------------------------------------------+-----------------------------------------------------+
| ``append(T val)`` | Adds ``val`` to the buffer. |
+---------------------------------------------------+-----------------------------------------------------+
| ``append(Iter first, Iter last)`` | Adds all elements from range ``[first, last)``. |
+---------------------------------------------------+-----------------------------------------------------+
| ``message extract(message_handler)`` | See extract_. |
+---------------------------------------------------+-----------------------------------------------------+
| ``message extract_opts(...)`` | See extract-opts_. |
+---------------------------------------------------+-----------------------------------------------------+
| | |
+---------------------------------------------------+-----------------------------------------------------+
| **Modifiers** | |
+---------------------------------------------------+-----------------------------------------------------+
| ``optional<message>`` ``apply(message_handler f)``| Returns ``f(*this)``. |
+---------------------------------------------------+-----------------------------------------------------+
| ``message move_to_message()`` | Transfers ownership of its data to the new message. |
+---------------------------------------------------+-----------------------------------------------------+
.. _extract:
Extracting
----------
The member function ``message::extract`` removes matched elements from a
message. x Messages are filtered by repeatedly applying a message handler to the
greatest remaining slice, whereas slices are generated in the sequence
``[0, size)``, ``[0, size-1)``, ``...``, ``[1, size-1)``, ``...``,
``[size-1, size)``. Whenever a slice is matched, it is removed from the message
and the next slice starts at the same index on the reduced message.
For example:
.. code-block:: C++
auto msg = make_message(1, 2.f, 3.f, 4);
// remove float and integer pairs
auto msg2 = msg.extract({
[](float, float) { },
[](int, int) { }
});
assert(msg2 == make_message(1, 4));
Step-by-step explanation:
* Slice 1: ``(1, 2.f, 3.f, 4)``, no match
* Slice 2: ``(1, 2.f, 3.f)``, no match
* Slice 3: ``(1, 2.f)``, no match
* Slice 4: ``(1)``, no match
* Slice 5: ``(2.f, 3.f, 4)``, no match
* Slice 6: ``(2.f, 3.f)``, *match*; new message is ``(1, 4)``
* Slice 7: ``(4)``, no match
Slice 7 is ``(4)``, i.e., does not contain the first element, because
the match on slice 6 occurred at index position 1. The function
``extract`` iterates a message only once, from left to right. The
returned message contains the remaining, i.e., unmatched, elements.
.. _extract-opts:
Extracting Command Line Options
-------------------------------
The class ``message`` also contains a convenience interface to
``extract`` for parsing command line options: the member function
``extract_opts``.
.. code-block:: C++
int main(int argc, char** argv) {
uint16_t port;
string host = "localhost";
auto res = message_builder(argv + 1, argv + argc).extract_opts({
{"port,p", "set port", port},
{"host,H", "set host (default: localhost)", host},
{"verbose,v", "enable verbose mode"}
});
if (! res.error.empty()) {
// read invalid CLI arguments
cerr << res.error << endl;
return 1;
}
if (res.opts.count("help") > 0) {
// CLI arguments contained "-h", "--help", or "-?" (builtin);
cout << res.helptext << endl;
return 0;
}
if (! res.remainder.empty()) {
// res.remainder stors all extra arguments that weren't consumed
}
if (res.opts.count("verbose") > 0) {
// enable verbose mode
}
// ...
}
/*
Output of ./program_name -h:
Allowed options:
-p [--port] arg : set port
-H [--host] arg : set host (default: localhost)
-v [--verbose] : enable verbose mode
*/
manual/Overview.rst
View file @
1de110dd
Overview
Overview
========
========
Compiling CAF requires CMake and a
C++11-compatible compiler. To get and
Compiling CAF requires CMake and a
recent C++ compiler. To get and compile the
compile the
sources on UNIX-like systems, type the following in a terminal:
sources on UNIX-like systems, type the following in a terminal:
..
::
..
code-block:: bash
git clone https://github.com/actor-framework/actor-framework
git clone https://github.com/actor-framework/actor-framework
cd actor-framework
cd actor-framework
./configure
./configure
make
make
-C build
make install [as root, optional]
make
-C build
install [as root, optional]
We recommended to run the unit tests as well:
Running ``configure`` is not a mandatory step. The script merely automates the
CMake setup and makes setting build options slightly more convenient. On
.. ::
Windows, use CMake directly to generate an MSVC project file.
make test
If the output indicates an error, please submit a bug report that includes (a)
your compiler version, (b) your OS, and (c) the content of the file
``build/Testing/Temporary/LastTest.log``.
Features
Features
--------
--------
...
@@ -32,19 +26,13 @@ Features
...
@@ -32,19 +26,13 @@ Features
* Thread-mapped actors for soft migration of existing applications
* Thread-mapped actors for soft migration of existing applications
* Publish/subscribe group communication
* Publish/subscribe group communication
Minimal Compiler Versions
-------------------------
* GCC 4.8
* Clang 3.4
* Visual Studio 2015, Update 3
Supported Operating Systems
Supported Operating Systems
---------------------------
---------------------------
* Linux
* Linux
* Mac OS X
* Windows
* Windows (static library only)
* macOS
* FreeBSD
Hello World Example
Hello World Example
-------------------
-------------------
...
...
manual/Testing.rst
View file @
1de110dd
...
@@ -139,6 +139,7 @@ The following example implements two actors, ``ping`` and
...
@@ -139,6 +139,7 @@ The following example implements two actors, ``ping`` and
.. literalinclude:: /examples/testing/ping_pong.cpp
.. literalinclude:: /examples/testing/ping_pong.cpp
:language: C++
:language: C++
:lines: 12-60
:start-after: --(rst-ping-pong-begin)--
:end-before: --(rst-ping-pong-end)--
manual/index.rst
View file @
1de110dd
...
@@ -21,7 +21,6 @@ Contents
...
@@ -21,7 +21,6 @@ Contents
ReferenceCounting
ReferenceCounting
Error
Error
ConfiguringActorApplications
ConfiguringActorApplications
Messages
GroupCommunication
GroupCommunication
ManagingGroupsOfWorkers
ManagingGroupsOfWorkers
Streaming
Streaming
...
...
manual/mailbox_element.png
View replaced file @
39cb34c8
View file @
1de110dd
20 KB
|
W:
|
H:
39.8 KB
|
W:
|
H:
2-up
Swipe
Onion skin
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