1. 程式人生 > 程式設計 >C++命令列解析包gflags的使用教程

C++命令列解析包gflags的使用教程

前言

gflags 是 Google 提供的一個命令列引數處理的開源庫,目前已經獨立開源,比傳統的 getopt() 功能更加強大,可以將不同的引數定義分佈到各個原始碼檔案中,不需要集中管理。

提供了 C++ 和 Python 兩個版本,這裡僅詳細介紹 C++ 版本的使用方式。

簡介

配置引數分開還是集中管理沒有嚴格的約束,關鍵要看專案裡的統一規範,只是,gflags 可以支援這兩種方式,允許使用者更加靈活的使用。

當將引數分佈到各個原始碼檔案中時,如果定義了相同的引數,那麼在編譯的時候會直接報錯。

安裝

很多發行版本會有自己相關的開發庫,這裡簡單介紹使用 CMake 從原始碼進行編譯,原始碼可以從 GitHub gflags Releases 中選擇相關的版本。

如下命令以最新的 2.2.2 版本為例。

$ tar xzf gflags-2.2.2.tar.gz
$ cd gflags-2.2.2
$ mkdir build && cd build
$ cmake -DCMAKE_INSTALL_PREFIX=/usr ..
$ make
$ make test # 單元測試,執行cmake時需要增加-DBUILD_TESTING=true引數
# make install # 安裝,一般需要root使用者執行

預設會安裝到 /usr/local 目錄下,需要配置動態庫、標頭檔案路徑等,通過上述的 -DCMAKE_INSTALL_PREFIX=/usr 引數修改該路徑,使用系統預設路徑,此時會安裝如下的檔案。

/usr/lib/libgflags.a
/usr/lib/libgflags_nothreads.a
/usr/include/gflags/gflags.h
/usr/include/gflags/gflags_declare.h
/usr/include/gflags/gflags_completions.h
/usr/include/gflags/gflags_gflags.h
/usr/lib/cmake/gflags/gflags-config.cmake
/usr/lib/cmake/gflags/gflags-config-version.cmake
/usr/lib/cmake/gflags/gflags-targets.cmake
/usr/lib/cmake/gflags/gflags-targets-release.cmake
/usr/lib/cmake/gflags/gflags-nonamespace-targets.cmake
/usr/lib/cmake/gflags/gflags-nonamespace-targets-release.cmake
/usr/bin/gflags_completions.sh
/usr/lib/pkgconfig/gflags.pc

詳細的安裝可以參考gflags install.md 中的介紹,可以使用 ccmake 選擇配置項,或者使用上述的 cmake + 引數的方式配置。

示例

假設有個網路客戶端程式碼,需要指定服務端的地址和埠,希望有預設引數,同時允許使用者通過命令列來指定不同的值。

#include <iostream>
#include <gflags/gflags.h>

DEFINE_string(host,"localhost","Server host address");
DEFINE_int32(port,8080,"Server port");

int main(int argc,char **argv)
{
 gflags::ParseCommandLineFlags(&argc,&argv,true);
 std::cout << "Got '" << FLAGS_host << ":" << FLAGS_port << "'." << std::endl;
 return 0;
}

在程式碼開頭通過 DEFINE_XXX 定義引數,包括了變數名、預設值、引數介紹等;主程式中使用 gflags::ParseCommandLineFlags() 函式解析引數;使用時,在變數名稱前新增 FLAGS_ 頭即可。

通過如下命令列進行編譯。

g++ main.cc -std=c++11 -o gflags -lgflags -lpthread

預設是需要 pthread 執行緒庫的,暫時還不太確定沒有使用多執行緒時,如何關閉該引數。

然後,可以通過如下方式指定引數。

----- 不指定引數,使用預設值
$ ./gflags
Got 'localhost:8080'.

----- 可以選擇指定一個引數,或者多個引數
$ ./gflags -host www.foobar.com
Got 'www.foobar.com:8080'.
$ ./gflags -port 80
Got 'localhost:80'.
$ ./gflags -host www.foobar.com -port 80
Got 'www.foobar.com:80'.

----- 同時支援不同的引數指定方式
$ ./gflags --host www.foobar.com --port 80
Got 'www.foobar.com:80'.
$ ./gflags --host=www.foobar.com=--port 80
Got 'www.foobar.com:80'.

