After getting a CR from @pacmaninbw and @ALX23z here, I want to share my new code, and to ask for better ways (which always exist) to improve the code, even with new libraries. The only thing that important to me, is the way of receiving the parameters have to be the command line [I am using Linux OS, so it's highly common to use command line params].
So, to separate the main to smaller functions, alongside avoiding messy functions' parameters handling, I created a class to handle the whole initialize part of the cmd params:
Edit:
- I changed the flags implementation so the user won't need to set the flag value (
true
/false
). If the flag exists the value istrue
, otherwise it'll be set tofalse
. - The project in GitHub.
- Relative revision at post creation time in GitHub.
- Please note the updated code after @pacmaninbw CR: program options from command line initialize [v3 - after CR]
cmd_options.h
#ifndef COMPUTERMONITORINGSTATISTICSPARSER_CMD_OPTIONS_H
#define COMPUTERMONITORINGSTATISTICSPARSER_CMD_OPTIONS_H
#include <iostream>
#include <boost/program_options.hpp>
struct cmd_options_data {
explicit cmd_options_data(const std::string &options_description) :
visible_options(options_description) {}
bool help = false; // Show help message
bool verbose = false; // Display login/logout details
bool anomaly_detection = false; // Show anomalies details if found
bool analyze_activity = true; // Analyze login/logout total/summarize times
std::string week_start_day;
std::string log_file_path;
std::string normal_login_word;
boost::program_options::options_description visible_options;
boost::program_options::variables_map variables_map;
};
class cmd_options {
public:
explicit cmd_options(int ac, char* av[]);
cmd_options_data get_data();
private:
boost::program_options::options_description init_cmd_po_generic_options();
boost::program_options::options_description init_cmd_po_calender_options();
boost::program_options::options_description init_cmd_po_logger_options();
boost::program_options::options_description init_cmd_po_hidden_options();
boost::program_options::options_description init_cmd_po_mode_options();
boost::program_options::positional_options_description init_cmd_positional_options();
boost::program_options::options_description group_cmd_options() {
return boost::program_options::options_description();
}
template<class... Args>
boost::program_options::options_description group_cmd_options(const boost::program_options::options_description &option, Args&... options);
void apply_program_options(int ac, char* av[]);
void update_flags();
cmd_options_data _options_data;
boost::program_options::options_description full_options;
boost::program_options::positional_options_description positional_options;
};
template<class... Args>
boost::program_options::options_description cmd_options::group_cmd_options(const boost::program_options::options_description &option, Args&... options) {
boost::program_options::options_description group;
group.add(option);
group.add(group_cmd_options(options...));
return group;
}
#endif //COMPUTERMONITORINGSTATISTICSPARSER_CMD_OPTIONS_H
cmd_options.cpp
#include "cmd_options.h"
namespace boost_cmd_po = boost::program_options;
cmd_options::cmd_options(int ac, char* av[]) : _options_data("Usage: program [options] [path/]logger_filename") {
auto generic_options = init_cmd_po_generic_options();
auto calender_options = init_cmd_po_calender_options();
auto logger_options = init_cmd_po_logger_options();
auto mode_options = init_cmd_po_mode_options();
auto hidden_options = init_cmd_po_hidden_options();
_options_data.visible_options.add(
group_cmd_options(
generic_options,
calender_options,
logger_options,
mode_options
)
);
full_options.add(
group_cmd_options(
generic_options,
calender_options,
logger_options,
mode_options,
hidden_options
)
);
positional_options = init_cmd_positional_options();
apply_program_options(ac, av);
update_flags();
}
boost_cmd_po::options_description cmd_options::init_cmd_po_generic_options() {
auto group = boost_cmd_po::options_description("Generic options");
group.add_options()
("help,h", "produce help message")
//("verbose", boost_cmd_po::value<bool>(&_options_data.verbose)->default_value(false), "Show detailed times of login.");
("verbose", "Show detailed times of login.");
return group;
}
boost_cmd_po::options_description cmd_options::init_cmd_po_calender_options() {
auto group = boost_cmd_po::options_description("Calender options");
group.add_options()
("week-start-day,d", boost_cmd_po::value<std::string>(&_options_data.week_start_day)->default_value("Monday"), "Week starting day ('--week-start-day help' for a list).");
return group;
}
boost_cmd_po::options_description cmd_options::init_cmd_po_logger_options() {
auto group = boost_cmd_po::options_description("Logger options");
group.add_options();
return group;
}
boost_cmd_po::options_description cmd_options::init_cmd_po_hidden_options() {
auto group = boost_cmd_po::options_description("Logger options");
group.add_options()
("log-path,l", boost_cmd_po::value<std::string>(&_options_data.log_file_path)->default_value( "/home/sherlock/message_from_computer"), "Path to login/logout logger.");
return group;
}
boost_cmd_po::options_description cmd_options::init_cmd_po_mode_options() {
auto group = boost_cmd_po::options_description("Mode options");
group.add_options()
//("analyze-log", boost_cmd_po::value<bool>(&_options_data.analyze_activity)->default_value(true), "Analyze activity - show activity times and summarise activity.")
("no-analyze", "Disable activity analyzing - don't show activity times/summarise.")
//("anomaly-detection", boost_cmd_po::value<bool>(&_options_data.anomaly_detection)->default_value(false), "Check for anomalies in logger.")
("anomaly-detection", "Check for anomalies in logger.")
("normal-login-word", boost_cmd_po::value<std::string>(&_options_data.normal_login_word)->default_value("login"), "For anomaly detector- word that should symbol a login line in login/logout logger (after '+' sign).");
return group;
}
boost_cmd_po::positional_options_description cmd_options::init_cmd_positional_options() {
boost_cmd_po::positional_options_description pd;
pd.add("log-path", -1);
return pd;
}
void cmd_options::apply_program_options(int ac, char **av) {
boost_cmd_po::store(
boost_cmd_po::command_line_parser(ac, av)
.options(full_options)
.positional(positional_options)
.run(), _options_data.variables_map);
boost_cmd_po::notify(_options_data.variables_map);
}
void cmd_options::update_flags() {
_options_data.help = (bool) _options_data.variables_map.count("help");
_options_data.verbose = (bool) _options_data.variables_map.count("verbose");
_options_data.analyze_activity = !(bool) _options_data.variables_map.count("no-analyze");
_options_data.anomaly_detection = (bool) _options_data.variables_map.count("anomaly-detection");
}
cmd_options_data cmd_options::get_data() {
return _options_data;
}
main.cpp
#include <iostream>
#include <boost/filesystem.hpp>
#include <boost/date_time.hpp>
#include "core/day.h"
#include "core/log_handler.h"
#include "utilities/design_text.h"
#include "cmd_options.h"
int main(int ac, char* av[]) {
cmd_options command_line_options(ac, av);
cmd_options_data cmd_data = command_line_options.get_data();
/// --help / -h option handler
if (cmd_data.help) {
std::cout << cmd_data.visible_options << "\n";
return EXIT_SUCCESS;
}
/// --log-path / -l option handler
if (!boost::filesystem::exists(cmd_data.log_file_path))
throw std::runtime_error("Log file path doesn't exist.");
/// --week-start-day / -d option handler
/// Initialize available days list
auto available_days = std::vector<day>{{"sunday", boost::date_time::weekdays::Sunday},
{"monday", boost::date_time::weekdays::Monday},
{"tuesday", boost::date_time::weekdays::Tuesday},
{"wednesday", boost::date_time::weekdays::Wednesday},
{"thursday", boost::date_time::weekdays::Thursday},
{"friday", boost::date_time::weekdays::Friday},
{"saturday", boost::date_time::weekdays::Saturday}};
if (auto selected_day = std::find(available_days.begin(), available_days.end(), boost::to_lower_copy(cmd_data.week_start_day)); selected_day != available_days.end()) { // Selected day exists
log_handler::week_start_day = selected_day->day_symbol;
} else { // Selected day doesn't exists
if (cmd_data.week_start_day == "help") { // Produce help days message
std::cout << "Available days:" << std::endl;
std::cout << "\tSun [Sunday]" << std::endl;
std::cout << "\tMon [Monday]" << std::endl;
std::cout << "\tTue [Tuesday]" << std::endl;
std::cout << "\tWed [Wednesday]" << std::endl;
std::cout << "\tThu [Thursday]" << std::endl;
std::cout << "\tFri [Friday]" << std::endl;
std::cout << "\tSat [Saturday]" << std::endl;
return EXIT_SUCCESS;
}
throw std::runtime_error("Unfamiliar day, for options list use '-d [ --week-start-day ] help'.");
}
// Anomalies detector
auto anomaly_detected = log_handler::anomalies_detector(cmd_data.log_file_path, cmd_data.normal_login_word, cmd_data.anomaly_detection);
if (cmd_data.analyze_activity) // Analyze logger times
log_handler::analyze(cmd_data.log_file_path, cmd_data.verbose);
if (anomaly_detected) // Produce anomalies warning if needed
std::cout << "\n\n" << design_text::make_colored(std::stringstream() << "*** Anomaly detected! ***", design_text::Color::NONE, design_text::Color::RED, true) << std::endl;
return EXIT_SUCCESS;
}
Update:
After @pacmaninbw review, new updated post: program options from command line initialize [v3 - after CR]
boost::program_options
has been reduced, the implementation can be changed without altering the code inmain()
. I suggest that you add code to main that tests cmd_options and cmd_options_data. Show how they are used within the program, and use the 3 flags that have been created. Test with and withoutverbose
, with and withoutanomaly_detection
and with and withoutanalyze_activity
. After this test with 2 of each of the options and then finally test with all 3 options at once. \$\endgroup\$