首页
学习
活动
专区
圈层
工具
发布
首页
学习
活动
专区
圈层
工具
MCP广场
社区首页 >问答首页 >命令行中的程序选项初始化[v2 -在CR之后]

命令行中的程序选项初始化[v2 -在CR之后]
EN

Code Review用户
提问于 2019-10-14 23:50:09
回答 1查看 137关注 0票数 6

在从@pacmaninbw和@ALX23z 这里获得CR后,我想要共享我的新代码,并询问改进代码的更好方法(这些方法总是存在的),即使使用新的库也是如此。对我来说唯一重要的是接收参数的方法必须是命令行我使用的是Linux操作系统,所以使用命令行params是非常常见的。

因此,为了将主函数和较小的函数分开,同时避免杂乱函数的参数处理,我创建了一个类来处理cmd参数的整个初始化部分:

编辑:

cmd_options.h

代码语言:javascript
运行
复制
#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

代码语言:javascript
运行
复制
#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

代码语言:javascript
运行
复制
#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之后[命令行中的程序选项初始化]

EN

回答 1

Code Review用户

回答已采纳

发布于 2019-10-15 18:25:25

首先,感谢您提供了指向您的GitHub存储库的链接,它允许进行更完整的审查。

我注意到代码中有一种真正的倾向,即避免创建类,使用过程编程而不是面向对象的编程。使用名称空间而不是创建类。类和对象的使用可能非常强大,首先它允许继承和多态。类的使用还可以使模块解耦并减少依赖关系,目前这些模块是强耦合的,随着程序的成熟和增长,这有一种防止对体系结构进行必要更改的趋势。

我还注意到一种非常强烈的倾向,使用auto而不是声明正确的类型。虽然auto类型在某些情况下非常有用,例如Rangrefor循环,但是维护这段代码可能会更加困难。个人类型可以帮助我更好地理解代码。我几乎可以说这段代码滥用了auto的使用。

避免使用名称空间std

core目录和utilities目录中的一个或多个源文件仍然包含using namespace std;语句。

复杂性

再一次,函数main()太复杂了(做得太多)。随着程序规模的增加,main()的使用应限于调用解析命令行的函数、调用为处理设置的函数、调用执行程序所需功能的函数以及调用函数以清理程序的主要部分。

这里也有一个叫做单一责任原则的编程原则。单一责任原则指出:

每个模块、类或函数都应该对软件提供的功能的单个部分负责,而该责任应该完全由该模块、类或函数封装。

这段代码可能是day.cpp中的函数,函数原型应该在day.h中:

代码语言:javascript
运行
复制
    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>

或者,函数应该执行后续的搜索日期,并返回日期本身。

代码语言:javascript
运行
复制
    auto selected_day = get_selected_day_of_the_week()

尝试捕捉抛出块

main()中的代码目前包含一个throw exception,但是没有try{} catch{}代码来捕获异常,这将导致程序终止而不报告问题。最好是在调试器中报告unhandled exceptionmain()代码应该包含一个try块和一个catch块来处理任何异常,可能应该在main()调用的子函数中调用throw语句。如果此代码停留在main()中,最好将抛出更改为std::cerr <<“消息”“<< std::endl”。

更喜欢\n而不是std::endl;

出于性能原因,\n优于std::endl,特别是在期望有多个std::cout的循环中。std::endl调用一个系统例程来刷新输出缓冲区。调用系统函数意味着在执行系统功能时,程序将被替换掉。

代码语言:javascript
运行
复制
        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;
        }

被重构为

代码语言:javascript
运行
复制
        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;
        }

在结束时冲洗所有输出。

票数 1
EN
页面原文内容由Code Review提供。腾讯云小微IT领域专用引擎提供翻译支持
原文链接:

https://codereview.stackexchange.com/questions/230728

复制
相关文章

相似问题

领券
问题归档专栏文章快讯文章归档关键词归档开发者手册归档开发者手册 Section 归档