1. 程式人生 > >用Google的gflags優雅的解析命令列引數(一)

用Google的gflags優雅的解析命令列引數(一)

寫了這麼多年的Linux下C/C++程式碼,一直使用getopt_long來解析命令列引數,同時定義一個全域性的struct來儲存各個命令列引數的值。雖然用得比較“繁瑣”,但也安於現狀。最近突然發現了Google早在多年前就開源了一個解析命令列引數的“神器”gflags。趕緊來爽一把。

  安裝

1、去官網下載一個最新的版本(gflags-2.1.1.tar.gz)。

2、現在流行cmake的構建方式,gflags的最新版本也改為使用cmake了。還好我最近也剛剛學習了cmake,算是跟上了潮流。有興趣的話,可以看 《讓cmake顯示gcc/g++的編譯資訊》
1 2 3 4 5 6 7 [amcool@leooxsoft]$tar xzvf gflags-2.1.1.tar.gz [amcool@leooxsoft]$cd gflags-2.1.1 [amcool@leooxgflags-2.1.1]$mkdir build [amcool@leooxgflags-2.1.1]$cd build/ [amcool@leooxbuild]$cmake..-DCMAKE_INSTALL_PREFIX=/home/amcool/local/gflags-2.1.1 [amcool@leooxbuild]$make [amcool@leooxbuild
]$make install

就是這麼簡單,安裝成功了。值得注意的是,我這裡新建了一個build資料夾,即採用“外部構建”的方式。這樣編譯過程中產生的中間檔案(比如.o檔案)就都放在build裡,不會“汙染”gflags原始碼,做到乾乾淨淨。

  爽一把

1、既然安裝好了,那趕緊來寫個簡單的程式碼來爽一把。話不多說,程式碼才是王道!

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 // demo.cpp #include <iostream> #include <gflags/gflags.h> usingnamespacestd; DEFINE_string(confPath,"../conf/setup.ini","program configure file."); DEFINE_int32(port,9090,"program listen port"); DEFINE_bool(daemon,true,"run daemon mode"); intmain(intargc,char**argv) { gflags::ParseCommandLineFlags(&argc,&argv,true); cout<<"confPath = "<<FLAGS_confPath<<endl; cout<<"port = "<<FLAGS_port<<endl; if(FLAGS_daemon){ cout<<"run background ..."<<endl; } else{ cout<<"run foreground ..."<<endl; } cout<<"good luck and good bye!"<<endl; gflags::ShutDownCommandLineFlags(); return0; }

2、很明顯,接下來就是要編譯了。這裡直接用g++寫一行命令就可以編譯了。但是既然學了cmake,那就“大材小用”一次吧。

1 2 3 4 5 6 7 8 9 project(demo) cmake_minimum_required(VERSION2.8) set(CMAKE_VERBOSE_MAKEFILEon) include_directories("/home/amcool/local/gflags-2.1.1/include") link_directories("/home/amcool/local/gflags-2.1.1/lib") add_executable(demodemo.cpp) target_link_libraries(demogflags pthread)

3、那當然就是編譯了

1 2 3 4 5 6 7 8 9 10 11 [amcool@leooxdemo]$ls CMakeLists.txt  demo.cpp [amcool@leooxdemo]$mkdir build [amcool@leooxdemo]$cd build [amcool@leooxbuild]$cmake.. [amcool@leooxbuild]$ls CMakeCache.txt  CMakeFiles  cmake_install.cmake  Makefile [amcool@leooxbuild]$make [amcool@leooxbuild]$ls CMakeCache.txt  CMakeFiles  cmake_install.cmake  demo  Makefile [amcool@leooxbuild]$

  設定命令列引數

1、直接執行,得到的就是我們設定的預設引數。(聰明的你,結合程式碼一看,就知道引數的預設值是什麼了)

1 2 3 4 5 [amcool@leooxbuild]$./demo confPath=../conf/setup.ini port=9090 run background... goodluck andgood bye!

2、設定引數值

i)可以用 –引數名=引數值 或者-引數名=引數值 的方式來設定引數值。

