C和C++语言应用程序解析命令行参数

一、前言

        对于简单的C/C++应用程序,一般只需要根据 main 函数的 argc 和 argv 参数列表进行传值。但是这种方式较为固化,每次都需要传入所有参数,并且参数间的相对关系也不能更改。

        虽然可以使用 shell 脚本对以上方式进行优化,不过在应用程序中进行集成可变的参数解析,对后续的开发也能起到很好的铺垫作用。

        本文使用 getopt 库进行参数解析,实现 Linux 常见应用中的命令行解析方式,即 -h 和 --help 形式。样例我分为 C 的版本和 C++ 的版本,C++ 使用面向对象进行了优化,整体的逻辑是一样的,都是使用 getopt 方式进行解析。C++ 好像使用 Boost.Program_options 的较多,读者可以自行设计,我这里是先做了 C 的版本,然后更迭出 C++ 的版本。

二、参考样例

1、C版本

        这里的源文件来自我 oled 小屏幕的应用程序,实现了引脚,页码,间隔时间等参数的解析,带有帮助显示,支持长命令和短命令混合使用。

parse_config.c:

#include <stdio.h>
#include <stdlib.h>
#include <getopt.h>

#include "parse_config.h"

/**
 * @Description: 显示帮助信息
 * @param {char} *program_name: 程序名称
 * @return {*}
 */
void print_help(const char *program_name) {
    printf("Usage: %s [options]\n", program_name);
    printf("Options:\n");
    printf("  -o, --oled_pins <scl,mosi,res,dc> Set oled pin number\n");
    printf("  -p, --page <number>               Set display page (1, 2, or 3)\n");
    printf("  -i, --interval <seconds>          Set update interval (default: 1)\n");
    printf("  -t, --text <string>               Set display text\n");
    printf("  -v, --verbose                     Enable verbose output\n");
    printf("  -h, --help                        Show this help message\n");
}

/**
 * @Description: 打印配置信息
 * @param {AppConfig} config: 配置信息
 * @return {*}
 */
void print_config(AppConfig config) {
    printf(" **************************\n");
    printf("Parse Information:\n");
    printf("    Display page: %d\n", config.page);
    printf("    Update Interval: %d millisecond(ms)\n", config.interval);
    printf("    Display Text: %s\n", config.text);
    printf("    GPIOs: %s\n", config.oled_pins);
}

/*
 * @description : 解析命令行参数
 * @param : 无
 * @return : 无
 */
AppConfig parse_arguments(int argc, char *argv[]) {
    AppConfig config = {
        .oled_pins = NULL,  // 默认为空字符串,提示用户传入
        .page = 1,          // 默认显示风格
        .interval = 1000,   // 默认更新间隔
        .text = "SPI OLED", // 默认显示文本
        .verbose = 0        // 默认关闭详细信息
    };

    /* 定义长选项 */ 
    struct option long_options[] = {
        {"oled_pins", required_argument, 0, 'o'},
        {"page",      required_argument, 0, 'p'},
        {"interval",  required_argument, 0, 'i'},
        {"text",      required_argument, 0, 't'},
        {"verbose",   no_argument,       0, 'v'},
        {"help",      no_argument,       0, 'h'},
        {0, 0, 0, 0}
    };

    /* 解析参数 */ 
    int opt;
    // 支持短选项和长选项
    // : 表示该选项需要一个参数,v 和 h 不需要
    // 如果解析到长选项,返回 val 字段的值(即第四列)
    while ((opt = getopt_long(argc, argv, "o:p:i:t:vh", long_options, NULL)) != -1) {
        switch (opt) {
            case 'o':
                config.oled_pins = optarg;
                break;
            case 'p':
                config.page = atoi(optarg);
                if (config.page < 1 || config.page > 3) {
                    fprintf(stderr, "Invalid page number. Use 1, 2, or 3.\n");
                    exit(EXIT_FAILURE);
                }
                break;
            case 'i':
                config.interval = atoi(optarg);
                if (config.interval <= 0) {
                    fprintf(stderr, "Interval must be a positive number.\n");
                    exit(EXIT_FAILURE);
                }
                break;
            case 't':
                config.text = optarg;
                break;
            case 'v':
                config.verbose = 1;
                break;
            case 'h':
                print_help(argv[0]);
                exit(EXIT_SUCCESS);
            default:
                print_help(argv[0]);
                exit(EXIT_FAILURE);
        }
    }

    return config;
}

parse_config.h:

#ifndef _PARSE_CONFIG_H_
#define _PARSE_CONFIG_H_


/* 定义命令行参数结构体 */ 
typedef struct {
    char *oled_pins; // 控制引脚
    int page;        // 显示主页
    int interval;    // 更新间隔(ms毫秒)
    char *text;      // 显示文本
    int verbose;     // 是否显示详细信息
} AppConfig;


void print_help(const char *program_name);
AppConfig parse_arguments(int argc, char *argv[]);
void print_config(AppConfig config);

#endif

2、C++ 版本

        这里的源文件来自另一个 YOLO 加速的项目,主要实现模型路径,输入数据,线程数等参数的解析。这里也同样带有帮助显示,支持长命令和短命令混合使用。

parse_config.cpp:

#include <iostream>
#include <cstdlib>
#include <getopt.h>

#include "parse_config.hpp"


/**
 * @Description: 显示帮助信息
 * @param {char} *program_name: 程序名称
 * @return {*}
 */
