// This program illustrates how to spawn a simple calculator // across the network. // // Run server at port 4242: // - remote_spawn -s -p 4242 // // Run client at the same host: // - remote_spawn -H localhost -p 4242 #include #include #include #include #include #include #include #include "caf/all.hpp" #include "caf/io/all.hpp" // --(rst-calculator-begin)-- using calculator = caf::typed_actor(caf::add_atom, int32_t, int32_t), caf::result(caf::sub_atom, int32_t, int32_t)>; // --(rst-calculator-end)-- CAF_BEGIN_TYPE_ID_BLOCK(remote_spawn, first_custom_type_id) CAF_ADD_TYPE_ID(remote_spawn, (calculator)) CAF_END_TYPE_ID_BLOCK(remote_spawn) using std::cerr; using std::cout; using std::endl; using std::string; using namespace caf; calculator::behavior_type calculator_fun(calculator::pointer self) { return { [=](add_atom, int32_t a, int32_t b) { aout(self) << "received task from a remote node" << endl; return a + b; }, [=](sub_atom, int32_t a, int32_t b) { aout(self) << "received task from a remote node" << endl; return a - b; }, }; } // removes leading and trailing whitespaces string trim(string s) { auto not_space = [](char c) { return isspace(c) == 0; }; // trim left s.erase(s.begin(), find_if(s.begin(), s.end(), not_space)); // trim right s.erase(find_if(s.rbegin(), s.rend(), not_space).base(), s.end()); return s; } // implements our main loop for reading user input void client_repl(function_view f) { auto usage = [] { cout << "Usage:" << endl << " quit : terminate program" << endl << " + : adds two int32_tegers" << endl << " - : subtracts two int32_tegers" << endl << endl; }; usage(); // read next line, split it, and evaluate user input string line; while (std::getline(std::cin, line)) { if ((line = trim(std::move(line))) == "quit") return; std::vector words; split(words, line, is_any_of(" "), token_compress_on); if (words.size() != 3) { usage(); continue; } auto to_int32_t = [](const string& str) -> optional { char* end = nullptr; auto res = strtol(str.c_str(), &end, 10); if (end == str.c_str() + str.size()) return static_cast(res); return none; }; auto x = to_int32_t(words[0]); auto y = to_int32_t(words[2]); if (!x || !y || (words[1] != "+" && words[1] != "-")) usage(); else cout << " = " << (words[1] == "+" ? f(add_atom_v, *x, *y) : f(sub_atom_v, *x, *y)) << "\n"; } } struct config : actor_system_config { config() { add_actor_type("calculator", calculator_fun); opt_group{custom_options_, "global"} .add(port, "port,p", "set port") .add(host, "host,H", "set node (ignored in server mode)") .add(server_mode, "server-mode,s", "enable server mode"); } uint16_t port = 0; string host = "localhost"; bool server_mode = false; }; void server(actor_system& system, const config& cfg) { auto res = system.middleman().open(cfg.port); if (!res) { cerr << "*** cannot open port: " << to_string(res.error()) << endl; return; } cout << "*** running on port: " << *res << endl << "*** press to shutdown server" << endl; getchar(); } // --(rst-client-begin)-- void client(actor_system& system, const config& cfg) { auto node = system.middleman().connect(cfg.host, cfg.port); if (!node) { cerr << "*** connect failed: " << to_string(node.error()) << endl; return; } auto type = "calculator"; // type of the actor we wish to spawn auto args = make_message(); // arguments to construct the actor auto tout = std::chrono::seconds(30); // wait no longer than 30s auto worker = system.middleman().remote_spawn(*node, type, args, tout); if (!worker) { cerr << "*** remote spawn failed: " << to_string(worker.error()) << endl; return; } // start using worker in main loop client_repl(make_function_view(*worker)); // be a good citizen and terminate remotely spawned actor before exiting anon_send_exit(*worker, exit_reason::kill); } // --(rst-client-end)-- void caf_main(actor_system& system, const config& cfg) { auto f = cfg.server_mode ? server : client; f(system, cfg); } CAF_MAIN(id_block::remote_spawn, io::middleman)