目录
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);
}
解释:
- 可以看出,这里多了一个新的前缀
DECLARE_
,没错,这类前缀只有7个,和extern含义差不多
,这里是为了处理不同文件想要使用同一个变量而引入的外部声明。 - 同时
修改默认值要在正式参数解析之前!!!
,这里使用的是gflags::ParseCommandLineNonHelpFlags
来处理,还有其他的哈
3.2 参数合法性校验
想想上面的,参数合法校验只有7种
。类型是预设好了的,不要自创。
下面以一个案例开始,比如上面的d,我们提前限定只能取CPU或GPU,当然小写也可以,如出现其他情况发生异常报警。
需要DEFINE_validator(name, validator)
宏,必须注意的是RegisterFlagValidator
只有7种,已配对上面的基本类型。
#define DEFINE_validator(name, validator)
不难看出,
- name就是上面的d
- 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);
可以看出,
- ValidatePort类型符合预设string类型
DEFINE_validator
表示将对参数d进行合法性校验ValidatePort- 若出现其他参数,直接异常,需要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;
}
- 务必要try-catch,分成两种,一种是
已知异常
,一种是未知异常
- 如果是
-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 输出
这代码做了如下事情
- 合理给出帮助信息
- 参数校验
- 必要参数和可选参数的分配
- 参数默认值
- 参数没有值也会出错
5.2 总结
- gflags是很友好的命令行参数工具
- gflags只有7种内置类型,并派生对应的操作宏
- gflags以特有的前缀标识,比如
DEFINE_
、FLAGS_
、DECLARE_
6 编译的注意事项
编译很简单,这里必须加上shlwapi.lib
,否则会有一个链接报错
// 下面的方式和在工程中设置链接器是一样的!
#pragma comment(lib, "shlwapi.lib")
6.1 编译
首先,你要知道,你将使用
- x86 or x64?
- 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