参考资料:
https://www.cnblogs.com/chenliyang/p/6633739.html
https://www.cnblogs.com/qingergege/p/5914218.html
https://blog.csdn.net/huangxiaohu_coder/article/details/7475156
三则参考资料已经将getopt、getopt_long和getopt_long_only的用法写的非常详尽,有珠玉在前,我也不打算再多讲用法了。这篇文章主要讲解一下如何在windows上使用getopt,以及分析一个比较实用的使用案例。
我们知道getopt和getopt_long主要还是用在Linux操作系统中,在windows上如果你使用#include <getopt.h>是会报错的,所以有人已经将源码迁移修改好供我们使用:https://github.com/Chunde/getopt-for-windows,只要在项目中手动加入源文件即可!
下面是案例部分,主要探讨一个程序的规范性写法(人性化!),希望对读者有所启发:
1. 首先,我们定义一个函数,用来打印程序的使用方法:
void usage(const char* progname) {
printf("Usage: %s [options] scenename\n", progname);
printf("Valid scenenames are: rgb, rgby, rand10k, rand100k, biglittle, littlebig, pattern,\n"
" bouncingballs, fireworks, hypnosis, snow, snowsingle\n");
printf("Program Options:\n");
printf(" -r --renderer <cpuref/cuda> Select renderer: ref or cuda (default=cuda)\n");
printf(" -s --size <INT> Rendered image size: <INT>x<INT> pixels (default=%d)\n", DEFAULT_IMAGE_SIZE);
printf(" -b --bench <START:END> Run for frames [START,END) (default=[0,1))\n");
printf(" -c --check Check correctness of CUDA output against CPU reference\n");
printf(" -i --interactive Render output to interactive display\n");
printf(" -f --file <FILENAME> Output file name (FILENAME_xxxx.ppm) (default=output)\n");
printf(" -? --help This message\n");
}
注意这个函数的参数是progname,联想到argv[0]不就正是程序的全名吗?用的时候传递argv[0]即可。
再来看这个usage的写法,它打印了短选项和等价的长选项的用法!最后输出的结果如下:

2. 然后我们定义一个结构体,在后面这个结构体会被getopt_long()解析(注意常用的是getopt_long,因为它同时支持长短选项)。
int opt;
static struct option long_options[] = {
{"help", 0, 0, '?'},
{"check", 0, 0, 'c'},
{"bench", 1, 0, 'b'},
{"interactive", 0, 0, 'i'},
{"file", 1, 0, 'f'},
{"renderer", 1, 0, 'r'},
{"size", 1, 0, 's'},
{0 ,0, 0, 0}
};
为什么这个结构长这个样子?其中一则参考资料给出了解释,记录如下:
struct option {
const char *name; /* 参数名称 */
int has_arg; /* 指明是否带有参数 */
int *flag; /* flag=NULL时,返回value;不为空时,*flag=val,返回0 */
int val; /* 用于指定函数找到选项的返回值或flag非空时指定*flag的值 */
};
has_arg 指明是否带参数值,其数值可选:
no_argument 表明长选项不带参数,如:--name, --help
required_argument 表明长选项必须带参数,如:--prefix /root或 --prefix=/root
optional_argument 表明长选项的参数是可选的,如:--help或 –prefix=/root,其它都是错误
所以最终这个结构体可能会长这个样子(与usage对应):
static struct option long_options[] = {
{"help", 0, 0, '?'},
{"check", 0, 0, 'c'},
{"bench", 1, 0, 'b'},
{"interactive", 0, 0, 'i'},
{"file", 1, 0, 'f'},
{"renderer", 1, 0, 'r'},
{"size", 1, 0, 's'},
{0 ,0, 0, 0}
};
或者这个样子(用到了预定义的参数):
static struct option long_options[] =
{
{"reqarg", required_argument,NULL, 'r'},
{"optarg", optional_argument,NULL, 'o'},
{"noarg", no_argument, NULL,'n'},
{NULL, 0, NULL, 0},
};
3. 万事俱备了,直接进入重头戏吧,即调用getopt_long
while ((opt = getopt_long(argc, argv, "b:f:r:s:ci?", long_options, NULL)) != EOF) {
switch (opt) {
case 'b':
if (sscanf(optarg, "%d:%d", &benchmarkFrameStart, &benchmarkFrameEnd) != 2) {
fprintf(stderr, "Invalid argument to -b option\n");
usage(argv[0]);
exit(1);
}
break;
case 'i':
interactiveMode = true;
break;
case 'c':
checkCorrectness = true;
break;
case 'f':
frameFilename = optarg;
break;
case 'r':
if (std::string(optarg).compare("cuda") == 0) {
useRefRenderer = false;
}
else if (std::string(optarg).compare("cpuref") == 0) {
useRefRenderer = true;
}
else {
fprintf(stderr, "ERROR: Unknown renderer type: %s\n", optarg);
usage(argv[0]);
return 1;
}
break;
case 's':
imageSize = atoi(optarg);
break;
case '?':
default:
usage(argv[0]);
return 1;
}
}
从循环中的switch我们看出,这个函数的奥秘就在于:“使用字符串和结构体定义规则,使用规则进行分叉”!具体的用法参看参考资料,这里只介绍这种好的写法。相信这个例子胜过一大段解释!