The program should first check whether all arguments have been specified correctly, and only then execute the actual actions. There should be a waiting period between two actions.
The arguments provided should be parsed using an FSM (if there is no simpler alternative).
Is the error handling correct? Could something be simpler or more robust? I would like to hear what you think about it.
#include <string>
#include <iostream>
#include <map>
#include <vector>
#include <algorithm>
#include <functional>
enum class Symbol
{
NA,
SLEEP,
TARGET,
ACTION1,
ACTION2
};
void action1(std::string target, int val)
{
std::cout << "action1: " << target << ", " << val << std::endl;
}
void action2(std::string target, int val, int magic)
{
std::cout << "action2: " << target << ", " << val << ", " << magic << std::endl;
}
void action3(int sleep)
{
std::cout << "action3: " << sleep << std::endl;
}
void exitApp(std::string cause)
{
std::cout << "EXIT: " << cause << std::endl;
std::exit(0);
}
/*
* Call with: ./a.out (sleep=<ms_int>) (target=<ip/host_str>) [a/b]=<port_int> ...
* Example: ./a.out sleep=2500 target=localhost b=123 a=23 b=123
* Example: ./a.out b=23
*
* The order of sleep and target arguments is not important, but they must not be specified twice;
* the a/b list must contain at least one action and must be at the end.
*/
int main(int argc, char *argv[])
{
// default values
std::string target = "0.0.0.0";
int sleep = 1000;
std::map<int, std::map<Symbol, int>> fsm;
fsm[0][Symbol::SLEEP] = 1;
fsm[0][Symbol::TARGET] = 2;
fsm[0][Symbol::ACTION1] = 3;
fsm[0][Symbol::ACTION2] = 3;
fsm[1][Symbol::TARGET] = 3;
fsm[1][Symbol::ACTION1] = 3;
fsm[1][Symbol::ACTION2] = 3;
fsm[2][Symbol::SLEEP] = 3;
fsm[2][Symbol::ACTION1] = 3;
fsm[2][Symbol::ACTION2] = 3;
fsm[3][Symbol::ACTION1] = 3;
fsm[3][Symbol::ACTION2] = 3;
std::vector<std::function<void()>> actions;
// parse command line
int i = 1;
int state = 0;
while (i < argc)
{
std::string arg{argv[i++]};
size_t equals = arg.find('=');
if (equals != std::string::npos)
{
std::string key = arg.substr(0, equals);
std::string value = arg.substr(equals + 1);
Symbol symbol = Symbol::NA;
if (key == "sleep")
{
symbol = Symbol::SLEEP;
}
if (key == "target")
{
symbol = Symbol::TARGET;
}
if (key == "a")
{
symbol = Symbol::ACTION1;
}
if (key == "b")
{
symbol = Symbol::ACTION2;
}
if (symbol == Symbol::NA)
{
exitApp("Unkown arg");
}
if (!fsm[state].count(symbol))
{
exitApp("Not allowed arg");
}
if (symbol == Symbol::SLEEP)
{
sleep = std::stoi(value);
}
if (symbol == Symbol::TARGET)
{
target = value;
}
if (symbol == Symbol::ACTION1)
{
int val = std::stoi(value);
if (val < 1 || val >= 1 << 16)
{
exitApp("Action val not in range");
}
actions.push_back([=]()
{ action1(target, val); });
actions.push_back([=]()
{ action3(sleep); });
}
if (symbol == Symbol::ACTION2)
{
int val = std::stoi(value);
if (val < 1 || val >= 1 << 16)
{
exitApp("Action val not in range");
}
actions.push_back([=]()
{ action2(target, val, 1234); });
actions.push_back([=]()
{ action3(sleep); });
}
state = fsm[state][symbol];
}
}
if (actions.empty())
{
exitApp("No action args given");
}
actions.pop_back(); // Pop last unneeded action
// Perfom actions
for (const auto &action : actions)
{
action();
}
return 0;
}
std::exit()in C++. It is a C library function. The fact that it is available in C++ does not mean it is for C++. \$\endgroup\$