C++ gflags的使用及注意事项(附一个demo)

1 gflags的简介

gflags的全称(formerly Google Commandline Flags),开发者是Google。命令行参数中占有绝对的优势,开源使用很方便。网上有很多参考资料,大部分是基于官方的
How To Use gflags (formerly Google Commandline Flags)
http://gflags.github.io/gflags/#using
接下来从零开始写一个Demo出来。对于编译,不是很困难,最后在分享下,下来直接使用静态库即可,不用源码。

2 头文件及内置类型

2.1 头文件

其中gflags所有的头文件都放在gflags头文件下的,这是个good habit。
注意:虽然用lib方式调用,可以不用源码,但是头文件必须加上,否则无法编译通过。也就是任何一种调用形式(你可以使用lib,或者dll…),都需要有头文件。

#include <gflags/gflags.h>  

2.2 内置类型

现在gflags一共有7个类型变量。这一点非常重要,有且只有7个,分别是:bool、int32、uint32、int64、uint64、double、string,为此衍生的宏或者函数类型也只有7个。
其前缀都是DEFINE_,都是简单的数据类型,没有更复杂的类型,比如列表,官方也解释了,不赘述。

DEFINE_bool: boolean  
DEFINE_int32: 32-bit integer  
DEFINE_uint32: unsigned 32-bit integer
DEFINE_int64: 64-bit integer  
DEFINE_uint64: unsigned 64-bit integer  
DEFINE_double: double  
DEFINE_string: C++ string  

所有的类型变量都只有三个入口参数,分别是(name, val, txt)

1. name: flag的名字,比如-h、-f、--purge等,可以使用help来获得帮助,即“--help”
2. val: 默认参数值
3. txt: 对应提示信息

案例
下面使用DEFINE_string定义一个类型为string名字为d默认值是"CPU"txt为device_message
注意txt一般被定义成static const char*

DEFINE_string(d, "CPU", device_message);    // DEFINE_string(name, val, txt)  
static const char device_message[] = "Optional. Specify the target device for gflags_Demo. Default value is CPU. otherwise GPU";  

3 访问变量及参数合法性校验

很幸运,gflags拥有自己一整套的规范,处理这些问题就很简单了。

3.1 访问参数

2.2节定义的变量,都有前缀FLAGS_,正如上面的d其变量名是FLAGS_d,那么如何修改FLAGS_d的默认值"CPU"呢?下面是一种方案

DECLARE_string(d);              // 修改-d的默认值  
int main(int argc, char* argv[]){  
    FLAGS_d = "GPU";            // 定义的变量以FLAGS_作为前缀  
    gflags::ParseCommandLineNonHelpFlags(&argc, &argv, true);  
}  

解释:

  1. 可以看出,这里多了一个新的前缀DECLARE_,没错,这类前缀只有7个和extern含义差不多,这里是为了处理不同文件想要使用同一个变量而引入的外部声明。
  2. 同时修改默认值要在正式参数解析之前!!!,这里使用的是gflags::ParseCommandLineNonHelpFlags来处理,还有其他的哈

3.2 参数合法性校验

想想上面的,参数合法校验只有7种。类型是预设好了的,不要自创。
下面以一个案例开始,比如上面的d,我们提前限定只能取CPU或GPU,当然小写也可以,如出现其他情况发生异常报警。
需要DEFINE_validator(name, validator)宏,必须注意的是RegisterFlagValidator只有7种,已配对上面的基本类型。

#define DEFINE_validator(name, validator)

不难看出,

  1. name就是上面的d
  2. validator是自定义校验函数,只有7个
extern GFLAGS_DLL_DECL bool RegisterFlagValidator(const bool*        flag, bool (*validate_fn)(const char*, bool));
extern GFLAGS_DLL_DECL bool RegisterFlagValidator(const int32*       flag, bool (*validate_fn)(const char*, int32));
extern GFLAGS_DLL_DECL bool RegisterFlagValidator(const uint32*      flag, bool (*validate_fn)(const char*, uint32));
extern GFLAGS_DLL_DECL bool RegisterFlagValidator(const int64*       flag, bool (*validate_fn)(const char*, int64));
extern GFLAGS_DLL_DECL bool RegisterFlagValidator(const uint64*      flag, bool (*validate_fn)(const char*, uint64));
extern GFLAGS_DLL_DECL bool RegisterFlagValidator(const double*      flag, bool (*validate_fn)(const char*, double));
extern GFLAGS_DLL_DECL bool RegisterFlagValidator(const std::string* flag, bool (*validate_fn)(const char*, const std::string&));

在这里插入图片描述
这里我还是用上面的d演示

static bool ValidatePort(const char* flagname, const std::string& value) {
    std::string out;
    out.resize(value.size());
    transform(value.begin(), value.end(), out.begin(), ::toupper);
    std::cout << out << std::endl;
    if (out=="CPU" || out=="GPU")
        return true;
    else
        throw std::logic_error("Parameter -d must CPU or GPU");
}
DEFINE_validator(d, &ValidatePort);

