コマンドライン引数解析関数を生成する gperf
プロファイラかと思ってたら全然違ってたのでメモ。
getopt するよりシンプルでいいな。
gperf を使って効率的に C/C++ コマンド・ラインを処理する
こんなファイルを用意する。
diff.gperf
%{ typedef struct CommandOption_ CommandOption; enum CommandOptionCode { IGNORE_CASE, IGNORE_FILE_NAME_CASE, IGNORE_TAB_EXPANSION, IGNORE_SPACE_CHANGE, IGNORE_ALL_SPACE, IGNORE_BLANK_LINES, }; %} struct CommandOption_ { const char *name; int code; }; %% -i,IGNORE_CASE --ignore-case,IGNORE_CASE --ignore-file-name-case,IGNORE_FILE_NAME_CASE -E,IGNORE_FILE_NAME_CASE --ignore-tab-expansion,IGNORE_TAB_EXPANSION -b,IGNORE_SPACE_CHANGE --ignore-space-change,IGNORE_SPACE_CHANGE -w,IGNORE_ALL_SPACE --ignore-all-space,IGNORE_ALL_SPACE -B,IGNORE_BLANK_LINES --ignore-blank-lines,IGNORE_BLANK_LINES
diff.c
#include <stdio.h> #include <string.h> #include "diff_option.c" int main(int argc, char* argv[]) { if (argc == 1) { puts("options:"); for (int i = 0; i < TOTAL_KEYWORDS; ++i) { printf("\t%s\n", wordlist[i].name); } return 1; } for (int i = 1; i < argc; ++i) { const CommandOption *option = in_word_set(argv[i], strlen(argv[i])); switch (option->code) { case IGNORE_CASE: puts("Ignore case differences in file contents."); break; case IGNORE_FILE_NAME_CASE: puts("Ignore case when comparing file names."); break; case IGNORE_TAB_EXPANSION: puts("Consider case when comparing file names."); break; case IGNORE_SPACE_CHANGE: puts("Ignore changes in the amount of white space."); break; case IGNORE_ALL_SPACE: puts("Ignore all white space."); break; case IGNORE_BLANK_LINES: puts("Ignore changes whose lines are all blank."); break; } } return 0; }
作る
$ gperf -CGD -LANSI-C -t diff.gperf > diff_option.c $ gcc -std=c99 -Wall diff.c -o diff $ ./diff options: -w -B -i -b --ignore-case -E --ignore-all-space --ignore-blank-lines --ignore-space-change --ignore-tab-expansion --ignore-file-name-case $ ./diff -w Ignore all white space.