1. 程式人生 > >google gflags 庫完全使用

google gflags 庫完全使用

簡單介紹

gflags 是 google 開源的用於處理命令列引數的專案。

安裝編譯

專案主頁:gflags

➜  ~  git clone https://github.com/gflags/gflags.git # 下載原始碼
➜  ~  cd gflags
➜  gflags git:(master) ✗ mkdir build && cd build # 建立資料夾
➜  build git:(master) ✗ cmake .. # 使用 cmake 編譯生成 Makefile 檔案
➜  build git:(master) ✗ make # make 編譯
➜ build git:(master) ✗ sudo make install # 安裝庫

這時 gflags 庫會預設安裝在 /usr/local/lib/ 下,標頭檔案放在 /usr/local/include/gflags/ 中。

基礎使用

我們從一個簡單的需求來看 gflags 的使用,只要一分鐘。假如我們有個程式,需要知道伺服器的 ip 和埠,我們在程式中有預設的指定引數,同時希望可以通過命令列來指定不同的值。

實現如下:

#include <iostream>

#include <gflags/gflags.h>

/**
 *  定義命令列引數變數
 *  預設的主機地址為 127.0.0.1,變數解釋為 'the server host'
 *  預設的埠為 12306,變數解釋為 'the server port'
 */
DEFINE_string(host, "127.0.0.1", "the server host"); DEFINE_int32(port, 12306, "the server port"); int main(int argc, char** argv) { // 解析命令列引數,一般都放在 main 函式中開始位置 gflags::ParseCommandLineFlags(&argc, &argv, true); // 訪問引數變數,加上 FLAGS_ std::cout << "The server host is: " << FLAGS_host << ", the server port is: "
<< FLAGS_port << std::endl; return 0; }

OK, 寫完了讓我們編譯執行。

➜  test g++ gflags_test.cc -o gflags_test -lgflags -lpthread # -l 連結庫進行編譯

➜  test ./gflags_test #不帶任何引數                                                       
The server host is: 127.0.0.1, the server port is: 12306

➜  test ./gflags_test -host 10.123.78.90 #只帶 host 引數
The server host is: 10.123.78.90, the server port is: 12306

➜  test ./gflags_test -port 8008 # 只帶 port 引數             
The server host is: 127.0.0.1, the server port is: 8008

➜  test ./gflags_test -host 10.123.78.90 -port 8008 # host 和 port 引數
The server host is: 10.123.78.90, the server port is: 8008

➜  test ./gflags_test --host 10.123.78.90 --port 8008 # 用 -- 指定
The server host is: 10.123.78.90, the server port is: 8008

➜  test ./gflags_test --host=10.123.78.90 --port=8008 # 用 = 連線引數值
The server host is: 10.123.78.90, the server port is: 8008

➜  test ./gflags_test --help # 用 help 檢視可指定的引數及引數說明
gflags_test: Warning: SetUsageMessage() never called

  Flags from /home/rookie/code/gflags/src/gflags.cc:
    .... # 略

  Flags from /home/rookie/code/gflags/src/gflags_reporting.cc:
    ..... # 略

  Flags from gflags_test.cc: #這裡是我們定義的引數說明和預設值
    -host (the server host) type: string default: "127.0.0.1"
    -port (the server port) type: int32 default: 12306

看,我們不僅快速完成了需求,而且似乎多了很多看起來不錯的特性。在上面我們使用了兩種型別的引數,string 和 int32,gflags 一共支援 5 種類型的命令列引數定義:

  • DEFINE_bool: 布林型別
  • DEFINE_int32: 32 位整數
  • DEFINE_int64: 64 位整數
  • DEFINE_uint64: 無符號 64 位整數
  • DEFINE_double: 浮點型別 double
  • DEFINE_string: C++ string 型別

如果你希望支援更復雜的結構,比如 list,你需要通過自己做一定的定義和解析,比如字串按某個分隔符分割得到一個列表。

每一種型別的定義和使用都跟上面我們的例子相似,有所不同的是 bool 引數,bool 引數在命令列可以不指定值也可以指定值,假如我們定義了一個 bool 引數 debug_switch,可以在命令列這樣指定:

➜  test ./gflags_test -debug_switch  # 這樣就是 true
➜  test ./gflags_test -debug_switch=true # 這樣也是 true
➜  test ./gflags_test -debug_switch=1 # 這樣也是 true
➜  test ./gflags_test -debug_switch=false # 0 也是 false

