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
656c6b43
Commit
656c6b43
authored
Jan 06, 2021
by
Dominik Charousset
Browse files
Options
Browse Files
Download
Plain Diff
Merge pull request #1165
parents
dbdf9b26
2e1201df
Changes
25
Hide whitespace changes
Inline
Side-by-side
Showing
25 changed files
with
369 additions
and
317 deletions
+369
-317
.ci/centos-7/Dockerfile
.ci/centos-7/Dockerfile
+12
-14
.ci/centos-8/Dockerfile
.ci/centos-8/Dockerfile
+10
-9
.ci/debian-10/Dockerfile
.ci/debian-10/Dockerfile
+9
-10
.ci/debian-9/Dockerfile
.ci/debian-9/Dockerfile
+12
-12
.ci/fedora-31/Dockerfile
.ci/fedora-31/Dockerfile
+11
-12
.ci/fedora-32/Dockerfile
.ci/fedora-32/Dockerfile
+11
-12
.ci/ubuntu-16.04/Dockerfile
.ci/ubuntu-16.04/Dockerfile
+11
-12
.ci/ubuntu-18.04/Dockerfile
.ci/ubuntu-18.04/Dockerfile
+9
-10
.ci/ubuntu-20.04/Dockerfile
.ci/ubuntu-20.04/Dockerfile
+9
-13
.clang-format
.clang-format
+3
-0
Jenkinsfile
Jenkinsfile
+7
-2
configure
configure
+0
-1
examples/CMakeLists.txt
examples/CMakeLists.txt
+19
-15
examples/broker/protobuf_broker.cpp
examples/broker/protobuf_broker.cpp
+59
-63
examples/broker/simple_broker.cpp
examples/broker/simple_broker.cpp
+3
-4
examples/qtsupport/chatwidget.cpp
examples/qtsupport/chatwidget.cpp
+52
-51
examples/qtsupport/chatwidget.hpp
examples/qtsupport/chatwidget.hpp
+8
-5
examples/qtsupport/qt_group_chat.cpp
examples/qtsupport/qt_group_chat.cpp
+37
-41
examples/remoting/distributed_calculator.cpp
examples/remoting/distributed_calculator.cpp
+12
-12
examples/remoting/group_chat.cpp
examples/remoting/group_chat.cpp
+3
-3
examples/remoting/group_server.cpp
examples/remoting/group_server.cpp
+4
-4
examples/remoting/pingpong.proto
examples/remoting/pingpong.proto
+3
-1
libcaf_core/caf/actor_system_config.hpp
libcaf_core/caf/actor_system_config.hpp
+16
-0
libcaf_core/src/actor_system_config.cpp
libcaf_core/src/actor_system_config.cpp
+40
-9
libcaf_core/test/actor_system_config.cpp
libcaf_core/test/actor_system_config.cpp
+9
-2
No files found.
.ci/centos-7/Dockerfile
View file @
656c6b43
FROM
centos:7
RUN
yum update
-y
\
&&
yum
install
-y
centos-release-scl
\
&&
yum
install
-y
epel-release
\
&&
yum update
-y
\
&&
yum clean all
RUN
yum
install
-y
\
cmake3
\
devtoolset-7
\
devtoolset-7-libasan-devel
\
devtoolset-7-libubsan-devel
\
git
\
make
\
openssl-devel
\
&&
yum clean all
&&
yum
install
-y
centos-release-scl
\
&&
yum
install
-y
epel-release
\
&&
yum update
-y
\
&&
yum
install
-y
\
cmake3
\
devtoolset-7
\
devtoolset-7-libasan-devel
\
devtoolset-7-libubsan-devel
\
git
\
make
\
openssl-devel
\
&&
yum clean all
ENV
CXX=/opt/rh/devtoolset-7/root/usr/bin/g++
.ci/centos-8/Dockerfile
View file @
656c6b43
FROM
centos:8
RUN
yum
install
-y
\
cmake
\
gcc-c++
\
libasan
\
libubsan
\
git
\
make
\
openssl-devel
\
&&
yum clean all
RUN
dnf update
-y
\
&&
dnf
install
-y
\
cmake
\
gcc-c++
\
git
\
libasan
\
libubsan
\
make
\
openssl-devel
\
&&
dnf clean all
.ci/debian-10/Dockerfile
View file @
656c6b43
FROM
debian:10
RUN
apt update
-y
&&
\
apt upgrade
-y
RUN
apt
install
-y
\
cmake
\
g++
\
git
\
libssl-dev
\
make
\
&&
apt autoclean
RUN
apt update
-y
\
&&
apt upgrade
-y
\
&&
apt
install
-y
\
cmake
\
g++
\
git
\
libssl-dev
\
make
\
&&
apt autoclean
.ci/debian-9/Dockerfile
View file @
656c6b43
FROM
debian:9
RUN
apt update
-y
&&
\
apt upgrade
-y
RUN
apt
install
-y
\
clang-7
\
libc++-7-dev
\
libc++abi
-7-dev
\
cmake
\
git
\
libssl-dev
\
make
\
&&
apt autoclean
RUN
apt update
-y
\
&&
apt upgrade
-y
\
&&
apt
install
-y
\
clang-7
\
cmake
\
git
\
libc++
-7-dev
\
libc++abi-7-dev
\
libprotobuf-dev
\
libssl-dev
\
make
\
&&
apt autoclean
ENV
CXX=/usr/bin/clang++-7
ENV
CXXFLAGS=-stdlib=libc++
.ci/fedora-31/Dockerfile
View file @
656c6b43
FROM
fedora:31
RUN
dnf update
-y
&&
\
dnf clean all
RUN
dnf
install
-y
\
git
\
make
\
cmake
\
gcc-c++
\
openssl-devel
\
libasan
\
libubsan
\
&&
dnf clean all
RUN
dnf update
-y
\
&&
dnf clean all
\
&&
dnf
install
-y
\
cmake
\
gcc-c++
\
git
\
libasan
\
libubsan
\
make
\
openssl-devel
\
&&
dnf clean all
.ci/fedora-32/Dockerfile
View file @
656c6b43
FROM
fedora:32
RUN
dnf update
-y
&&
\
dnf clean all
RUN
dnf
install
-y
\
git
\
make
\
cmake
\
gcc-c++
\
openssl-devel
\
libasan
\
libubsan
\
&&
dnf clean all
RUN
dnf update
-y
\
&&
dnf clean all
\
&&
dnf
install
-y
\
cmake
\
gcc-c++
\
git
\
libasan
\
libubsan
\
make
\
openssl-devel
\
&&
dnf clean all
.ci/ubuntu-16.04/Dockerfile
View file @
656c6b43
FROM
ubuntu:16.04
RUN
\
apt update
-y
\
&&
apt upgrade
-y
\
&&
apt
install
-y
\
clang-8
\
libc++-8-dev
\
libc++abi-8-dev
\
cmake
\
git
\
libssl-dev
\
make
\
&&
apt autoclean
RUN
apt update
-y
\
&&
apt upgrade
-y
\
&&
apt
install
-y
\
clang-8
\
cmake
\
git
\
libc++-8-dev
\
libc++abi-8-dev
\
libssl-dev
\
make
\
&&
apt autoclean
ENV
CXX=/usr/bin/clang++-8
ENV
CXXFLAGS=-stdlib=libc++
.ci/ubuntu-18.04/Dockerfile
View file @
656c6b43
FROM
ubuntu:18.04
RUN
apt update
-y
&&
\
apt upgrade
-y
RUN
apt
install
-y
\
cmake
\
g++-8
\
git
\
libssl-dev
\
make
\
&&
apt-get autoclean
RUN
apt update
-y
\
&&
apt upgrade
-y
\
&&
apt
install
-y
\
cmake
\
g++-8
\
git
\
libssl-dev
\
make
\
&&
apt-get autoclean
ENV
CXX=/usr/bin/g++-8
.ci/ubuntu-20.04/Dockerfile
View file @
656c6b43
FROM
ubuntu:20.04
ENV
TZ=Europe/Berlin
RUN
ln
-snf
/usr/share/zoneinfo/
$TZ
/etc/localtime
&&
echo
$TZ
>
/etc/timezone
RUN
apt update
-y
&&
\
apt upgrade
-y
RUN
apt
install
-y
\
cmake
\
g++
\
git
\
libssl-dev
\
make
\
&&
apt-get autoclean
RUN
apt update
-y
\
&&
apt upgrade
-y
\
&&
DEBIAN_FRONTEND
=
"noninteractive"
apt
install
-y
\
cmake
\
g++
\
git
\
libssl-dev
\
make
\
&&
apt-get autoclean
.clang-format
View file @
656c6b43
...
...
@@ -59,4 +59,7 @@ SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
UseTab: Never
---
Language: Proto
DisableFormat: true
...
Jenkinsfile
View file @
656c6b43
...
...
@@ -17,8 +17,9 @@ config = [
],
// Default CMake flags by build type.
buildFlags:
[
'CAF_ENABLE_RUNTIME_CHECKS:BOOL=ON'
,
'CAF_ENABLE_ACTOR_PROFILER:BOOL=ON'
,
'CAF_ENABLE_EXAMPLES:BOOL=ON'
,
'CAF_ENABLE_RUNTIME_CHECKS:BOOL=ON'
,
],
extraDebugFlags:
[
'CAF_LOG_LEVEL:STRING=TRACE'
,
...
...
@@ -107,12 +108,16 @@ config = [
'LDFLAGS=-fno-sanitize-recover=undefined'
,
],
]],
// Other UNIX systems.
// Other UNIX systems.
On macOS, we also build *all* examples.
[
'macOS'
,
[
numCores:
4
,
builds:
[
'debug'
,
'release'
],
extraBuildFlags:
[
'CAF_ENABLE_CURL_EXAMPLES:BOOL=ON'
,
'CAF_ENABLE_PROTOBUF_EXAMPLES:BOOL=ON'
,
'CAF_ENABLE_QT5_EXAMPLES:BOOL=ON'
,
'OPENSSL_ROOT_DIR:PATH=/usr/local/opt/openssl'
,
'Qt5_DIR:PATH=/usr/local/opt/qt/lib/cmake/Qt5'
,
],
extraDebugBuildFlags:
[
'CAF_SANITIZERS:STRING=address'
,
...
...
configure
View file @
656c6b43
...
...
@@ -71,7 +71,6 @@ Influential Environment Variables (only on first invocation):
CXX C++ compiler command
CXXFLAGS Additional C++ compiler flags
LDFLAGS Additional linker flags
CMAKE_GENERATOR Selects a custom generator
"
# Appends a CMake cache entry definition to the CMakeCacheEntries variable.
...
...
examples/CMakeLists.txt
View file @
656c6b43
...
...
@@ -82,27 +82,31 @@ if(TARGET CAF::io)
if
(
CAF_ENABLE_QT5_EXAMPLES
)
find_package
(
Qt5 COMPONENTS Core Gui Widgets REQUIRED
)
message
(
STATUS
"Found Qt5"
)
#include(${QT_USE_FILE})
QT5_ADD_RESOURCES
(
GROUP_CHAT_RCS
)
QT5_WRAP_UI
(
GROUP_CHAT_UI_HDR qtsupport/chatwindow.ui
)
QT5_WRAP_CPP
(
GROUP_CHAT_MOC_SRC qtsupport/chatwidget.hpp
)
qt5_wrap_ui
(
GROUP_CHAT_UI_HDR qtsupport/chatwindow.ui
)
qt5_wrap_cpp
(
GROUP_CHAT_MOC_SRC qtsupport/chatwidget.hpp
)
# generated headers will be in cmake build directory
include_directories
(
qtsupport
${
CMAKE_CURRENT_BINARY_DIR
}
${
Qt5Core_INCLUDE_DIRS
}
${
Qt5Gui_INCLUDE_DIRS
}
${
Qt5Widgets_INCLUDE_DIRS
}
)
set
(
GROUP_CHAT_SRC qtsupport/qt_group_chat.cpp qtsupport/chatwidget.cpp
)
add_executable
(
qt_group_chat
${
GROUP_CHAT_SRC
}
qtsupport/qt_group_chat.cpp
qtsupport/chatwidget.cpp
${
GROUP_CHAT_MOC_SRC
}
${
GROUP_CHAT_UI_HDR
}
)
target_link_libraries
(
qt_group_chat
CAF::io
CAF::internal
Qt5::Core
Qt5::Gui
Qt5::Widgets
CAF::io
)
Qt5::Widgets
)
target_include_directories
(
qt_group_chat PRIVATE
qtsupport
${
CMAKE_CURRENT_BINARY_DIR
}
${
Qt5Core_INCLUDE_DIRS
}
${
Qt5Gui_INCLUDE_DIRS
}
${
Qt5Widgets_INCLUDE_DIRS
}
)
if
(
CMAKE_VERSION VERSION_LESS 3.8
)
message
(
STATUS
"Note: build fails if Qt5 sets incompatible -std=ARG flag"
)
else
()
set_property
(
TARGET qt_group_chat PROPERTY CXX_STANDARD 17
)
endif
()
add_dependencies
(
qt_group_chat all_examples
)
endif
()
...
...
@@ -110,7 +114,7 @@ if(TARGET CAF::io)
find_package
(
CURL REQUIRED
)
add_executable
(
curl_fuse curl/curl_fuse.cpp
)
include_directories
(
${
CURL_INCLUDE_DIRS
}
)
target_link_libraries
(
curl_fuse
${
CURL_LIBRARY
}
CAF::io
)
target_link_libraries
(
curl_fuse
${
CURL_LIBRARY
}
CAF::io
CAF::internal
)
add_dependencies
(
curl_fuse all_examples
)
endif
()
...
...
examples/broker/protobuf_broker.cpp
View file @
656c6b43
#include <vector>
#include <string>
#include <iostream>
#include <limits>
#include <memory>
#include <iostream>
#include <string>
#include <vector>
#include "caf/all.hpp"
#include "caf/io/all.hpp"
#ifdef CAF_WINDOWS
#include <winsock2.h>
#
include <winsock2.h>
#else
#include <arpa/inet.h>
#
include <arpa/inet.h>
#endif
CAF_PUSH_WARNINGS
#include "pingpong.pb.h"
CAF_POP_WARNINGS
CAF_BEGIN_TYPE_ID_BLOCK
(
protobuf_example
,
first_custom_type_id
)
CAF_ADD_ATOM
(
protobuf_example
,
kickoff_atom
)
CAF_END_TYPE_ID_BLOCK
(
protobuf_example
)
namespace
{
using
namespace
std
;
using
namespace
caf
;
using
namespace
caf
::
io
;
using
ping_atom
=
atom_constant
<
atom
(
"ping"
)
>
;
using
pong_atom
=
atom_constant
<
atom
(
"pong"
)
>
;
using
kickoff_atom
=
atom_constant
<
atom
(
"kickoff"
)
>
;
// utility function to print an exit message with custom name
void
print_on_exit
(
scheduled_actor
*
self
,
const
std
::
string
&
name
)
{
self
->
attach_functor
([
=
](
const
error
&
reason
)
{
aout
(
self
)
<<
name
<<
" exited: "
<<
to_string
(
reason
)
<<
endl
;
aout
(
self
)
<<
name
<<
" exited: "
<<
to_string
(
reason
)
<<
std
::
endl
;
});
}
...
...
@@ -42,90 +43,85 @@ behavior ping(stateful_actor<ping_state>* self, size_t num_pings) {
print_on_exit
(
self
,
"ping"
);
return
{
[
=
](
kickoff_atom
,
const
actor
&
pong
)
{
self
->
send
(
pong
,
ping_atom
::
value
,
1
);
self
->
become
(
[
=
](
pong_atom
,
int
value
)
->
message
{
if
(
++
(
self
->
state
.
count
)
>=
num_pings
)
self
->
quit
();
return
make_message
(
ping_atom
::
value
,
value
+
1
);
}
);
}
self
->
send
(
pong
,
ping_atom_v
,
1
);
self
->
become
([
=
](
pong_atom
,
int
value
)
->
result
<
ping_atom
,
int
>
{
if
(
++
(
self
->
state
.
count
)
>=
num_pings
)
self
->
quit
();
return
{
ping_atom_v
,
value
+
1
};
});
},
};
}
behavior
pong
(
event_based_actor
*
self
)
{
print_on_exit
(
self
,
"pong"
);
return
{
[
=
](
ping_atom
,
int
value
)
{
return
make_message
(
pong_atom
::
value
,
value
);
}
[
=
](
ping_atom
,
int
value
)
{
return
make_message
(
pong_atom_v
,
value
);
},
};
}
void
protobuf_io
(
broker
*
self
,
connection_handle
hdl
,
const
actor
&
buddy
)
{
print_on_exit
(
self
,
"protobuf_io"
);
aout
(
self
)
<<
"protobuf broker started"
<<
endl
;
aout
(
self
)
<<
"protobuf broker started"
<<
std
::
endl
;
self
->
monitor
(
buddy
);
self
->
set_down_handler
(
[
=
](
const
down_msg
&
dm
)
{
if
(
dm
.
source
==
buddy
)
{
aout
(
self
)
<<
"our buddy is down"
<<
endl
;
self
->
quit
(
dm
.
reason
);
}
});
auto
write
=
[
=
](
const
org
::
libcppa
::
PingOrPong
&
p
)
{
string
buf
=
p
.
SerializeAsString
();
self
->
set_down_handler
([
=
](
const
down_msg
&
dm
)
{
if
(
dm
.
source
==
buddy
)
{
aout
(
self
)
<<
"our buddy is down"
<<
std
::
endl
;
self
->
quit
(
dm
.
reason
);
}
});
auto
write
=
[
=
](
const
org
::
caf
::
PingOrPong
&
p
)
{
std
::
string
buf
=
p
.
SerializeAsString
();
auto
s
=
htonl
(
static_cast
<
uint32_t
>
(
buf
.
size
()));
self
->
write
(
hdl
,
sizeof
(
uint32_t
),
&
s
);
self
->
write
(
hdl
,
buf
.
size
(),
buf
.
data
());
self
->
flush
(
hdl
);
};
message_handler
default_bhvr
=
{
auto
default_callbacks
=
message_handler
{
[
=
](
const
connection_closed_msg
&
)
{
aout
(
self
)
<<
"connection closed"
<<
endl
;
aout
(
self
)
<<
"connection closed"
<<
std
::
endl
;
self
->
send_exit
(
buddy
,
exit_reason
::
remote_link_unreachable
);
self
->
quit
(
exit_reason
::
remote_link_unreachable
);
},
[
=
](
ping_atom
,
int
i
)
{
aout
(
self
)
<<
"'ping' "
<<
i
<<
endl
;
org
::
libcppa
::
PingOrPong
p
;
aout
(
self
)
<<
"'ping' "
<<
i
<<
std
::
endl
;
org
::
caf
::
PingOrPong
p
;
p
.
mutable_ping
()
->
set_id
(
i
);
write
(
p
);
},
[
=
](
pong_atom
,
int
i
)
{
aout
(
self
)
<<
"'pong' "
<<
i
<<
endl
;
org
::
libcppa
::
PingOrPong
p
;
aout
(
self
)
<<
"'pong' "
<<
i
<<
std
::
endl
;
org
::
caf
::
PingOrPong
p
;
p
.
mutable_pong
()
->
set_id
(
i
);
write
(
p
);
}
}
,
};
auto
await_protobuf_data
=
message_handler
{
[
=
](
const
new_data_msg
&
msg
)
{
org
::
libcppa
::
PingOrPong
p
;
org
::
caf
::
PingOrPong
p
;
p
.
ParseFromArray
(
msg
.
buf
.
data
(),
static_cast
<
int
>
(
msg
.
buf
.
size
()));
if
(
p
.
has_ping
())
{
self
->
send
(
buddy
,
ping_atom
::
value
,
p
.
ping
().
id
());
self
->
send
(
buddy
,
ping_atom
_v
,
p
.
ping
().
id
());
}
else
if
(
p
.
has_pong
())
{
self
->
send
(
buddy
,
pong_atom
::
value
,
p
.
pong
().
id
());
self
->
send
(
buddy
,
pong_atom
_v
,
p
.
pong
().
id
());
}
else
{
self
->
quit
(
exit_reason
::
user_shutdown
);
cerr
<<
"neither Ping nor Pong!"
<<
endl
;
std
::
cerr
<<
"neither Ping nor Pong!"
<<
std
::
endl
;
}
// receive next length prefix
self
->
configure_read
(
hdl
,
receive_policy
::
exactly
(
sizeof
(
uint32_t
)));
self
->
unbecome
();
}
}.
or_else
(
default_
bhvr
);
}
,
}.
or_else
(
default_
callbacks
);
auto
await_length_prefix
=
message_handler
{
[
=
](
const
new_data_msg
&
msg
)
{
uint32_t
num_bytes
;
memcpy
(
&
num_bytes
,
msg
.
buf
.
data
(),
sizeof
(
uint32_t
));
num_bytes
=
htonl
(
num_bytes
);
if
(
num_bytes
>
(
1024
*
1024
))
{
aout
(
self
)
<<
"someone is trying something nasty"
<<
endl
;
aout
(
self
)
<<
"someone is trying something nasty"
<<
std
::
endl
;
self
->
quit
(
exit_reason
::
user_shutdown
);
return
;
}
...
...
@@ -133,8 +129,8 @@ void protobuf_io(broker* self, connection_handle hdl, const actor& buddy) {
auto
nb
=
static_cast
<
size_t
>
(
num_bytes
);
self
->
configure_read
(
hdl
,
receive_policy
::
exactly
(
nb
));
self
->
become
(
keep_behavior
,
await_protobuf_data
);
}
}.
or_else
(
default_
bhvr
);
}
,
}.
or_else
(
default_
callbacks
);
// initial setup
self
->
configure_read
(
hdl
,
receive_policy
::
exactly
(
sizeof
(
uint32_t
)));
self
->
become
(
await_length_prefix
);
...
...
@@ -142,14 +138,14 @@ void protobuf_io(broker* self, connection_handle hdl, const actor& buddy) {
behavior
server
(
broker
*
self
,
const
actor
&
buddy
)
{
print_on_exit
(
self
,
"server"
);
aout
(
self
)
<<
"server is running"
<<
endl
;
aout
(
self
)
<<
"server is running"
<<
std
::
endl
;
return
{
[
=
](
const
new_connection_msg
&
msg
)
{
aout
(
self
)
<<
"server accepted new connection"
<<
endl
;
aout
(
self
)
<<
"server accepted new connection"
<<
std
::
endl
;
auto
io_actor
=
self
->
fork
(
protobuf_io
,
msg
.
handle
,
buddy
);
// only accept 1 connection in our example
self
->
quit
();
}
}
,
};
}
...
...
@@ -161,33 +157,33 @@ public:
config
()
{
opt_group
{
custom_options_
,
"global"
}
.
add
(
port
,
"port,p"
,
"set port"
)
.
add
(
host
,
"host,H"
,
"set host (ignored in server mode)"
)
.
add
(
server_mode
,
"server-mode,s"
,
"enable server mode"
);
.
add
(
port
,
"port,p"
,
"set port"
)
.
add
(
host
,
"host,H"
,
"set host (ignored in server mode)"
)
.
add
(
server_mode
,
"server-mode,s"
,
"enable server mode"
);
}
};
void
run_server
(
actor_system
&
system
,
const
config
&
cfg
)
{
cout
<<
"run in server mode"
<<
endl
;
std
::
cout
<<
"run in server mode"
<<
std
::
endl
;
auto
pong_actor
=
system
.
spawn
(
pong
);
auto
server_actor
=
system
.
middleman
().
spawn_server
(
server
,
cfg
.
port
,
pong_actor
);
if
(
!
server_actor
)
cerr
<<
"unable to spawn server: "
<<
to_string
(
server_actor
.
error
())
<<
endl
;
std
::
cerr
<<
"unable to spawn server: "
<<
to_string
(
server_actor
.
error
())
<<
std
::
endl
;
}
void
run_client
(
actor_system
&
system
,
const
config
&
cfg
)
{
cout
<<
"run in client mode"
<<
endl
;
std
::
cout
<<
"run in client mode"
<<
std
::
endl
;
auto
ping_actor
=
system
.
spawn
(
ping
,
20u
);
auto
io_actor
=
system
.
middleman
().
spawn_client
(
protobuf_io
,
cfg
.
host
,
cfg
.
port
,
ping_actor
);
if
(
!
io_actor
)
{
cout
<<
"cannot connect to "
<<
cfg
.
host
<<
" at port "
<<
cfg
.
port
<<
": "
<<
to_string
(
io_actor
.
error
())
<<
endl
;
std
::
cout
<<
"cannot connect to "
<<
cfg
.
host
<<
" at port "
<<
cfg
.
port
<<
": "
<<
to_string
(
io_actor
.
error
())
<<
std
::
endl
;
return
;
}
send_as
(
*
io_actor
,
ping_actor
,
kickoff_atom
::
value
,
*
io_actor
);
send_as
(
*
io_actor
,
ping_actor
,
kickoff_atom
_v
,
*
io_actor
);
}
void
caf_main
(
actor_system
&
system
,
const
config
&
cfg
)
{
...
...
@@ -197,4 +193,4 @@ void caf_main(actor_system& system, const config& cfg) {
}
// namespace
CAF_MAIN
(
io
::
middleman
)
CAF_MAIN
(
i
d_block
::
protobuf_example
,
i
o
::
middleman
)
examples/broker/simple_broker.cpp
View file @
656c6b43
...
...
@@ -3,10 +3,9 @@
* a broker. Server and client exchange integers in a 'ping-pong protocol'. *
* *
* Minimal setup: *
* - ./build/bin/broker -s 4242 *
* - ./build/bin/broker -c localhost 4242 *
\
******************************************************************************/
* - simple_broker -s 4242 *
* - simple_broker -c localhost 4242 *
\******************************************************************************/
// Manual refs: 42-47 (Actors.tex)
...
...
examples/qtsupport/chatwidget.cpp
View file @
656c6b43
...
...
@@ -14,10 +14,8 @@ CAF_POP_WARNINGS
using
namespace
std
;
using
namespace
caf
;
ChatWidget
::
ChatWidget
(
QWidget
*
parent
,
Qt
::
WindowFlags
f
)
:
super
(
parent
,
f
),
input_
(
nullptr
),
output_
(
nullptr
)
{
ChatWidget
::
ChatWidget
(
QWidget
*
parent
)
:
super
(
parent
),
input_
(
nullptr
),
output_
(
nullptr
)
{
// nop
}
...
...
@@ -27,15 +25,17 @@ ChatWidget::~ChatWidget() {
void
ChatWidget
::
init
(
actor_system
&
system
)
{
super
::
init
(
system
);
set_message_handler
([
=
](
actor_companion
*
self
)
->
message_handler
{
set_message_handler
([
=
](
actor_companion
*
self
)
->
message_handler
{
return
{
[
=
](
join_atom
,
const
group
&
what
)
{
if
(
chatroom_
)
{
self
->
send
(
chatroom_
,
name_
+
" has left the chatroom"
);
self
->
leave
(
chatroom_
);
auto
groups
=
self
->
joined_groups
();
for
(
const
auto
&
grp
:
groups
)
{
print
((
"*** leave "
+
to_string
(
grp
)).
c_str
());
self
->
send
(
grp
,
name_
+
" has left the chatroom"
);
self
->
leave
(
grp
);
}
print
((
"*** join "
+
to_string
(
what
)).
c_str
());
self
->
join
(
what
);
print
((
"*** joined "
+
to_string
(
what
)).
c_str
());
chatroom_
=
what
;
self
->
send
(
what
,
name_
+
" has entered the chatroom"
);
},
...
...
@@ -44,9 +44,7 @@ void ChatWidget::init(actor_system& system) {
name_
=
std
::
move
(
name
);
print
(
"*** changed name to "
+
QString
::
fromUtf8
(
name_
.
c_str
()));
},
[
=
](
quit_atom
)
{
quit_and_close
();
},
[
=
](
quit_atom
)
{
quit_and_close
();
},
[
=
](
const
string
&
txt
)
{
// don't print own messages
if
(
self
!=
self
->
current_sender
())
...
...
@@ -55,7 +53,15 @@ void ChatWidget::init(actor_system& system) {
[
=
](
const
group_down_msg
&
gdm
)
{
print
(
"*** chatroom offline: "
+
QString
::
fromUtf8
(
to_string
(
gdm
.
source
).
c_str
()));
}
},
[
=
](
leave_atom
)
{
auto
groups
=
self
->
joined_groups
();
for
(
const
auto
&
grp
:
groups
)
{
std
::
cout
<<
"*** leave "
<<
to_string
(
grp
)
<<
std
::
endl
;
self
->
send
(
grp
,
name_
+
" has left the chatroom"
);
self
->
leave
(
grp
);
}
},
};
});
}
...
...
@@ -65,47 +71,42 @@ void ChatWidget::sendChatMessage() {
input
()
->
setText
(
QString
());
});
QString
line
=
input
()
->
text
();
if
(
line
.
startsWith
(
'/'
))
{
if
(
line
.
isEmpty
())
{
// Ignore empty lines.
}
else
if
(
line
.
startsWith
(
'/'
))
{
vector
<
string
>
words
;
split
(
words
,
line
.
midRef
(
1
).
toUtf8
().
constData
(),
is_any_of
(
" "
));
message_builder
mb
;
if
(
words
.
size
()
>
1
)
{
// convert first word to an atom
mb
.
append
(
atom_from_string
(
words
.
front
()))
.
append
(
words
.
begin
()
+
1
,
words
.
end
());
};
auto
res
=
mb
.
apply
({
[
=
](
join_atom
,
const
string
&
mod
,
const
string
&
g
)
{
auto
x
=
system
().
groups
().
get
(
mod
,
g
);
if
(
!
x
)
print
(
"*** error: "
+
QString
::
fromUtf8
(
to_string
(
x
.
error
()).
c_str
()));
else
self
()
->
send
(
self
(),
atom
(
"join"
),
std
::
move
(
*
x
));
},
[
=
](
set_name_atom
atm
,
string
&
name
)
{
send_as
(
as_actor
(),
as_actor
(),
atm
,
std
::
move
(
name
));
}
});
if
(
!
res
)
auto
utf8
=
line
.
midRef
(
1
).
toUtf8
();
auto
sv
=
caf
::
string_view
{
utf8
.
constData
(),
static_cast
<
size_t
>
(
utf8
.
size
())};
split
(
words
,
sv
,
is_any_of
(
" "
));
if
(
words
.
size
()
==
3
&&
words
[
0
]
==
"join"
)
{
if
(
auto
x
=
system
().
groups
().
get
(
words
[
1
],
words
[
2
]))
send_as
(
as_actor
(),
as_actor
(),
join_atom_v
,
std
::
move
(
*
x
));
else
print
(
"*** error: "
+
QString
::
fromUtf8
(
to_string
(
x
.
error
()).
c_str
()));
}
else
if
(
words
.
size
()
==
2
&&
words
[
0
]
==
"setName"
)
send_as
(
as_actor
(),
as_actor
(),
set_name_atom_v
,
std
::
move
(
words
[
1
]));
else
{
print
(
"*** list of commands:
\n
"
"/join <module> <group id>
\n
"
"/setName <new name>
\n
"
);
return
;
}
if
(
name_
.
empty
())
{
print
(
"*** please set a name before sending messages"
);
return
;
}
if
(
!
chatroom_
)
{
print
(
"*** no one is listening... please join a group"
);
return
;
return
;
}
}
else
{
if
(
name_
.
empty
())
{
print
(
"*** please set a name before sending messages"
);
return
;
}
if
(
!
chatroom_
)
{
print
(
"*** no one is listening... please join a group"
);
return
;
}
string
msg
=
name_
;
msg
+=
": "
;
msg
+=
line
.
toUtf8
().
constData
();
print
(
"<you>: "
+
input
()
->
text
());
send_as
(
as_actor
(),
chatroom_
,
std
::
move
(
msg
));
}
string
msg
=
name_
;
msg
+=
": "
;
msg
+=
line
.
toUtf8
().
constData
();
print
(
"<you>: "
+
input
()
->
text
());
send_as
(
as_actor
(),
chatroom_
,
std
::
move
(
msg
));
}
void
ChatWidget
::
joinGroup
()
{
...
...
@@ -130,12 +131,12 @@ void ChatWidget::joinGroup() {
if
(
!
x
)
QMessageBox
::
critical
(
this
,
"Error"
,
to_string
(
x
.
error
()).
c_str
());
else
self
()
->
send
(
self
(),
join_atom
::
value
,
std
::
move
(
*
x
));
self
()
->
send
(
self
(),
join_atom
_v
,
std
::
move
(
*
x
));
}
void
ChatWidget
::
changeName
()
{
auto
name
=
QInputDialog
::
getText
(
this
,
"Change Name"
,
"Please enter a new name"
);
if
(
!
name
.
isEmpty
())
send_as
(
as_actor
(),
as_actor
(),
atom
(
"setName"
)
,
name
.
toUtf8
().
constData
());
send_as
(
as_actor
(),
as_actor
(),
set_name_atom_v
,
name
.
toUtf8
().
constData
());
}
examples/qtsupport/chatwidget.hpp
View file @
656c6b43
...
...
@@ -11,6 +11,13 @@ CAF_PUSH_WARNINGS
#include <QTextEdit>
CAF_POP_WARNINGS
CAF_BEGIN_TYPE_ID_BLOCK
(
qtsupport
,
first_custom_type_id
)
CAF_ADD_ATOM
(
qtsupport
,
set_name_atom
)
CAF_ADD_ATOM
(
qtsupport
,
quit_atom
)
CAF_END_TYPE_ID_BLOCK
(
qtsupport
)
class
ChatWidget
:
public
caf
::
mixin
::
actor_widget
<
QWidget
>
{
private:
// -- Qt boilerplate code ----------------------------------------------------
...
...
@@ -22,11 +29,7 @@ public:
using
super
=
caf
::
mixin
::
actor_widget
<
QWidget
>
;
using
set_name_atom
=
caf
::
atom_constant
<
caf
::
atom
(
"setName"
)
>
;
using
quit_atom
=
caf
::
atom_constant
<
caf
::
atom
(
"quit"
)
>
;
ChatWidget
(
QWidget
*
parent
=
nullptr
,
Qt
::
WindowFlags
f
=
0
);
ChatWidget
(
QWidget
*
parent
=
nullptr
);
~
ChatWidget
();
...
...
examples/qtsupport/qt_group_chat.cpp
View file @
656c6b43
...
...
@@ -4,79 +4,75 @@
* terminal version in remote_actors/group_chat.cpp. *
* *
* Setup for a minimal chat between "alice" and "bob": *
* -
./build/bin/group_server -p 4242
*
* -
./build/bin/qt_group_chat -g remote:chatroom@localhost:4242 -n alice
*
* -
./build/bin/qt_group_chat -g remote:chatroom@localhost:4242 -n bob
*
* -
group_server -p 4242
*
* -
qt_group_chat -g remote:chatroom@localhost:4242 -n alice
*
* -
qt_group_chat -g remote:chatroom@localhost:4242 -n bob
*
\******************************************************************************/
#include <set>
#include <map>
#include <vector>
#include <cstdlib>
#include <iostream>
#include <map>
#include <set>
#include <sstream>
#include <time.h>
#include <
cstdlib
>
#include <
vector
>
#include "caf/all.hpp"
#include "caf/io/all.hpp"
CAF_PUSH_WARNINGS
#include <QMainWindow>
#include <QApplication>
#include "ui_chatwindow.h" // auto generated from chatwindow.ui
#include <QApplication>
#include <QMainWindow>
CAF_POP_WARNINGS
using
namespace
std
;
#include "chatwidget.hpp"
using
namespace
caf
;
class
config
:
public
actor_system_config
{
public:
std
::
string
name
;
std
::
string
group_id
;
config
(
int
argc
,
char
**
argv
)
{
config
()
{
opt_group
{
custom_options_
,
"global"
}
.
add
(
name
,
"name,n"
,
"set name"
)
.
add
(
group_id
,
"group,g"
,
"join group (format: <module>:<id>"
);
parse
(
argc
,
argv
);
load
<
io
::
middleman
>
();
.
add
<
std
::
string
>
(
"name,n"
,
"set name"
)
.
add
<
std
::
string
>
(
"group,g"
,
"join group"
);
}
};
int
main
(
int
argc
,
char
**
argv
)
{
config
cfg
{
argc
,
argv
};
actor_system
system
{
cfg
};
auto
name
=
cfg
.
name
;
int
caf_main
(
actor_system
&
sys
,
const
config
&
cfg
)
{
std
::
string
name
;
if
(
auto
config_name
=
get_if
<
std
::
string
>
(
&
cfg
,
"name"
))
name
=
std
::
move
(
*
config_name
);
while
(
name
.
empty
())
{
std
::
cout
<<
"please enter your name: "
<<
std
::
flush
;
if
(
!
std
::
getline
(
std
::
cin
,
name
))
{
std
::
cerr
<<
"*** no name given... terminating"
<<
std
::
endl
;
return
EXIT_FAILURE
;
}
}
group
grp
;
// evaluate group parameters
if
(
!
cfg
.
group_id
.
empty
())
{
auto
p
=
cfg
.
group_id
.
find
(
':'
);
if
(
p
==
std
::
string
::
npos
)
{
cerr
<<
"*** error parsing argument "
<<
cfg
.
group_id
<<
", expected format: <module_name>:<group_id>"
;
if
(
auto
locator
=
get_if
<
std
::
string
>
(
&
cfg
,
"group"
))
{
if
(
auto
maybe_grp
=
sys
.
groups
().
get
(
*
locator
))
{
grp
=
std
::
move
(
*
maybe_grp
);
}
else
{
auto
module
=
cfg
.
group_id
.
substr
(
0
,
p
);
auto
group_uri
=
cfg
.
group_id
.
substr
(
p
+
1
);
auto
g
=
system
.
groups
().
get
(
module
,
group_uri
);
if
(
!
g
)
{
cerr
<<
"*** unable to get group "
<<
group_uri
<<
" from module "
<<
module
<<
": "
<<
to_string
(
g
.
error
())
<<
endl
;
return
-
1
;
}
grp
=
std
::
move
(
*
g
);
std
::
cerr
<<
R"(*** failed to parse ")"
<<
*
locator
<<
R"(" as group locator: )"
<<
to_string
(
maybe_grp
.
error
())
<<
std
::
endl
;
}
}
auto
[
argc
,
argv
]
=
cfg
.
c_args_remainder
();
QApplication
app
{
argc
,
argv
};
app
.
setQuitOnLastWindowClosed
(
true
);
QMainWindow
mw
;
Ui
::
ChatWindow
helper
;
helper
.
setupUi
(
&
mw
);
helper
.
chatwidget
->
init
(
sys
tem
);
helper
.
chatwidget
->
init
(
sys
);
auto
client
=
helper
.
chatwidget
->
as_actor
();
if
(
!
name
.
empty
())
send_as
(
client
,
client
,
atom
(
"setName"
),
move
(
name
));
if
(
grp
)
send_as
(
client
,
client
,
atom
(
"join"
),
std
::
move
(
grp
));
anon_send
(
client
,
set_name_atom_v
,
move
(
name
));
anon_send
(
client
,
join_atom_v
,
std
::
move
(
grp
));
mw
.
show
();
return
app
.
exec
();
}
CAF_MAIN
(
id_block
::
qtsupport
,
io
::
middleman
)
examples/remoting/distributed_calculator.cpp
View file @
656c6b43
/
/ This program is a distributed version of the math_actor example.
// Client and server use a stateless request/response protocol and the client
// is failure resilient by using a FIFO request queue.
// The client auto-reconnects and also allows for server reconfiguration.
//
// Run server at port 4242:
// - ./build/bin/distributed_math_actor -s -p 4242
//
// Run client at the same host:
// - ./build/bin/distributed_math_actor -c -p 4242
// Manual refs: 206-218 (ConfiguringActorSystems)
/
******************************************************************************\
* This program is a distributed version of the math_actor example. *
* Client and server use a stateless request/response protocol and the client *
* is failure resilient by using a FIFO request queue. *
* The client auto-reconnects and also allows for server reconfiguration. *
* *
* Run server at port 4242: *
* - distributed_calculator -s -p 4242 *
* *
* Run client at the same host: *
* - distributed_calculator -c -p 4242 *
\******************************************************************************/
#include <array>
#include <cassert>
...
...
examples/remoting/group_chat.cpp
View file @
656c6b43
...
...
@@ -3,9 +3,9 @@
* based on group communication. *
* *
* Setup for a minimal chat between "alice" and "bob": *
* -
./build/bin/group_chat -s -p 4242
*
* -
./build/bin/group_chat -g remote:chatroom@localhost:4242 -n alice
*
* -
./build/bin/group_chat -g remote:chatroom@localhost:4242 -n bob
*
* -
group_chat -s -p 4242
*
* -
group_chat -g remote:chatroom@localhost:4242 -n alice
*
* -
group_chat -g remote:chatroom@localhost:4242 -n bob
*
\******************************************************************************/
#include <cstdlib>
...
...
examples/remoting/group_server.cpp
View file @
656c6b43
...
...
@@ -3,10 +3,10 @@
* communication server. *
* *
* Setup for a minimal chat between "alice" and "bob": *
* -
./build/bin/group_server -p 4242
*
* -
./build/bin/group_chat -g remote:chatroom@localhost:4242 -n alice
*
* -
./build/bin/group_chat -g remote:chatroom@localhost:4242 -n bob
*
\
******************************************************************************/
* -
group_server -p 4242
*
* -
group_chat -g remote:chatroom@localhost:4242 -n alice
*
* -
group_chat -g remote:chatroom@localhost:4242 -n bob
*
\******************************************************************************/
#include <string>
#include <cstdlib>
...
...
examples/remoting/pingpong.proto
View file @
656c6b43
package
org
.
libcppa
;
syntax
=
"proto2"
;
package
org
.
caf
;
message
Ping
{
required
int32
id
=
1
;
...
...
libcaf_core/caf/actor_system_config.hpp
View file @
656c6b43
...
...
@@ -185,9 +185,20 @@ public:
/// and instead return from `main` immediately.
bool
cli_helptext_printed
;
/// Stores the content of `argv[0]` from the arguments passed to `parse`.
std
::
string
program_name
;
/// Stores CLI arguments that were not consumed by CAF.
string_list
remainder
;
/// Returns the remainder including the program name (`argv[0]`) suitable for
/// passing the returned pair of arguments to C-style functions that usually
/// accept `(argc, argv)` input as passed to `main`. This function creates the
/// necessary buffers lazily on first call.
/// @note The returned pointer remains valid only as long as the
/// `actor_system_config` object exists.
std
::
pair
<
int
,
char
**>
c_args_remainder
()
const
noexcept
;
// -- caf-run parameters -----------------------------------------------------
/// Stores whether this node was started in slave mode.
...
...
@@ -317,6 +328,11 @@ protected:
config_option_set
custom_options_
;
private:
void
set_remainder
(
string_list
args
);
mutable
std
::
vector
<
char
*>
c_args_remainder_
;
std
::
vector
<
char
>
c_args_remainder_buf_
;
actor_system_config
&
set_impl
(
string_view
name
,
config_value
value
);
error
extract_config_file_path
(
string_list
&
args
);
...
...
libcaf_core/src/actor_system_config.cpp
View file @
656c6b43
...
...
@@ -53,6 +53,7 @@ actor_system_config::~actor_system_config() {
actor_system_config
::
actor_system_config
()
:
cli_helptext_printed
(
false
),
program_name
(
"unknown-caf-app"
),
slave_mode
(
false
),
config_file_path
(
default_config_file
),
slave_mode_fun
(
nullptr
)
{
...
...
@@ -187,18 +188,47 @@ settings actor_system_config::dump_content() const {
error
actor_system_config
::
parse
(
int
argc
,
char
**
argv
,
const
char
*
config_file_cstr
)
{
string_list
args
;
if
(
argc
>
1
)
args
.
assign
(
argv
+
1
,
argv
+
argc
);
if
(
argc
>
0
)
{
program_name
=
argv
[
0
];
if
(
argc
>
1
)
args
.
assign
(
argv
+
1
,
argv
+
argc
);
}
return
parse
(
std
::
move
(
args
),
config_file_cstr
);
}
error
actor_system_config
::
parse
(
int
argc
,
char
**
argv
,
std
::
istream
&
conf
)
{
string_list
args
;
if
(
argc
>
1
)
args
.
assign
(
argv
+
1
,
argv
+
argc
);
if
(
argc
>
0
)
{
program_name
=
argv
[
0
];
if
(
argc
>
1
)
args
.
assign
(
argv
+
1
,
argv
+
argc
);
}
return
parse
(
std
::
move
(
args
),
conf
);
}
std
::
pair
<
int
,
char
**>
actor_system_config
::
c_args_remainder
()
const
noexcept
{
return
{
static_cast
<
int
>
(
c_args_remainder_
.
size
()),
c_args_remainder_
.
data
()};
}
void
actor_system_config
::
set_remainder
(
string_list
args
)
{
remainder
.
swap
(
args
);
c_args_remainder_buf_
.
assign
(
program_name
.
begin
(),
program_name
.
end
());
c_args_remainder_buf_
.
emplace_back
(
'\0'
);
for
(
const
auto
&
arg
:
remainder
)
{
c_args_remainder_buf_
.
insert
(
c_args_remainder_buf_
.
end
(),
//
arg
.
begin
(),
arg
.
end
());
c_args_remainder_buf_
.
emplace_back
(
'\0'
);
}
auto
ptr
=
c_args_remainder_buf_
.
data
();
auto
end
=
ptr
+
c_args_remainder_buf_
.
size
();
auto
advance_ptr
=
[
&
ptr
]
{
while
(
*
ptr
++
!=
'\0'
)
;
// nop
};
for
(;
ptr
!=
end
;
advance_ptr
())
c_args_remainder_
.
emplace_back
(
ptr
);
}
namespace
{
struct
config_iter
{
...
...
@@ -288,15 +318,16 @@ error actor_system_config::parse(string_list args, std::istream& config) {
using
std
::
make_move_iterator
;
auto
res
=
custom_options_
.
parse
(
content
,
args
);
if
(
res
.
second
!=
args
.
end
())
{
if
(
res
.
first
!=
pec
::
success
&&
starts_with
(
*
res
.
second
,
"-"
))
if
(
res
.
first
!=
pec
::
success
&&
starts_with
(
*
res
.
second
,
"-"
))
{
return
make_error
(
res
.
first
,
*
res
.
second
);
auto
first
=
args
.
begin
();
first
+=
std
::
distance
(
args
.
c
begin
(),
res
.
second
);
remainder
.
insert
(
remainder
.
end
(),
make_move_iterator
(
first
),
make_move_iterator
(
args
.
end
()));
}
else
{
args
.
erase
(
args
.
begin
(),
res
.
second
);
set_remainder
(
std
::
move
(
args
));
}
}
else
{
cli_helptext_printed
=
get_or
(
content
,
"help"
,
false
)
||
get_or
(
content
,
"long-help"
,
false
);
set_remainder
(
string_list
{});
}
// Generate help text if needed.
if
(
cli_helptext_printed
)
{
...
...
libcaf_core/test/actor_system_config.cpp
View file @
656c6b43
...
...
@@ -86,6 +86,9 @@ CAF_TEST(parsing - without CLI arguments) {
parse
(
text
);
CAF_CHECK
(
cfg
.
remainder
.
empty
());
CAF_CHECK_EQUAL
(
get_or
(
cfg
,
"foo.bar"
,
""
),
"hello"
);
auto
[
argc
,
argv
]
=
cfg
.
c_args_remainder
();
CAF_REQUIRE_EQUAL
(
argc
,
1
);
CAF_CHECK_EQUAL
(
argv
[
0
],
cfg
.
program_name
);
}
CAF_TEST
(
parsing
-
without
CLI
cfg
.
remainder
)
{
...
...
@@ -112,11 +115,15 @@ CAF_TEST(parsing - without CLI cfg.remainder) {
CAF_TEST
(
parsing
-
with
CLI
cfg
.
remainder
)
{
auto
text
=
"foo{
\n
bar=
\"
hello
\"
}"
;
options
(
"?foo"
).
add
<
std
::
string
>
(
"bar,b"
,
"some string parameter"
);
CAF_MESSAGE
(
"valid cfg.remainder"
);
parse
(
text
,
{
"-b"
,
"test"
,
"hello"
,
"world"
});
CAF_REQUIRE_EQUAL
(
cfg
.
remainder
.
size
(),
2u
);
CAF_CHECK_EQUAL
(
get_or
(
cfg
,
"foo.bar"
,
""
),
"test"
);
CAF_CHECK_EQUAL
(
cfg
.
remainder
,
string_list
({
"hello"
,
"world"
}));
CAF_MESSAGE
(
"invalid cfg.remainder"
);
auto
[
argc
,
argv
]
=
cfg
.
c_args_remainder
();
CAF_REQUIRE_EQUAL
(
argc
,
3
);
CAF_CHECK_EQUAL
(
argv
[
0
],
cfg
.
program_name
);
CAF_CHECK_EQUAL
(
argv
[
1
],
cfg
.
remainder
[
0
]);
CAF_CHECK_EQUAL
(
argv
[
2
],
cfg
.
remainder
[
1
]);
}
CAF_TEST
(
file
input
overrides
defaults
but
CLI
args
always
win
)
{
...
...
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