Commit 89115514 authored by Dominik Charousset's avatar Dominik Charousset

Add `message::filter` function & CLI utility

parent ac19cd13
...@@ -21,50 +21,26 @@ ...@@ -21,50 +21,26 @@
#include <QApplication> #include <QApplication>
#include "caf/all.hpp" #include "caf/all.hpp"
#include "cppa/opt.hpp"
#include "ui_chatwindow.h" // auto generated from chatwindow.ui #include "ui_chatwindow.h" // auto generated from chatwindow.ui
using namespace std; using namespace std;
using namespace caf; using namespace caf;
/*
namespace {
struct line { string str; };
istream& operator>>(istream& is, line& l) {
getline(is, l.str);
return is;
}
any_tuple s_last_line;
any_tuple split_line(const line& l) {
vector<string> result;
stringstream strs(l.str);
string tmp;
while (getline(strs, tmp, ' ')) {
if (!tmp.empty()) result.push_back(std::move(tmp));
}
s_last_line = any_tuple::view(std::move(result));
return s_last_line;
}
} // namespace <anonymous>
*/
int main(int argc, char** argv) { int main(int argc, char** argv) {
string name; string name;
string group_id; string group_id;
options_description desc; auto res = message_builder(argv + 1, argv + argc).filter_cli({
bool args_valid = match_stream<string>(argv + 1, argv + argc) ( {"name,n", "set chat name", name},
on_opt1('n', "name", &desc, "set name") >> rd_arg(name), {"group,g", "join chat group", group_id}
on_opt1('g', "group", &desc, "join group <arg1>") >> rd_arg(group_id), });
on_opt0('h', "help", &desc, "print help") >> print_desc_and_exit(&desc) if (!res.remainder.empty()) {
); std::cerr << res.helptext << std::endl;
return 1;
}
if (res.opts.count("help") > 0) {
return 0;
}
group gptr; group gptr;
// evaluate group parameter // evaluate group parameter
if (!group_id.empty()) { if (!group_id.empty()) {
...@@ -72,13 +48,10 @@ int main(int argc, char** argv) { ...@@ -72,13 +48,10 @@ int main(int argc, char** argv) {
if (p == std::string::npos) { if (p == std::string::npos) {
cerr << "*** error parsing argument " << group_id cerr << "*** error parsing argument " << group_id
<< ", expected format: <module_name>:<group_id>"; << ", expected format: <module_name>:<group_id>";
} } else {
else {
try { try {
gptr = group::get(group_id.substr(0, p), gptr = group::get(group_id.substr(0, p), group_id.substr(p + 1));
group_id.substr(p + 1)); } catch (exception& e) {
}
catch (exception& e) {
ostringstream err; ostringstream err;
cerr << "*** exception: group::get(\"" << group_id.substr(0, p) cerr << "*** exception: group::get(\"" << group_id.substr(0, p)
<< "\", \"" << group_id.substr(p + 1) << "\") failed; " << "\", \"" << group_id.substr(p + 1) << "\") failed; "
...@@ -86,9 +59,6 @@ int main(int argc, char** argv) { ...@@ -86,9 +59,6 @@ int main(int argc, char** argv) {
} }
} }
} }
if (!args_valid) print_desc_and_exit(&desc)();
QApplication app(argc, argv); QApplication app(argc, argv);
app.setQuitOnLastWindowClosed(true); app.setQuitOnLastWindowClosed(true);
QMainWindow mw; QMainWindow mw;
...@@ -102,8 +72,8 @@ int main(int argc, char** argv) { ...@@ -102,8 +72,8 @@ int main(int argc, char** argv) {
send_as(client, client, atom("join"), gptr); send_as(client, client, atom("join"), gptr);
} }
mw.show(); mw.show();
auto res = app.exec(); auto app_res = app.exec();
shutdown();
await_all_actors_done(); await_all_actors_done();
return res; shutdown();
return app_res;
} }
...@@ -21,9 +21,6 @@ ...@@ -21,9 +21,6 @@
#include "caf/all.hpp" #include "caf/all.hpp"
#include "caf/io/all.hpp" #include "caf/io/all.hpp"
// the options_description API is deprecated
#include "cppa/opt.hpp"
using namespace std; using namespace std;
using namespace caf; using namespace caf;
...@@ -183,41 +180,40 @@ void client_repl(const string& host, uint16_t port) { ...@@ -183,41 +180,40 @@ void client_repl(const string& host, uint16_t port) {
} }
int main(int argc, char** argv) { int main(int argc, char** argv) {
string mode;
string host;
uint16_t port = 0; uint16_t port = 0;
options_description desc; string host = "localhost";
auto set_mode = [&](const string& arg) -> function<bool()> { auto res = message_builder(argv + 1, argv + argc).to_message().filter_cli({
return [arg, &mode]() -> bool { {"port,p", "set port (either to publish at or to connect to)", port},
if (!mode.empty()) { {"host,H", "set host (client mode only, default: localhost)", host},
cerr << "mode already set to " << mode << endl; {"server,s", "run in server mode"},
return false; {"client,c", "run in client mode"}
} });
mode = move(arg); if (res.opts.count("help") > 0) {
return true; return 0;
}; }
}; if (!res.remainder.empty()) {
string copts = "client options"; cerr << "*** invalid command line options" << endl << res.helptext << endl;
string sopts = "server options"; return 1;
bool args_valid = match_stream<string> (argv + 1, argv + argc) ( }
on_opt1('p', "port", &desc, "set port") >> rd_arg(port), bool is_server = res.opts.count("server") > 0;
on_opt1('H', "host", &desc, "set host (default: localhost)", copts) >> rd_arg(host), if (is_server == (res.opts.count("client") > 0)) {
on_opt0('s', "server", &desc, "run in server mode", sopts) >> set_mode("server"), if (is_server) {
on_opt0('c', "client", &desc, "run in client mode", copts) >> set_mode("client"), cerr << "*** cannot be server and client at the same time" << endl;
on_opt0('h', "help", &desc, "print help") >> print_desc_and_exit(&desc) } else {
); cerr << "*** either --server or --client option must be set" << endl;
if (!args_valid || port == 0 || mode.empty()) { }
if (port == 0) cerr << "*** no port specified" << endl; return 1;
if (mode.empty()) cerr << "*** no mode specified" << endl; }
cerr << endl; if (!is_server && port == 0) {
auto description_printer = print_desc(&desc, cerr); cerr << "*** no port to server specified" << endl;
description_printer(); return 1;
return -1; }
} if (is_server) {
if (mode == "server") {
try { try {
// try to publish math actor at given port // try to publish math actor at given port
io::publish(spawn(calculator), port); cout << "*** try publish at port " << port << endl;
auto p = io::publish(spawn(calculator), port);
cout << "*** server successfully published at port " << p << endl;
} }
catch (exception& e) { catch (exception& e) {
cerr << "*** unable to publish math actor at port " << port << "\n" cerr << "*** unable to publish math actor at port " << port << "\n"
...@@ -226,10 +222,8 @@ int main(int argc, char** argv) { ...@@ -226,10 +222,8 @@ int main(int argc, char** argv) {
} }
} }
else { else {
if (host.empty()) host = "localhost";
client_repl(host, port); client_repl(host, port);
} }
await_all_actors_done(); await_all_actors_done();
shutdown(); shutdown();
return 0;
} }
...@@ -18,9 +18,6 @@ ...@@ -18,9 +18,6 @@
#include "caf/all.hpp" #include "caf/all.hpp"
#include "caf/io/all.hpp" #include "caf/io/all.hpp"
// the options_description API is deprecated
#include "cppa/opt.hpp"
using namespace std; using namespace std;
using namespace caf; using namespace caf;
...@@ -80,14 +77,16 @@ void client(event_based_actor* self, const string& name) { ...@@ -80,14 +77,16 @@ void client(event_based_actor* self, const string& name) {
int main(int argc, char** argv) { int main(int argc, char** argv) {
string name; string name;
string group_id; string group_id;
options_description desc; auto res = message_builder(argv + 1, argv + argc).filter_cli({
bool args_valid = match_stream<string>(argv + 1, argv + argc) ( {"name,n", "set name", name},
on_opt1('n', "name", &desc, "set name") >> rd_arg(name), {"group,g", "join group", group_id}
on_opt1('g', "group", &desc, "join group <arg1>") >> rd_arg(group_id), });
on_opt0('h', "help", &desc, "print help") >> print_desc_and_exit(&desc) if (!res.remainder.empty()) {
); std::cout << res.helptext << std::endl;
if (!args_valid) { return 1;
print_desc_and_exit(&desc)(); }
if (res.opts.count("help") > 0) {
return 0;
} }
while (name.empty()) { while (name.empty()) {
cout << "please enter your name: " << flush; cout << "please enter your name: " << flush;
......
...@@ -20,10 +20,13 @@ ...@@ -20,10 +20,13 @@
#ifndef CAF_MESSAGE_HPP #ifndef CAF_MESSAGE_HPP
#define CAF_MESSAGE_HPP #define CAF_MESSAGE_HPP
#include <tuple>
#include <type_traits> #include <type_traits>
#include "caf/atom.hpp" #include "caf/atom.hpp"
#include "caf/config.hpp" #include "caf/config.hpp"
#include "caf/from_string.hpp"
#include "caf/skip_message.hpp"
#include "caf/detail/int_list.hpp" #include "caf/detail/int_list.hpp"
#include "caf/detail/comparable.hpp" #include "caf/detail/comparable.hpp"
...@@ -93,29 +96,34 @@ class message { ...@@ -93,29 +96,34 @@ class message {
} }
/** /**
* Creates a new tuple with all but the first n values. * Creates a new message with all but the first n values.
*/ */
message drop(size_t n) const; message drop(size_t n) const;
/** /**
* Creates a new tuple with all but the last n values. * Creates a new message with all but the last n values.
*/ */
message drop_right(size_t n) const; message drop_right(size_t n) const;
/** /**
* Creates a new tuple from the first n values. * Creates a new message from the first n values.
*/ */
inline message take(size_t n) const { inline message take(size_t n) const {
return n >= size() ? *this : drop_right(size() - n); return n >= size() ? *this : drop_right(size() - n);
} }
/** /**
* Creates a new tuple from the last n values. * Creates a new message from the last n values.
*/ */
inline message take_right(size_t n) const { inline message take_right(size_t n) const {
return n >= size() ? *this : drop(size() - n); return n >= size() ? *this : drop(size() - n);
} }
/**
* Creates a new message of size `n` starting at element `pos`.
*/
message slice(size_t pos, size_t n) const;
/** /**
* Gets a mutable pointer to the element at position @p p. * Gets a mutable pointer to the element at position @p p.
*/ */
...@@ -199,21 +207,91 @@ class message { ...@@ -199,21 +207,91 @@ class message {
} }
/** /**
* Checks whether this tuple is dynamically typed, i.e., * Checks whether this message is dynamically typed, i.e.,
* its types were not known at compile time. * its types were not known at compile time.
*/ */
bool dynamically_typed() const; bool dynamically_typed() const;
/** /**
* Applies @p handler to this message and returns the result * Applies `handler` to this message and returns the result
* of `handler(*this)`. * of `handler(*this)`.
*/ */
optional<message> apply(message_handler handler); optional<message> apply(message_handler handler);
/**
* Returns a new message consisting of all elements that were *not*
* matched by `handler`.
*/
message filter(message_handler handler) const;
/**
* Stores the name of a command line option ("<long name>[,<short name>]")
* along with a description and a callback.
*/
struct cli_arg {
std::string name;
std::string text;
std::function<bool (const std::string&)> fun;
inline cli_arg(std::string nstr, std::string tstr)
: name(std::move(nstr)),
text(std::move(tstr)) {
// nop
}
inline cli_arg(std::string nstr, std::string tstr, std::string& arg)
: name(std::move(nstr)),
text(std::move(tstr)) {
fun = [&arg](const std::string& str) -> bool{
arg = str;
return true;
};
}
inline cli_arg(std::string nstr, std::string tstr, std::vector<std::string>& arg)
: name(std::move(nstr)),
text(std::move(tstr)) {
fun = [&arg](const std::string& str) -> bool {
arg.push_back(str);
return true;
};
}
template <class T>
cli_arg(std::string nstr, std::string tstr, T& arg)
: name(std::move(nstr)),
text(std::move(tstr)) {
fun = [&arg](const std::string& str) -> bool {
auto res = from_string<T>(str);
if (!res) {
return false;
}
arg = *res;
return true;
};
}
template <class T>
cli_arg(std::string nstr, std::string tstr, std::vector<T>& arg)
: name(std::move(nstr)),
text(std::move(tstr)) {
fun = [&arg](const std::string& str) -> bool {
auto res = from_string<T>(str);
if (!res) {
return false;
}
arg.push_back(*res);
return true;
};
}
};
struct cli_res;
/**
* A simplistic interface for using `filter` to parse command line options.
*/
cli_res filter_cli(std::vector<cli_arg> args) const;
/** @cond PRIVATE */ /** @cond PRIVATE */
inline uint32_t type_token() const { inline uint32_t type_token() const {
return m_vals->type_token(); return m_vals ? m_vals->type_token() : 0xFFFFFFFF;
} }
inline void force_detach() { inline void force_detach() {
...@@ -224,7 +302,7 @@ class message { ...@@ -224,7 +302,7 @@ class message {
explicit message(raw_ptr); explicit message(raw_ptr);
inline const std::string* tuple_type_names() const { inline std::string tuple_type_names() const {
return m_vals->tuple_type_names(); return m_vals->tuple_type_names();
} }
...@@ -246,9 +324,26 @@ class message { ...@@ -246,9 +324,26 @@ class message {
/** @endcond */ /** @endcond */
private: private:
message filter_impl(size_t start, message_handler handler) const;
data_ptr m_vals; data_ptr m_vals;
};
/**
* Stores the result of `message::filter_cli`.
*/
struct message::cli_res {
/**
* Stores the remaining (unmatched) arguments.
*/
message remainder;
/**
* Stores the names of all active options.
*/
std::set<std::string> opts;
/**
* Stores the automatically generated help text.
*/
std::string helptext;
}; };
/** /**
......
...@@ -90,6 +90,14 @@ class message_builder { ...@@ -90,6 +90,14 @@ class message_builder {
*/ */
message to_message(); message to_message();
inline message filter(message_handler f) {
return to_message().filter(f);
}
inline message::cli_res filter_cli(std::vector<message::cli_arg> args) {
return to_message().filter_cli(std::move(args));
}
/** /**
* Convenience function for `to_message().apply(handler). * Convenience function for `to_message().apply(handler).
*/ */
......
...@@ -18,7 +18,11 @@ ...@@ -18,7 +18,11 @@
******************************************************************************/ ******************************************************************************/
#include "caf/message.hpp" #include "caf/message.hpp"
#include <iostream>
#include "caf/message_handler.hpp" #include "caf/message_handler.hpp"
#include "caf/string_algorithms.hpp"
#include "caf/detail/singletons.hpp" #include "caf/detail/singletons.hpp"
#include "caf/detail/decorated_tuple.hpp" #include "caf/detail/decorated_tuple.hpp"
...@@ -90,8 +94,17 @@ message message::drop_right(size_t n) const { ...@@ -90,8 +94,17 @@ message message::drop_right(size_t n) const {
return message{}; return message{};
} }
std::vector<size_t> mapping(size() - n); std::vector<size_t> mapping(size() - n);
size_t i = 0; std::iota(mapping.begin(), mapping.end(), size_t{0});
std::generate(mapping.begin(), mapping.end(), [&] { return i++; }); return message{detail::decorated_tuple::create(m_vals, std::move(mapping))};
}
message message::slice(size_t pos, size_t n) const {
auto s = size();
if (pos >= s) {
return message{};
}
std::vector<size_t> mapping(std::min(s - pos, n));
std::iota(mapping.begin(), mapping.end(), pos);
return message{detail::decorated_tuple::create(m_vals, std::move(mapping))}; return message{detail::decorated_tuple::create(m_vals, std::move(mapping))};
} }
...@@ -103,11 +116,160 @@ message::const_iterator message::begin() const { ...@@ -103,11 +116,160 @@ message::const_iterator message::begin() const {
return m_vals ? m_vals->begin() : const_iterator{nullptr, 0}; return m_vals ? m_vals->begin() : const_iterator{nullptr, 0};
} }
/**
* Returns an iterator to the end.
*/
message::const_iterator message::end() const { message::const_iterator message::end() const {
return m_vals ? m_vals->end() : const_iterator{nullptr, 0}; return m_vals ? m_vals->end() : const_iterator{nullptr, 0};
} }
message message::filter_impl(size_t start, message_handler handler) const {
auto s = size();
for (size_t i = start; i < s; ++i) {
for (size_t n = (s - i) ; n > 0; --n) {
auto res = handler(slice(i, n));
if (res) {
std::vector<size_t> mapping(s);
std::iota(mapping.begin(), mapping.end(), size_t{0});
auto first = mapping.begin() + static_cast<ptrdiff_t>(i);
auto last = first + static_cast<ptrdiff_t>(n);
mapping.erase(first, last);
if (mapping.empty()) {
return message{};
}
message next{detail::decorated_tuple::create(m_vals,
std::move(mapping))};
return next.filter_impl(i, handler);
}
}
}
return *this;
}
message message::filter(message_handler handler) const {
return filter_impl(0, handler);
}
message::cli_res message::filter_cli(std::vector<cli_arg> cliargs) const {
std::set<std::string> opts;
cli_arg dummy{"help,h", ""};
std::map<std::string, cli_arg*> shorts;
std::map<std::string, cli_arg*> longs;
shorts["-h"] = &dummy;
shorts["-?"] = &dummy;
longs["--help"] = &dummy;
for (auto& cliarg : cliargs) {
std::vector<std::string> s;
split(s, cliarg.name, is_any_of(","), token_compress_on);
if (s.size() == 2 && s.back().size() == 1) {
longs["--" + s.front()] = &cliarg;
shorts["-" + s.back()] = &cliarg;
} else if (s.size() == 1) {
longs[s.front()] = &cliarg;
} else {
throw std::invalid_argument("invalid option name: " + cliarg.name);
}
}
auto insert_opt_name = [&](const cli_arg* ptr) {
auto separator = ptr->name.find(',');
if (separator == std::string::npos) {
opts.insert(ptr->name);
} else {
opts.insert(ptr->name.substr(0, separator));
}
};
auto res = filter({
[&](const std::string& arg) -> optional<skip_message_t> {
if (arg.empty() || arg.front() != '-') {
return skip_message();
}
auto i = shorts.find(arg);
if (i != shorts.end()) {
if (i->second->fun) {
return skip_message();
}
insert_opt_name(i->second);
return none;
}
auto eq_pos = arg.find('=');
auto j = longs.find(arg.substr(0, eq_pos));
if (j != longs.end()) {
if (j->second->fun) {
if (eq_pos == std::string::npos) {
std::cerr << "missing argument to " << arg << std::endl;
return none;
}
if (!j->second->fun(arg.substr(eq_pos + 1))) {
std::cerr << "invalid value for option "
<< j->second->name << ": " << arg << std::endl;
return none;
}
insert_opt_name(j->second);
return none;
}
insert_opt_name(j->second);
return none;
}
std::cerr << "unknown command line option: " << arg << std::endl;
return none;
},
[&](const std::string& arg1,
const std::string& arg2) -> optional<skip_message_t> {
if (arg1.size() < 2 || arg1[0] != '-' || arg1[1] == '-') {
return skip_message();
}
auto i = shorts.find(arg1);
if (i != shorts.end() && i->second->fun) {
if (!i->second->fun(arg2)) {
std::cerr << "invalid value for option "
<< i->second->name << ": " << arg2 << std::endl;
return none;
}
insert_opt_name(i->second);
return none;
}
std::cerr << "unknown command line option: " << arg1 << std::endl;
return none;
}
});
size_t name_width = 0;
for (auto& ca : cliargs) {
// name field contains either only "--<long_name>" or
// "-<short name> [--<long name>]" depending on whether or not
// a ',' appears in the name
auto nw = ca.name.find(',') == std::string::npos
? ca.name.size() + 2 // "--<name>"
: ca.name.size() + 5; // "-X [--<name>]" (minus trailing ",X")
if (ca.fun) {
nw += 4; // trailing " arg"
}
name_width = std::max(name_width, nw);
}
std::ostringstream oss;
oss << std::left;
oss << "Allowed options:" << std::endl;
for (auto& ca : cliargs) {
std::string lhs;
auto separator = ca.name.find(',');
if (separator == std::string::npos) {
lhs += "--";
lhs += ca.name;
} else {
lhs += "-";
lhs += ca.name.back();
lhs += " [--";
lhs += ca.name.substr(0, separator);
lhs += "]";
}
if (ca.fun) {
lhs += " arg";
}
oss << " ";
oss.width(static_cast<std::streamsize>(name_width));
oss << lhs << " : " << ca.text << std::endl;
}
auto helptext = oss.str();
if (opts.count("help") == 1) {
std::cout << helptext << std::endl;
}
return {res, std::move(opts), std::move(helptext)};
}
} // namespace caf } // namespace caf
...@@ -90,10 +90,6 @@ class message_builder::dynamic_msg_data : public detail::message_data { ...@@ -90,10 +90,6 @@ class message_builder::dynamic_msg_data : public detail::message_data {
return m_type_token; return m_type_token;
} }
const std::string* tuple_type_names() const override {
return nullptr; // get_tuple_type_names(*this);
}
std::vector<uniform_value> m_elements; std::vector<uniform_value> m_elements;
uint32_t m_type_token; uint32_t m_type_token;
}; };
......
...@@ -21,6 +21,7 @@ add_unit_test(atom) ...@@ -21,6 +21,7 @@ add_unit_test(atom)
add_unit_test(metaprogramming) add_unit_test(metaprogramming)
add_unit_test(intrusive_containers) add_unit_test(intrusive_containers)
add_unit_test(match) add_unit_test(match)
add_unit_test(message)
add_unit_test(serialization) add_unit_test(serialization)
add_unit_test(uniform_type) add_unit_test(uniform_type)
add_unit_test(fixed_vector) add_unit_test(fixed_vector)
......
/******************************************************************************
* ____ _ _____ *
* / ___| / \ | ___| C++ *
* | | / _ \ | |_ Actor *
* | |___ / ___ \| _| Framework *
* \____/_/ \_|_| *
* *
* Copyright (C) 2011 - 2014 *
* Dominik Charousset <dominik.charousset (at) haw-hamburg.de> *
* *
* Distributed under the terms and conditions of the BSD 3-Clause License or *
* (at your option) under the terms and conditions of the Boost Software *
* License 1.0. See accompanying files LICENSE and LICENSE_ALTERNATIVE. *
* *
* If you did not receive a copy of the license files, see *
* http://opensource.org/licenses/BSD-3-Clause and *
* http://www.boost.org/LICENSE_1_0.txt. *
******************************************************************************/
#include "test.hpp"
#include "caf/message.hpp"
using std::cout;
using std::endl;
using namespace caf;
void test_drop() {
auto m1 = make_message(1, 2, 3, 4, 5);
std::vector<message> messages{
m1,
make_message(2, 3, 4, 5),
make_message(3, 4, 5),
make_message(4, 5),
make_message(5),
message{}
};
for (size_t i = 0; i < messages.size(); ++i) {
CAF_CHECK_EQUAL(to_string(m1.drop(i)), to_string(messages[i]));
}
}
void test_slice() {
auto m1 = make_message(1, 2, 3, 4, 5);
auto m2 = m1.slice(2, 2);
CAF_CHECK_EQUAL(to_string(m2), to_string(make_message(3, 4)));
}
void test_filter1(int lhs1, int lhs2, int lhs3, int rhs1, int rhs2, int val) {
auto m1 = make_message(lhs1, lhs2, lhs3);
auto m2 = make_message(rhs1, rhs2);
auto m3 = m1.filter(on(val) >> [] {});
CAF_CHECK_EQUAL(to_string(m2), to_string(m3));
}
void test_filter2() {
auto m1 = make_message(1.0, 2.0, 3.0);
auto m2 = make_message(1, 2, 1.0, 2.0, 3.0);
auto m3 = make_message(1.0, 1, 2, 2.0, 3.0);
auto m4 = make_message(1.0, 2.0, 1, 2, 3.0);
auto m5 = make_message(1.0, 2.0, 3.0, 1, 2);
auto m6 = make_message(1, 2, 1.0, 2.0, 3.0, 1, 2);
auto m7 = make_message(1.0, 1, 2, 3, 4, 2.0, 3.0);
message_handler f{
[](int, int) { },
[](float, float) { }
};
CAF_CHECK_EQUAL(to_string(m2.filter(f)), to_string(m1));
CAF_CHECK_EQUAL(to_string(m3.filter(f)), to_string(m1));
CAF_CHECK_EQUAL(to_string(m4.filter(f)), to_string(m1));
CAF_CHECK_EQUAL(to_string(m5.filter(f)), to_string(m1));
CAF_CHECK_EQUAL(to_string(m6.filter(f)), to_string(m1));
CAF_CHECK_EQUAL(to_string(m7.filter(f)), to_string(m1));
}
void test_filter3() {
auto m1 = make_message(1);
CAF_CHECK_EQUAL(to_string(m1.filter([](int) {})), to_string(message{}));
auto m2 = make_message(1.0, 2, 3, 4.0);
auto m3 = m2.filter({
[](int, int) { },
[](double, double) { }
});
// check for false positives through collapsing
CAF_CHECK_EQUAL(to_string(m3), to_string(make_message(1.0, 4.0)));
}
void test_filter_cli() {
auto f = [](std::vector<std::string> args) {
std::string filename;
auto res = message_builder(args.begin(), args.end()).filter_cli({
{"version,v", "print version"},
{"file,f", "set output file", filename},
{"whatever", "do whatever"}
});
CAF_CHECK_EQUAL(res.opts.count("file"), 1);
CAF_CHECK_EQUAL(to_string(res.remainder), to_string(message{}));
CAF_CHECK_EQUAL(filename, "hello.txt");
};
f({"--file=hello.txt"});
f({"-f", "hello.txt"});
}
void test_type_token() {
auto m1 = make_message(get_atom::value);
CAF_CHECK_EQUAL(m1.type_token(), detail::make_type_token<get_atom>());
}
int main() {
CAF_TEST(message);
test_drop();
test_slice();
test_filter1(1, 2, 3, 2, 3, 1);
test_filter1(1, 2, 3, 1, 3, 2);
test_filter1(1, 2, 3, 1, 2, 3);
test_filter2();
test_filter3();
test_filter_cli();
test_type_token();
return CAF_TEST_RESULT();
}
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