同時也可以使用 --help 引數檢視幫助資訊,包含了 gflags 庫提供的引數,以及使用者提供的引數,如下是輸出的使用者引數資訊。

 Flags from main.cc:
 -host (Server host address) type: string default: "localhost"
 -port (Server port) type: int32 default: 8080

接著看看詳細使用方式。

使用詳解

包含了如何定義、解析等使用場景。

定義引數

在如上的示例中定義了兩種型別的引數,分別為字串 string 和整型 int32 ,包括了變數名、預設值、引數介紹三個入參,三個引數都是必須的。

gflags 總共提供了六種定義方式 (或者說型別)。

DEFINE_bool boolean
DEFINE_int32 32-bit integer
DEFINE_int64 64-bit integer
DEFINE_uint64 unsigned 64-bit integer
DEFINE_double double
DEFINE_string C++ string

可以定義到某個 NameSpace 下,這樣在使用時也必須要帶著 NameSpace 字首。如果在不同的檔案中定義,那麼可以在某個集中的標頭檔案中通過 DECLARE_XXX(VAR) 進行宣告。

注意,不要定義相同名稱的引數,即使在不同的 NameSpace 也不可以;還有幾個保留引數,包括了 flagfile fromenv tryfromenv undefok 等等。

引數解析

在上述的示例中,通過 ParseCommandLineFlags() 函式解析引數,另外還有不帶幫助文件的解析方式,兩個函式的宣告如下。

uint32 ParseCommandLineFlags(int* argc,char*** argv,bool remove_flags);
uint32 ParseCommandLineNonHelpFlags(int* argc,bool remove_flags);

其中 remove_flags 標識指定引數的處理方式,如果為 true ,那麼解析時會將 flag 以及 flag 對應的值從 argv 中刪除,並修改 argc ,也就是說,最後存放的是不包含 flag 的引數;如果為 false 則僅對引數重排,標誌位引數放最前面。

引數校驗

為了檢查引數的值是否合法,可以針對某個引數註冊一個驗證函式,當引數解析或者修改 (呼叫 SetCommandLineOption() 時) 該驗證函式都會被呼叫,返回 true 表示校驗成功,否則失敗。

如下是對於 port 引數的校驗。

#include <iostream>
#include <gflags/gflags.h>

DEFINE_string(host,"Server host address.");
DEFINE_int32(port,"Server port.");

static bool ValidatePort(const char *flag,gflags::int32 value)
{
 if (value > 0 && value < 32768)
  return true;
 std::cerr << "Invalid value for --" << flag << ": " << value << std::endl;
 return false;
}
static const bool port_dummy = gflags::RegisterFlagValidator(&FLAGS_port,&ValidatePort);

int main(int argc,true);
 std::cout << "Got '" << FLAGS_host << ":" << FLAGS_port << "'" << std::endl;
 return 0;
}

如果超過了指定的範圍則會報錯。

使用引數

提供了預設的 --help 檢視幫助資訊,指定引數可以使用 - 或者 -- 符號,引數和值的分割可以使用 ` ` 或者 = ,如上的示例。

對於布林型別,還可以使用 --foobar --nofoobar --foobar=true --foobar false 的方式指定。

檢查引數是否設定

在通過 ParseCommandLineFlags() 函式解析完引數之後,可以通過如下方法檢查對應的變數是否被設定。

gflags::CommandLineFlagInfo info;
if (gflags::GetCommandLineFlagInfo("port",&info) && info.is_default) {
 std::cout << "port is not set." << std::endl;
} else {
 std::cout << "port is set." << std::endl;
}

這裡不是直接比對的設定值與預設值相同,即使指定了與預設值相同的值,也會被認為引數被修改了。

檔案引入

可以通過 --flagfile=FileName 指定引數檔名,檔名也可以使用萬用字元 * 以及 ?,在檔案中每行標識一個引數,例如:

$ cat flags.txt
# This is the test server.
--host=www.foobar.com
--port=80
$ ./gflags --flagfile flags.txt
Got 'www.foobar.com:80'.

以 # 開頭的為註釋,也可以再次使用 --flagfile=FileName 包含一個引數配置檔案。

環境變數引入

可以使用 --fromenv 或者 --tryfromenv 從環境變數中引入引數,也可通過 --fromenv=foo,bar 指定讀取的引數,當然,需要先設定好環境變數。

