管理命令行参数

Managing command line arguments

本文关键字:参数 命令行 管理      更新时间:2023-10-16

正在更新一些旧代码,原作者决定所有的命令行参数变量都应该是全局变量。从测试和开发的角度来看,这显然使事情更具挑战性。

我的问题是如何最好地管理所有类需要使用的命令行参数(例如跟踪标志/调试标志)。一位同事建议至少将变量包装在名称空间中,但这似乎还不够。我考虑过单例或静态类,只提供getter,但这似乎不是很优雅。另一方面,这似乎比必须传递5个配置选项给每个需要知道是否调试和少数其他选项设置的类要好。

全局变量的最大问题是,在函数内部更改它们往往会产生意想不到的副作用,从而引入bug。然而,在命令行参数的情况下,就正在运行的进程而言,它们本质上是常量。唯一阻止您声明它们的是const,它们需要在您开始解析命令行时分配。

我建议创建一些机制,允许您在开始时初始化参数,但随后防止程序的任何部分更改它们。这将有效地避免全局变量通常会引入的任何缺点。

一种方法可能是ProgramArguments类/结构,其const成员在构造函数中通过解析命令行初始化。你可以这样写:

std::unique_ptr<ProgramArguments const> g_program_arguments;
int main(int argc, char* argv[])
{
    g_program_arguments.reset(new ProgramArguments(argc, argv));
    if(g_program_arguments->verbose)
        std::cout << "verbose!" << std::endl;
    // ...
    return 0;
}

这不会阻止你改变指针指向不同的ProgramArguments实例。另一种方法可能是为了初始化目的暂时抛弃constness:

struct ProgramArguments {
    ProgramArguments() {}
    bool verbose;
};
ProgramArguments const g_program_arguments;
void init_program_arguments(int argc, char* argv[])
{    
    ProgramArguments& program_arguments = const_cast<ProgramArguments&>(g_program_arguments);
    program_arguments.verbose = true;
}
int main(int argc, char* argv[])
{
    init_program_arguments(argc, argv);
    if(g_program_arguments.verbose)
        std::cout << "verbose!" << std::endl;
    return 0;
}

这取决于我们正在讨论的全局变量的数量。就我个人而言,我认为有一些全局变量是很好的,比如调试标志和单例日志管理器。

如果你真的想遵守书本上的OOP原则,那么你就必须把函数或对象需要的所有东西作为参数传递。从不访问全局状态。正如您所提到的,向每个函数传递大量公共参数很快就会变得无聊,因此有一种模式可以帮助您缓解这种情况,即上下文对象。