ii)對於bool型別的引數,除了上述方式外,還可以用 –引數名 的方式設定為true(即不帶值), 使用–no引數名 的方式設定為false。為了統一,我建議都使用 上面的 第 i)種方法來設定引數。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 [amcool@leooxbuild]$./demo--port=8888--confPath=./setup.ini--daemon=true confPath=./setup.ini port=8888 run background... goodluck andgood bye! [amcool@leooxbuild]$./demo-port=8888-confPath=./setup.ini-daemon=false confPath=./setup.ini port=8888 runforeground... good luck andgood bye! [amcool@leooxbuild]$./demo-port=8888-confPath=./setup.ini-daemon confPath=./setup.ini port=8888 run background... goodluck andgood bye! [amcool@leooxbuild]$./demo-port=8888-confPath=./setup.ini-nodaemon confPath=./setup.ini port=8888 runforeground... good luck andgood bye! [amcool@leooxbuild]$

3、從檔案讀入“命令列”引數

如果我們的程式比較牛逼,配置項非常多,也就是說命令列引數很多,那你每次啟動都要一個一個的輸入,那豈不是很麻煩?gflags已經幫我們解決了,用 –flagfile=命令列檔案 的方式就可以了。你接著往下看,就明白了。param.cmd就是上面說的命令列檔案

1 2 3 4 5 6 7 8 9 10 [amcool@leooxbuild]$vi param.cmd --port=8888 --confPath=./setup.ini --daemon=true [amcool@leooxbuild]$./demo--flagfile=param.cmd confPath=./setup.ini port=8888 run background... goodluck andgood bye! [amcool@leooxbuild]$

怎麼樣,這樣就不怕引數配置錯誤了吧。儲存到檔案,每次啟動,就很輕鬆了。

4、從環境變數讀入引數值

gflags另外還給我們提供了 –fromenv 和 –tryfromenv 引數,通過這兩個引數,我們的程式可以從環境變數中獲取到具體的值。兩者有什麼不一樣呢。你看到他們的區別僅僅是有無“try”,聰明的你一定猜到了。

  • –fromenv 從環境變數讀取引數值 –fromenv=port,confPath 表明要從環境變數讀取port,confPath兩個引數的值。但是當無法從環境變數中獲取到的時候,會報錯,同時程式退出。【注意:gflags的變數名是 FLAGS_我們定義的引數名,開篇的程式碼裡,估計細心的你已經發現了】
  • –tryfromenv 與–fromenv類似,當引數的沒有在環境變數定義時,不退出

也來一個例子,一看便明瞭。

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 [amcool@leooxbuild]$./demo--fromenv=port,confPath ERROR:FLAGS_confPath notfound inenvironment ERROR:FLAGS_port notfound inenvironment [amcool@leooxbuild]$./demo--tryfromenv=port,confPath confPath=../conf/setup.ini port=9090 runbackground... good luck andgood bye! [amcool@leooxbuild]$exportFLAGS_confPath=./loveyou.ini [amcool@leooxbuild]$exportFLAGS_port=36888 [amcool@leooxbuild]$env|grep FLAGS FLAGS_port=36888 FLAGS_confPath=./loveyou.ini [amcool@leooxbuild]$ [amcool@leooxbuild]$./demo--fromenv=port,confPath     confPath=./loveyou.ini port=36888 run background... goodluck andgood bye! [amcool@leooxbuild]$

  版本號和幫助資訊

我們一般使用程式的時候,都離不開兩個引數 –version 和 –help。來看看上面實現的demo能否支援呢?

1 2 3 4 5 6 7 8 9 10 [amcool@leooxbuild]$./demo--version demo [amcool@leooxbuild]$./demo--help demo:Warning:SetUsageMessage()never called Flags from/home/thrift/program/gflags/demo/demo.cpp: -confPath(program configurefile.)type:string default:"../conf/setup.ini" -daemon(run daemonmode)type:booldefault:true -port(program listenport)type:int32 default:9090

哈,help支援了,但是version沒支援,而且help資訊裡面還有waring。沒關係,我們可以用  SetVersionString() 和 SetUsageMessage() 方法來滿足需求。修改後的程式碼如下:

【注意:SetVersionString() 和 SetUsageMessage() 一定要在 ParseCommandLineFlags() 之前設定。】

1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 #include <iostream> #include <gflags/gflags.h> usingnamespacestd; DEFINE_string(confPath,"../conf/setup.ini","program configure file."); DEFINE_int32(port,9090,"program listen port"); DEFINE_bool(daemon,true,"run daemon mode"); intmain(intargc,char**argv) { gflags::SetVersionString("1.0.0.0"); gflags::SetUsageMessage("Usage : ./demo ")