export FLAGS_foo=xxx; export FLAGS_bar=yyy # sh
setenv FLAGS_foo xxx; setenv FLAGS_bar yyy # tcsh

這種方式等價於在命令列指定 --foo=xxx --bar=yyy 引數。

其中 --fromenv 時如果環境變數不存在則會報錯,而 --tryfromenv 當環境變數中不存在時會使用預設值。

程式中指定

最常見的是允許使用者動態進行配置,也就是說動態載入,可以呼叫 SetCommandLineOption() 函式來實現,函式的宣告如下。

std::string SetCommandLineOption(const char* name,const char* value);

例如。

gflags::SetCommandLineOption("port","9999");

成功則會返回 port set to 9999 字串,否則會返回空字串。

另外也可以通過 bool GetCommandLineOption(const char* name,std::string* OUTPUT) 函式獲取 flag 的介面,如果沒有指定,則會通過 OUTPUT 返回預設的值,只有當指定了一個未定義的 flag 名稱時,會返回 false 。

正常讀寫都可以使用 if (FLAGS_foo); FLAGS_Foo = bar 的形式,但是如果需要執行緒安全的呼叫,建議使用這兩個函式。

完整示例

如上設定的完整示例如下。

#include <iostream>
#include <gflags/gflags.h>

DEFINE_string(host,char **argv)
{
 gflags::SetVersionString("1.1.0");
 gflags::SetUsageMessage("./gflags");
 gflags::ParseCommandLineFlags(&argc,true);

 std::cout << gflags::SetCommandLineOption("port","999") << std::endl;

 std::cout << "Got '" << FLAGS_host << ":" << FLAGS_port << "'" << std::endl;
 return 0;
}

可以通過 g++ main.cc -std=c++11 -o gflags -lgflags -lpthread 命令編譯。

其它

常用引數

gflags 中包含了幾類預設的引數。

--help 顯示所有檔案的所有flag,按檔案、名稱排序,顯示flag名、預設值和幫助
--helpfull 和 --help 相同,顯示全部flag
--helpshort 只顯示執行檔案中包含的flag,通常是 main() 所在檔案
--helpxml 類似 --help,但輸出為xml
--helpon=FILE 只顯示定義在 FILE.* 中得flag
--helpmatch=S 只顯示定義在 *S*.* 中的flag
--helppackage 顯示和 main() 在相同目錄的檔案中的flag
--version 列印執行檔案的版本資訊

--undefok=flagname,flagname,... 後面列出的flag名,可以在無定義的情況下忽略而不報錯

--fromenv --tryfromenv 從環境變數中引入
--flagfile 從檔案中引入

定製版本和幫助資訊

通過 --version 和 --help 預設會輸出其對應的版本和幫助資訊,也可以通過 SetVersionString() 設定版本資訊,通過 SetUsageMessage() 設定幫助的開始軟體資訊 (幫助資訊無法覆蓋)。

gflags::SetVersionString("1.1.0");
gflags::SetUsageMessage("./gflags");

注意,引數的設定需要在呼叫 ParseCommandLineFlags() 之前。

CMake 使用

最新版本的 gflags 已經可以支援 CMake 了,如上安裝時所示,在安裝時會在 /usr/lib/cmake/gflags/ 目錄下安裝相關的檔案,那麼在專案中可以通過如下方式使用。

FIND_PACKAGE(gflags REQUIRED)
INCLUDE_DIRECTORIES(${gflags_INCLUDE_DIR})

ADD_EXECUTABLE(foo main.cc)
TARGET_LINK_LIBRARIES(foo gflags)

如果是將 CMake 安裝到了其它路徑下,那麼可以將上述的檔案複製到 CMake 的模組路徑下再使用。

整型選擇

在 gflags/gflags_declare.h 中定義了 int32 int64 等型別,可以直接使用,不過編譯時需要新增 -std=c++11 引數,否則在使用 cstdint 標頭檔案時會報錯。

其它

可以通過 void GetAllFlags(std::vector<CommandLineFlagInfo>* OUTPUT) 介面遍歷所有的引數,更多介面可以檢視 gflags/gflags.h 標頭檔案。

參考

詳細可以檢視官方文件 How To Use gflags 中的介紹。

到此這篇關於C++命令列解析包gflags使用教程的文章就介紹到這了,更多相關C++命令列解析包gflags使用內容請搜尋我們以前的文章或繼續瀏覽下面的相關文章希望大家以後多多支援我們!