可以看出,

  1. ValidatePort类型符合预设string类型
  2. DEFINE_validator表示将对参数d进行合法性校验ValidatePort
  3. 若出现其他参数,直接异常,需要main进行try-catch

C++ string大小写转换
https://blog.csdn.net/weixin_39956356/article/details/107558261

4 参数解析

这一步很简单,直接调用gflags::ParseCommandLineNonHelpFlags(&argc, &argv, true);即可,加上一些额外的处理,下面是一个模板。
main.cpp

#include "gflags_Demo.hpp"

#pragma comment(lib, "lib/Release/gflags_nothreads_static.lib")
//#pragma comment(lib, "lib/Debug/gflags_nothreads_static.lib")
#pragma comment(lib, "shlwapi.lib")


bool ParseAndCheckCommandLine(int argc, char* argv[]) {
    gflags::ParseCommandLineNonHelpFlags(&argc, &argv, true);
    if (FLAGS_h) {
        showUsage();
        return false;
    }

    // 由预设-i -m是必须参数,不给则异常,后面用try-catch捕获
    if (FLAGS_i.empty()) {
        throw std::logic_error("Parameter -i is not set");
    }
    if (FLAGS_m.empty()) {
        throw std::logic_error("Parameter -m is not set");
    }
    return true;
}
DECLARE_string(d);                  // 修改-d的默认值
int main(int argc, char* argv[])
{
    try {
        FLAGS_d = "GPU";            // 定义的变量以FLAGS_作为前缀
        if (!ParseAndCheckCommandLine(argc, argv)) {
            return EXIT_FAILURE;
        }
    }
    catch(const std::exception& error){
        std::cerr << "[ ERROR ] " << error.what() << std::endl;
        return EXIT_FAILURE;
    }
    catch (...) {
        std::cerr << "[ ERROR ] Unknown/internal exception happened." << std::endl;
        return EXIT_FAILURE;
    }
    return EXIT_SUCCESS;
}
  1. 务必要try-catch,分成两种,一种是已知异常,一种是未知异常
  2. 如果是-h,我们将输出所有的提示信息,即showUsage

下面是头文件,当然不止d一个参数,

gflags_Demo.hpp

#pragma once //ba

#include <gflags/gflags.h>
#include <iostream>
// tranform
#include <string>  
#include <algorithm>

static const char help_message[] = "Print a usage message.";
static const char video_message[] = "Required. Path to a video. Default value is \"cam\" to work with camera.";
static const char model_message[] = "Required. Path to the model file.";
static const char device_message[] = "Optional. Specify the target device for gflags_Demo. Default value is CPU. otherwise GPU";
static const char report_message[] = "Optional. performance report.";

DEFINE_bool(h, false, help_message);                        // DEFINE_bool(name, val, txt)
DEFINE_string(i, "cam", video_message);
DEFINE_string(m, "", model_message);
DEFINE_string(d, "CPU", device_message);
DEFINE_bool(pc, false, report_message);

static bool ValidatePort(const char* flagname, const std::string& value) {
    std::string out;
    out.resize(value.size());
    transform(value.begin(), value.end(), out.begin(), ::toupper);
    std::cout << out << std::endl;
    if (out=="CPU" || out=="GPU")
        return true;
    else
        throw std::logic_error("Parameter -d must CPU or GPU");
}
DEFINE_validator(d, &ValidatePort);


/**
* @brief This function shows a help message
*/
static void showUsage() {
    std::cout << std::endl;
    std::cout << "human_pose_estimation_demo [OPTION]" << std::endl;
    std::cout << "Options:" << std::endl;
    std::cout << std::endl;
    std::cout << "    -h                         " << help_message << std::endl;
    std::cout << "    -i \"<path>\"                " << video_message << std::endl;
    std::cout << "    -m \"<path>\"                " << model_message << std::endl;
    std::cout << "    -d \"<device>\"              " << device_message << std::endl;
    std::cout << "    -pc                        " << report_message << std::endl;
}

5 输出及总结

5.1 输出

在这里插入图片描述
这代码做了如下事情

  1. 合理给出帮助信息
  2. 参数校验
  3. 必要参数和可选参数的分配
  4. 参数默认值
  5. 参数没有值也会出错

5.2 总结

  1. gflags是很友好的命令行参数工具
  2. gflags只有7种内置类型,并派生对应的操作宏
  3. gflags以特有的前缀标识,比如DEFINE_FLAGS_DECLARE_

6 编译的注意事项

编译很简单,这里必须加上shlwapi.lib,否则会有一个链接报错

// 下面的方式和在工程中设置链接器是一样的!
#pragma comment(lib, "shlwapi.lib")

6.1 编译

首先,你要知道,你将使用

  1. x86 or x64?
  2. Debug or release?
    由于源码已经是Cmake工程。所以九十分简单了

gflags源码:
https://github.com/gflags/gflags
下面分享下windows的流程。linux直接下命令。
首先安装好cmake软件
在这里插入图片描述
在这里插入图片描述
Debug or release?自己选
在这里插入图片描述
在这里插入图片描述
linux

cmake -DCMAKE_BUILD_TYPE=Debug(Release) . && make -j 
  • 0
    点赞
  • 9
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值