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

Merge pull request #1165

parents dbdf9b26 2e1201df
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++
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
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
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++
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
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
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++
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
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
......@@ -59,4 +59,7 @@ SpacesInParentheses: false
SpacesInSquareBrackets: false
Standard: Cpp11
UseTab: Never
---
Language: Proto
DisableFormat: true
...
......@@ -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',
......
......@@ -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.
......
......@@ -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()
......
#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(id_block::protobuf_example, io::middleman)
......@@ -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)
......
......@@ -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());
}
......@@ -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();
......
......@@ -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(system);
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)
// 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>
......
......@@ -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>
......
......@@ -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>
......
package org.libcppa;
syntax = "proto2";
package org.caf;
message Ping {
required int32 id = 1;
......
......@@ -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);
......
......@@ -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.cbegin(), 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) {
......
......@@ -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{\nbar=\"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) {
......
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