Commit 656c6b43 authored by Dominik Charousset's avatar Dominik Charousset

Merge pull request #1165

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