void ConfigParser::print_help(const string &program_name) const { // 常量成员函数承诺不会修改调用它的对象的任何成员变量(函数内只有只读操作)
    cout << "Usage: " << program_name << " [options]" << endl;
    cout << "Options:" << endl;
    cout << "  -m, --model_path <string, require> || Set rknn model path. need to be set" << endl;
    cout << "  -i, --input <int or string, require> || Set input source. int: Camera index, like 0; String: video path. need to be set" << endl;
    cout << "  -a, --hwaccels <int> || Configure the hardware acceleration mode. 1:letterbox, 2:RGA. default: 2" << endl;
    cout << "  -t, --threads <string> || Set threads number. default: 1" << endl;
    cout << "  -c, --opencl <bool or int> || Configure the opencl mode. true(1):use opencl, fals(0):use cpu. default: True(1)" << endl;
    cout << "  -v, --verbose || Enable verbose output" << endl;
    cout << "  -h, --help || Show this help message" << endl;
}

/**
 * @Description: 打印配置信息
 * @param {AppConfig} config: 配置信息
 * @return {*}
 */
// 打印配置信息
void ConfigParser::printConfig(const AppConfig &config) const {
    cout << "​*************************" << endl;
    cout << "Parse Information:" << endl;
    cout << "    Model path: " << config.model_path << endl;
    cout << "    Input source: " << config.input << endl;
    cout << "    Hwaccels: " << config.hwaccels << endl;
    cout << "    Threads: " << config.threads << endl;
    cout << "    Opencl: " << boolalpha << config.opencl << endl; // boolalpha: 将 bool 类型以 true/false 形式输出
}

/**
 * @Description: 解析命令行参数
 * @param {int} argc: 命令行参数个数
 * @param {char} *argv: 命令行参数数组
 * @return {*}
 */
AppConfig ConfigParser::parse_arguments(int argc, char *argv[]) const {
    if (argc < 2) {
        this->print_help(argv[0]);
        exit(EXIT_FAILURE);
    }
    AppConfig config;


    /* 定义长选项 */ 
    struct option long_options[] = {
        {"model_path", required_argument, nullptr, 'm'},
        {"input",      required_argument, nullptr, 'i'},
        {"hwaccels",   required_argument, nullptr, 'a'},
        {"threads",    required_argument, nullptr, 't'},
        {"opencl",     required_argument, nullptr, 'c'},
        {"verbose",    no_argument,       nullptr, 'v'},
        {"help",       no_argument,       nullptr, 'h'},
        {nullptr,      0,                 nullptr, 0}
    };

    /* 解析参数 */ 
    int opt;
    // 支持短选项和长选项
    // : 表示该选项需要一个参数,v 和 h 不需要
    // 如果解析到长选项,返回 val 字段的值(即第四列)
    while ((opt = getopt_long(argc, argv, "m:i:a:t:c:vh", long_options, nullptr)) != -1) {
        switch (opt) {
            case 'm':
                config.model_path = optarg;
                break;
            case 'i':
                config.input = optarg;
                break;
            case 'a':{
                try {
                    config.hwaccels = std::stoi(optarg);
                    if (config.hwaccels != LETTERBOX && config.hwaccels != RGA) 
                        throw std::invalid_argument("Unsupported hwaccel type.");
                } catch (const std::exception &e) {
                    exit(EXIT_FAILURE);
                }
                break;
            }
            case 't':
                config.threads = std::stoi(optarg);
                break;
            case 'c': {
                if (optarg == "true" || optarg == "1")
                    config.opencl = true;
                 else if (optarg == "false" || optarg == "0") 
                    config.opencl = false;
            }
            case 'v':
                config.verbose = true;
                break;
            case 'h':
                this->print_help(argv[0]);
                exit(EXIT_SUCCESS);
            default:
                this->print_help(argv[0]);
                exit(EXIT_FAILURE);
        }
    }
    if (config.verbose)
        this->printConfig(config);

    return config;
}

parse_config.hpp:

#ifndef _PARSE_CONFIG_HPP_
#define _PARSE_CONFIG_HPP_

#include <iostream>
#include <string>
using namespace std;

enum hwaccels {
    LETTERBOX = 1,
    RGA = 2,
};

/* 定义命令行参数结构体 */ 
struct AppConfig {
    std::string model_path = "";  // rknn 模型路径
    std::string input = "";       // 输入源
    int hwaccels = RGA;           // 硬件加速,默认为 RGA
    int threads = 1;              // 线程数,默认为1
    bool opencl = true;           // 是否使用opencl
    bool verbose = false;         // 是否打印命令行参数
};

/* 定义配置解析类 */
class ConfigParser {
    public:
        // 显示帮助信息
        void print_help(const string &program_name) const;
        // 打印配置信息
        void printConfig(const AppConfig &config) const;
        // 解析命令行参数
        AppConfig parse_arguments(int argc, char *argv[]) const;

    private:
        // 私有成员(如果有需要可以添加)
    };

#endif

三、总结

        实现效果:

         源码我会放至 GitHub 和 Gitee,方便克隆使用。

1125962926/argument_parsing: Command line argument parsinghttps://github.com/1125962926/argument_parsing
argument_parsing: Command line argument parsinghttps://gitee.com/lrf1125962926/argument_parsing


项目首页 - argument_parsing:Command line argument parsing - GitCodehttps://gitcode.com/plmm__/argument_parsing

评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

plmm烟酒僧

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值