所有我們定義的 gflags 變數都可以通過 FLAGS_ 字首加引數名訪問,gflags 變數也可以被自由修改:

if (FLAGS_consider_made_up_languages)
    FLAGS_languages += ",klingon";
if (FLAGS_languages.find("finnish") != string::npos)
    HandleFinnish();

進階?同樣 Easy

定義規範

如果你想要訪問在另一個檔案定義的 gflags 變數呢?使用 DECLARE_,它的作用就相當於用 extern 宣告變數。為了方便的管理變數,我們推薦在 .cc 或者 .cpp 檔案中 DEFINE 變數,然後只在對應 .h 中或者單元測試中 DECLARE 變數。例如,在 foo.cc 定義了一個 gflags 變數 DEFINE_string(name, 'bob', ''),假如你需要在其他檔案中使用該變數,那麼在 foo.h 中宣告 DECLARE_string(name),然後在使用該變數的檔案中 include "foo.h" 就可以。當然,這只是為了更好地管理檔案關聯,如果你不想遵循也是可以的。

引數檢查

如果你定義的 gflags 引數很重要,希望檢查其值是否符合預期,那麼可以定義並註冊引數的值的檢查函式。如果採用 static 全域性變數來確保檢查函式會在 main 開始時被註冊,可以保證註冊會在 ParseCommandLineFlags 函式之前。如果預設值檢查失敗,那麼 ParseCommandLineFlags 將會使程式退出。如果之後使用 SetCommandLineOption() 來改變引數的值,那麼檢查函式也會被呼叫,但是如果驗證失敗,只會返回 false,然後引數保持原來的值,程式不會結束。看下面的程式示例:

#include <stdint.h>
#include <stdio.h>
#include <iostream>

#include <gflags/gflags.h>

// 定義對 FLAGS_port 的檢查函式
static bool ValidatePort(const char* name, int32_t value) {
    if (value > 0 && value < 32768) {
        return true;
    }
    printf("Invalid value for --%s: %d\n", name, (int)value);
    return false;
}

/**
 *  設定命令列引數變數
 *  預設的主機地址為 127.0.0.1,變數解釋為 'the server host'
 *  預設的埠為 12306,變數解釋為 'the server port'
 */
DEFINE_string(host, "127.0.0.1", "the server host");
DEFINE_int32(port, 12306, "the server port");

// 使用全域性 static 變數來註冊函式,static 變數會在 main 函式開始時就呼叫
static const bool port_dummy = gflags::RegisterFlagValidator(&FLAGS_port, &ValidatePort);

int main(int argc, char** argv) {
    // 解析命令列引數,一般都放在 main 函式中開始位置
    gflags::ParseCommandLineFlags(&argc, &argv, true);
    std::cout << "The server host is: " << FLAGS_host
        << ", the server port is: " << FLAGS_port << std::endl;

    // 使用 SetCommandLineOption 函式對引數進行設定才會呼叫檢查函式
    gflags::SetCommandLineOption("port", "-2");
    std::cout << "The server host is: " << FLAGS_host
        << ", the server port is: " << FLAGS_port << std::endl;
    return 0;
}

讓我們執行一下程式,看看怎麼樣:

#命令列指定非法值,程式解析引數時直接退出
➜  test ./gflags_test -port -2 
Invalid value for --port: -2
ERROR: failed validation of new value '-2' for flag 'port'
# 這裡引數預設值合法,但是 SetCommandLineOption 指定的值不合法,程式不退出,引數保持原來的值 
➜  test ./gflags_test        
The server host is: 127.0.0.1, the server port is: 12306
Invalid value for --port: -2
The server host is: 127.0.0.1, the server port is: 12306

使用 flagfile

如果我們定義了很多引數,那麼每次啟動時都在命令列指定對應的引數顯然是不合理的。gflags 庫已經很好的解決了這個問題。你可以把 flag 引數和對應的值寫在檔案中,然後執行時使用 -flagfile 來指定對應的 flag 檔案就好。檔案中的引數定義格式與通過命令列指定是一樣的。

例如,我們可以定義這樣一個檔案,檔案字尾名沒有關係,為了方便管理可以使用 .flags:

--host=10.123.14.11
--port=23333

然後命令列指定:

➜  test ./gflags_test --flagfile server.flags 
The server host is: 10.123.14.11, the server port is: 23333

棒!以後再也不用擔心引數太多了~^_^

看到這裡,是不是覺得 gflags 對你的專案很有幫助?用起來吧,釋放超能力 :)