使用LLVM中的命令行参数处理接口

Page content

LLVM中提供了一组便捷好用的命令行参数处理接口,本文针对不同的场景介绍如何使用它们。

头文件llvm/Support/CommandLine.h声明了这些接口,这些接口在命名空间llvmcl里面。因此,使用它们时,代码像下面这个样子。

#include "llvm/Support/CommandLine.h"

using namespace llvm;

int main(int argc, char **argv) {
  cl::ParseCommandLineOptions(argc, argv);
}

上面的代码中已经使用了接口cl::ParseCommandLineOptions(argc, argv),它的作用是读取并解析用户通过命令行传入的参数,检测其是否符合代码指定的参数规范,并将传入的参数赋值给代码定义的变量。上面的代码还没有定义一些参数,你需要像声明全局变量那样声明参数。下面通过模仿Linux中的ls命令来分享如何使用这些接口。

实现 ls /home

我们可以通过使用ls /home来列出/home目录下所有的文件和文件夹。你可以这样使用cl里的接口来模拟这种单个字符串参数:

#include "llvm/Support/CommandLine.h"

using namespace llvm;

cl::opt<std::string> DirName(cl::Positional, cl::desc("<dir>"));

int main(int argc, char **argv) {
  cl::ParseCommandLineOptions(argc, argv);
  errs() << DirName.c_str() << "\n";
}

其中DirName是一个string型参数,可以被转换为string,你也可以指定为其它类型。在声明该参数时,cl::Positional表明该参数是一个位置型参数,cl::desc 和为程序的--help选项提供信息。假设生成的程序为main,则交互情况如下:

-> ./main /home
/home

-> ./main --help
USAGE: main [options] <dir>
......

在声明DirName时里面的像cl::Positional这样的参数之间是没有顺序要求的。对于上述程序,如果不指定一个参数,会生成字符串型的默认构造函数的生成值。你也可以在声明时通过cl::init("you set a value")来指定一个默认值,或者通过cl::Required强制用户输入参数。

实现 ls /home /etc

上面的代码只能接受一个位置型字符串,ls是可以接受多个文件夹作为参数的。这时,就要用cl::list<>代替cl::opt了。

#include "llvm/Support/CommandLine.h"

using namespace llvm;

cl::list<std::string> DirName(cl::Positional, cl::desc("<dirs>"), cl::OneOrMore);

int main(int argc, char **argv) {
  cl::ParseCommandLineOptions(argc, argv);
  for (auto S : DirName) {
    errs() << S << "\n";
  }
}

我们通过cl::list<std::string>指定了一个接受多个字符串的参数。其中cl::OneMore强制用户输入至少一个参数。在main函数中,可以像使用vector<std::string>一样使用DirName

实现 ls -a

用户还可以通过ls -a来显示隐藏的文件与文件夹。我们额外声明一个bool型的a参数,代码如下:

#include "llvm/Support/CommandLine.h"

using namespace llvm;

cl::opt<bool> Hidden("a", cl::desc("Show hidden entries"), cl::init(false));
cl::list<std::string> DirName(cl::Positional, cl::desc("<dirs>"), cl::OneOrMore);

int main(int argc, char **argv) {
  cl::ParseCommandLineOptions(argc, argv);
  errs() << "Show hidden: " << Hidden << "\n";
  for (auto string : DirName) {
    errs() << string << "\n";
  }
}

用户可以通过-a=false-a=FALSE-a=0来将变量Hidden设置为false,或者通过-a-a=1-a=true-a=TRUE来将其设置为true。

实现 ls –hide=XX

通过ls --hide=<PATTERN>还可以设置不显示的文件与文件夹。于是我们再用cl::opt<std::string>定义一个这样的参数hide,如下:

#include "llvm/Support/CommandLine.h"

using namespace llvm;

cl::opt<bool> Hidden("a", cl::desc("Show hidden entries"), cl::init(false));
cl::opt<std::string> Filter("hide", cl::desc("Filter some pattern"), cl::Required);
cl::list<std::string> DirName(cl::Positional, cl::desc("<dirs>"), cl::OneOrMore);

int main(int argc, char **argv) {
  cl::ParseCommandLineOptions(argc, argv);
  errs() << "Filter output: " << Filter << "\n";
  errs() << "Show hidden: " << Hidden << "\n";
  for (auto string : DirName) {
    errs() << string << "\n";
  }
}

与命令行交互如下:

-> ./main -a -hide=*hide* /home /opt
Filter output: *hide*
Show hidden: 1
/home
/opt

-> ./main --help
USAGE: main [options] <dirs>
......
General options:

  -a              - Show hidden entries
  --hide=<string> - Filter some pattern    

除了上面介绍的,LLVM还包含很多灵活的接口,详见文档https://llvm.org/docs/CommandLine.html#quick-start-guide。