在从@pacmaninbw和@ALX23z 这里获得CR后,我想要共享我的新代码,并询问改进代码的更好方法(这些方法总是存在的),即使使用新的库也是如此。对我来说唯一重要的是接收参数的方法必须是命令行我使用的是Linux操作系统,所以使用命令行params是非常常见的。
因此,为了将主函数和较小的函数分开,同时避免杂乱函数的参数处理,我创建了一个类来处理cmd参数的整个初始化部分:
true
/false
)。如果该标志存在,则该值为true
,否则它将被设置为false
。#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
#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;
}
#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;
}
@pacmaninbw评论之后,新更新的帖子:V3-在CR之后[命令行中的程序选项初始化]
发布于 2019-10-15 18:25:25
首先,感谢您提供了指向您的GitHub存储库的链接,它允许进行更完整的审查。
我注意到代码中有一种真正的倾向,即避免创建类,使用过程编程而不是面向对象的编程。使用名称空间而不是创建类。类和对象的使用可能非常强大,首先它允许继承和多态。类的使用还可以使模块解耦并减少依赖关系,目前这些模块是强耦合的,随着程序的成熟和增长,这有一种防止对体系结构进行必要更改的趋势。
我还注意到一种非常强烈的倾向,使用auto
而不是声明正确的类型。虽然auto
类型在某些情况下非常有用,例如Rangrefor循环,但是维护这段代码可能会更加困难。个人类型可以帮助我更好地理解代码。我几乎可以说这段代码滥用了auto
的使用。
std
core
目录和utilities
目录中的一个或多个源文件仍然包含using namespace std;
语句。
再一次,函数main()
太复杂了(做得太多)。随着程序规模的增加,main()
的使用应限于调用解析命令行的函数、调用为处理设置的函数、调用执行程序所需功能的函数以及调用函数以清理程序的主要部分。
这里也有一个叫做单一责任原则的编程原则。单一责任原则指出:
每个模块、类或函数都应该对软件提供的功能的单个部分负责,而该责任应该完全由该模块、类或函数封装。
这段代码可能是day.cpp
中的函数,函数原型应该在day.h
中:
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}};
函数应该返回一种类型的std::vector<day>
;
或者,函数应该执行后续的搜索日期,并返回日期本身。
auto selected_day = get_selected_day_of_the_week()
main()
中的代码目前包含一个throw exception
,但是没有try{} catch{}
代码来捕获异常,这将导致程序终止而不报告问题。最好是在调试器中报告unhandled exception
。main()
代码应该包含一个try
块和一个catch
块来处理任何异常,可能应该在main()
调用的子函数中调用throw
语句。如果此代码停留在main()
中,最好将抛出更改为std::cerr <<“消息”“<< std::endl”。
\n
而不是std::endl;
出于性能原因,\n
优于std::endl
,特别是在期望有多个std::cout
的循环中。std::endl
调用一个系统例程来刷新输出缓冲区。调用系统函数意味着在执行系统功能时,程序将被替换掉。
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;
}
被重构为
if (cmd_data.week_start_day == "help") { // Produce help days message
std::cout << "Available days:\n";
std::cout << "\tSun [Sunday]\n";
std::cout << "\tMon [Monday]\n";
std::cout << "\tTue [Tuesday]\n";
std::cout << "\tWed [Wednesday]\n";
std::cout << "\tThu [Thursday]\n";
std::cout << "\tFri [Friday]\n";
std::cout << "\tSat [Saturday]" << std::endl;
return EXIT_SUCCESS;
}
在结束时冲洗所有输出。
https://codereview.stackexchange.com/questions/230728
复制相似问题