Boost 库Program Options--第二篇

本文深入介绍了 Boost.ProgramOptions 库的高级用法,包括选项描述的详细配置、简化输入方式、设置预设值、定义必要选项及自定义类型等。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

程式執行參數處理函式庫:Boost Program Options(2/N)

前一篇已經大致解釋了 Boost Program Options 基本上的使用方法。而這一篇,則來細講一下選項描述(options_description)的進一步設定方法吧~

在上一篇裡,基本上是示範了兩種最簡單的方法:

bOptions.add_options()
  ( "help",    "Produce help message" )
  ( "vint",    BPO::value<int>(), "int value" );

其中,help 這個選項的設定,就是最單純、沒有任何對應值的選項,只需要加上說明文字就可以了。而如果像 vint 一樣,需要指定型別等進一步的資料的話,則就需要透過 value<>() 這個 template 函式,來產生一個型別為 typed_value<> 的物件,透過他來指定這個選項對應的值的性質。

最簡單的使用方法,就如同上面 vint 的範例一樣,只要指定型別就可以了。不過如果有需要的話,value<>() 這個函式(文件),和他產生的 typed_value<> 這個型別的物件(文件),都還有許多可以做進一步控制的功能~


直接將值寫入變數

像是以目前 vint 的設定來說,我們還需要自己去把他的值讀出來才行,其實算是有點麻煩的。實際上,Boost Program Options 是可以讓他自動把值寫到我們指定的變數裡的~要這樣做的方法也非常簡單,只要把要拿來儲存這個選項的值的變數的指標,當作 value<>() 這個函式的參數傳進去就可以了~

下面就是一個簡單的範例(註 1)

int main( int argc, char** argv )
{
  int iValInt = 0;
  // setup program options description
  BPO::options_description bOptions( "Test Options" );
  bOptions.add_options()
      ( "vint", BPO::value(&iValInt), "int value" );

  // parse program options
  BPO::variables_map mVMap;
  BPO::store( BPO::parse_command_line( argc, argv, bOptions ), mVMap );
  BPO::notify( mVMap );

  // output
  cout << "Value: " << iValInt << endl;
}

如此一來,當分析完輸入的資料後,如果有指定 vint 的值的話,他的值就會直接被寫到 iValInt裡,可以直接拿來用、而不需要再從 mVMap 裡面拿了~對於要使用的選項很多的時候,這樣是可以有效簡化程式碼的~


相同選項的多個值

像上面這樣的設定方法,基本上在命令提示字元時,只能指定一次 --vint 的值,如果超過一次的話,是會產生 exception 的。但是,如果希望可以指定複數個 vint 的值的話,應該要怎麼辦呢?Boost Program Options 也有針對這樣的需求,做額外的設計;最簡單的改法,就是把值的型別,從 int 改成 vector<int> 就可以了~下面就是一個簡單的範例:

int main( int argc, char** argv )
{
  vector<int> vValInt;

  // setup program options description
  BPO::options_description bOptions( "Test Options" );
  bOptions.add_options()
      ( "vint", BPO::value(&vValInt), "int value" );

  // parse program options
  BPO::variables_map mVMap;
  BPO::store( BPO::parse_command_line( argc, argv, bOptions ), mVMap );
  BPO::notify( mVMap );

  // output
  for( int i = 0; i < vValInt.size(); ++ i )
    cout << i << " -> " << vValInt[i] << endl;
}

其中,value(&vValInt) 是 value< vector<int> >(&vValInt) 的簡化寫法。

而這樣的程式,就可以接受有多個 --vint 了~下面就是一個簡單的使用範例:

C:\>text.exe --vint 1 --vint 2
0 -> 1
1 -> 2

但是,如果希望可以接受「-vint 1 2」這樣的指令的時候,該怎麼辦呢?這時候,可以透過typed_value<> 的函式、multitoken() 來做設定;他的設定方法如下:

bOptions.add_options()
    ( "vint", BPO::value(&vValInt)->multitoken(), "int value" );

這樣的話,執行「text.exe --vint 1 2」的結果就會和「text.exe --vint 1 --vint 2」一樣了~(註 2)


簡化輸入(縮寫)

在上面,選項的名稱在命令提示字元輸入的時候,都必須要加上「--」,而且要把完整的名稱打出來,在要輸入許多參數的時候,還滿麻煩的。而 Boost Program Options 為了可以簡化輸入,也有提供可以簡化的方法。

像上面的範例,可以在選項名稱「vint」後面加上逗號,然後再加上一個字元「I」,變成下面這樣:

bOptions.add_options()
    ( "vint,I", BPO::value(&vValInt), "int value" );

如此一來,在命令提示字元指定參數的時候,除了可以使用本來的「--vint」,也可以使用更短的「-I」來做輸入~這樣,就可以簡化輸入時要打的字了;這在要輸入的參數很多的時候,是很好用的。

下面就是一個簡單的使用範例:

C:\>test.exe --vint 1 -I 2 -I 3
0 -> 1
1 -> 2
2 -> 3

選項的預設值

有的時候,我們會希望某些選項,在使用者沒有指定值的情況下,會有一個預設值,這點在 Boost Program Options 裡面也是可以很簡單做到的~只需要使用 typed_value<> 提供的 default_value()這個函式,來指定預設值就可以了;下面就是簡單的使用範例:

int main( int argc, char** argv )
{
  int iValInt;

  // setup program options description
  BPO::options_description bOptions( "Test Options" );
  bOptions.add_options()
      ( "vint", BPO::value(&iValInt)->default_value(-1), "int value" );

  // parse program options
  BPO::variables_map mVMap;
  BPO::store( BPO::parse_command_line( argc, argv, bOptions ), mVMap );
  BPO::notify( mVMap );

  cout << iValInt << endl;
}

如此一來,在沒有透過 --vint 指定的值時候,iValInt 就會是 -1 了~

而另外一種狀況,則是有可能我們會希望可以只給選項、而不給值,這時候,則可以透過implicit_value() 這個函示來做設定。它的使用方法如下:

bOptions.add_options()
    ( "vint", BPO::value(&iValInt)->implicit_value(1), "int value" );

如此一來,除了可以透過「--vint 10」這樣的參數,把 iValInt 的值設定為 10 之外,也可以只給「--vint」這樣的參數,讓 iValInt 的值變成 1。

而當然,default_value() 和 implicit_value() 也是可以合併使用的!比如說,如果設定成:

bOptions.add_options()
  ( "vint", BPO::value(&iValInt)->default_value(0)->implicit_value(1), 
    "int value" );

在不做特別指定的情況下,iValInt 的值會就會預設值的 0,而如果只有給「--vint」的話,則會是 implicit value 的 1。下面就是這樣的使用範例:

C:\>test.exe
0

C:\>test.exe --vint
1

C:\>test.exe --vint 2
2

在很多時候,這樣的設定方法是相當方便的~


必要選項

如果某個選項,是要求使用者一定要指定的,也可以透過 typed_value<> 的 required() 這個函式,來要求在分析的時候,一定要有這個選項;而如果指定成必要的選項在分析的時候沒有被找到的話,則是會在執行階段丟出一個 exception,沒有特別處理的話,程式是會直接結束的。

下面就是一個簡單的範例:

int main( int argc, char** argv )
{
  int iValInt;

  // setup program options description
  BPO::options_description bOptions( "Test Options" );
  bOptions.add_options()
      ( "vint", BPO::value(&iValInt)->required(), "int value" );

  // parse program options
  BPO::variables_map mVMap;
  try
  {
    BPO::store( BPO::parse_command_line( argc, argv, bOptions ), mVMap );
    BPO::notify( mVMap );
  }
  catch( BPO::error_with_option_name e )
  {
    cerr << e.what() << endl;
    return -1;
  }

  cout << iValInt << endl;
}

這樣一來,在沒有給「--vint」的情況下執行這個程式,就會因為接到 error_with_option_name 這個 exception(註 3),而讓程式結束了~

C:\>test.exe
the option '--vint' is required but missing

C:\>test.exe --vint 1
1

不過由於個人在使用上是習慣透過「--help」來讓程式輸出選項的描述,所以和這邊這種 required()的使用方法算是有衝突的,所以大概不會用吧…


自訂型別

大部分時候,會拿來當作選項的值,所使用的型別都是相對簡單的,一般來說都會 C++ 內建的型別。不過,如果有需要使用自己定義的型別當作選項的值的型別的話,也是沒問題的~唯一的需求,就是必須要針對這個字定義的型別,定義好他的「extraction operator」,也就是 input stream 的 operator>>。(參考

下面就是一個字定義的 2D 向量結構、SVector 的範例。


      
struct SVector { int x; int y; };
istream & operator>> ( istream & in , SVector & vec ) { in >> skipws >> vec .x >> vec .y; return in ; }
int main( int argc , char ** argv ) { SVector vec; // setup program options description BPO:: options_description bOptions( "Test Options" ); bOptions.add_options() ( "vec" , BPO::value< SVector >( &vec ), "a 2D vector" ); // parse program options BPO:: variables_map mVMap; BPO::store( BPO::parse_command_line( argc , argv , bOptions ), mVMap ); BPO::notify( mVMap ); cout << vec.x << ", " << vec.y << endl; }

在這邊,Boost Program Options 在偵測到 --vec 的選項的時候,會透過 input stream 的形式,把輸入的值轉換成指定的型別、也就是 SVector。而下面則是一個簡單的使用範例:

C:\>test.exe --vec "3 2"
3, 2

在這邊給了 --vec "3 2" 這個執行參數後,Boost Program Options 就會自動把 vec 的 x 和 y 的值填進去了~這也算是 C++ iostream 可擴充性的一個很好的利用範例了~

不過要注意的是,由於 C++ 會用空格把命令提示字元的參數做切割,所以像這邊「"3 2"」因為中間有空白,所以就需要使用「"」來把他包起來,不然是會被視為兩個項目、而造成程式的錯誤的。


Nofitier

在 Boost Program Options 的架構裡,在根據 options_description 分析完輸入的來源、把資料寫到 variables_map 後,還需要呼叫 notify() 這個函式、執行「通知」這個動作。而這個通知的動作要做那些事呢?實際上是可以自己定義的;透過 typed_value<> 的 notifier() 這個函式,我們可以指定再呼叫 notify() 的時候,如果這個選項存在、要做那些事。

而使用的方法也很簡單,只要丟一個函式給他就可以了~下面就是一個簡單的範例:


      
void test( const int & rVal ) { cout << "Notified: " << rVal << endl; }
int main( int argc , char ** argv ) { // setup program options description BPO:: options_description bOptions( "Test Options" ); bOptions.add_options() ( "vint" , BPO::value< int >()->notifier( test ), "int value" ); // parse program options BPO:: variables_map mVMap; BPO::store( BPO::parse_command_line( argc , argv , bOptions ), mVMap ); BPO::notify( mVMap ); }

這邊就是很簡單,把 test() 這個函式當作參數,傳給 notifier() 就可以了。在這樣設定後,當呼叫BPO::notify( mVMap ) 的時候,如果有偵測到 vint 這個選項的話,他就會呼叫 test() 這個函式,並且把偵測到的值傳進去。

實際上,notifier() 接受的參數是 Boost 裡、型別是 function1<void, const T&> 的 function object(註 4),所以只要是符合形式(沒有回傳值、接受一個 const reference 的值)、可以呼叫的物件,都可以傳進來;像是 C++11 的 lambda expression(參考),也是可以拿來用的~

如果在程式裡面,針對格別選項的處理流程很複雜的話,使用 Boost Program Options 的 notify 機制,應該也是一個把程式模組成一塊一塊的好方法。


變數名稱

實際上,typed_value<> 還有提供一些其他的功能,可以使用。不過有的 Heresy 是先跳過了。而在這篇,最後來講一個主要影響是顯示上的功能,那就是「變數名稱」。

Boost Program Options 在 Heresy 來看,其中一個最重要的好處,就是在完成options_description 的設定的同時,也就把給使用者看得使用說明的資料完成了!比如像下面的設定了一個 options_description

BPO::options_description bOptions( "Test Options" );
bOptions.add_options()
    ( "help", "Help message" )
    ( "vint", BPO::value<int>()->default_value(1), "a int value" )
    ( "vec",  BPO::value<SVector>( &vec ), "a 2D vector" );

如果我們透過 output stream 把 bOptions 輸出,就可以得到下面的說明:

Test Options:
  --help                Help message
  --vint arg (=1)       a int value
  --vec arg             a 2D vector

這真的是相當方便的~因為如此一來,就可以避免使用說明和實際參數的不一致了!(分開寫常常會忘了改 :p)

但是,不知道有沒有發現,在上面的說明裡,提示要輸入的變數,都是「arg」,看不出來是怎樣類型的資料,其實算是比較美中不足的地方。

不過實際上,Boost Program Options 也是有針對這個問題做處理的!只要透過 typed_value<> 的value_name() 這個函式,就可以把「arg」這個通用性的字串改掉,改成我們自己要的內容了~比如說,把上面的程式碼改成下面的:

bOptions.add_options()
  ( "help", "Help message" )
  ( "vint", BPO::value<int>()->default_value(1)->value_name( "0~9" ), 
            "a int value" )
  ( "vec",  BPO::value<SVector>( &vec )->value_name( "\"x y\""), 
            "a 2D vector" );

那在輸出的時候,就會變成:

Test Options:
  --help                Help message
  --vint 0~9 (=1)       a int value
  --vec "x y"           a 2D vector

可以看到,本來的 arg 就已經變成上面指定的值了~如此一來,在說明的部分,就可以更有效地說明如何使用了。


附註:

  1. 實際上這邊的 value(&iValInt) 算是 value<int>(&iValInt) 的簡化寫法;因為型別 int 已經可以透過 iValInt 判斷出來,所以就不需要特別寫出來了。

  2. 官方文件(連結)裡的 multitoken() 並沒有使用 vectot<> 當作型別,但是 Heresy 測試這樣是有問題的…

  3. Boost Program Options 有定義許多不同的錯誤狀態,可以參考 errors.hpp 這個檔案(官方文件);而根據不同的錯誤、會丟出不同的類型的 exception 物件,如果要處理得好的話,要個別處理。基本上,個人是覺得使用上還滿麻煩的,之後有機會會再補上這邊的資料。

  4. 內部應該是定義成 STL 的 unary_function



    原帖:http://viml.nchc.org.tw/blog/paper_info.php?CLASS_ID=1&SUB_ID=1&PAPER_ID=362

转载于:https://www.cnblogs.com/catkins/p/5270623.html

<think>好的,我现在需要解决用户关于Boost程序选项中遇到“too_many_positional_options_error”错误的问题。用户提到是因为命令行参数过多导致的,我需要找出可能的原因和解决方案。 首先,这个错误通常发生在位置参数的数量超过了程序预期的数量。Boost.Program_options在解析命令行参数时,如果发现用户输入了比定义的位置参数更多的参数,就会抛出这个异常。因此,我需要考虑用户是否正确定义了位置参数,或者是否有未处理的额外参数。 可能的原因有几个:用户可能没有正确配置位置参数的数量,或者在定义选项时没有将某些参数标记为多值参数。例如,如果用户定义了一个位置参数,但实际输入了多个,而没有使用`multitoken()`或`composing()`方法,就会导致错误。此外,用户可能没有正确处理剩余的参数,比如使用`allow_unregistered()`选项来忽略未知参数,或者使用`positional_options_description`的`max_total_count`来限制参数数量。 接下来,我需要回忆Boost.Program_options的相关文档和常见解决方案。记得位置参数是通过`add_options()`和`positional_options_description`来定义的。如果用户希望接受多个位置参数,可能需要使用`multitoken()`来允许一个选项接受多个值,或者正确设置`max_total_count`。例如,设置`positional_options_description`的`max_total_count`为一个较大的数值,或者设置为-1表示无限制,但实际上Boost可能不支持无限制,需要测试。 另外,用户可能在代码中未捕获未识别的参数,导致多余的参数未被处理,从而触发异常。这时可以启用`allow_unregistered()`选项,让解析器忽略未知参数,但需要用户自己处理这些参数。不过,这可能会导致其他问题,比如拼写错误的选项被忽略,所以需要谨慎使用。 可能的解决方案步骤包括:检查位置参数的定义是否正确,确认是否使用了`multitoken()`或`composing()`,调整`max_total_count`的值,或者启用`allow_unregistered()`。此外,用户可能需要使用`vector`来存储多个位置参数,并在解析后处理它们。 需要验证这些方法的正确性,比如查看Boost的文档确认`max_total_count`是否可以设置为-1,或者是否必须明确指定数量。例如,在Boost的文档中,`positional_options_description::add`方法的第二个参数是最大数量,如果设置为-1,可能表示接受所有剩余参数。但实际测试中可能需要设置为一个足够大的数值,比如1000,或者分阶段处理参数。 另外,用户可能在处理多个子命令或复杂参数结构时遇到问题,需要正确配置各个子命令的位置参数。例如,不同的子命令可能有不同的位置参数需求,需要动态调整解析逻辑。 最后,可能需要提供代码示例,展示如何正确配置位置参数和处理多余的参数,以及如何捕获和处理异常,给出用户友好的错误提示。同时,提醒用户在定义位置参数时要明确预期数量,避免不匹配的情况。</think>### 解决 `boost::program_options::too_many_positional_options_error` 错误 当使用 Boost.Program_options 解析命令行参数时,若位置参数(positional arguments)数量超过预定义的限制,会抛出 `too_many_positional_options_error` 异常。以下是分步解决方案: --- #### **1. 原因分析** - **位置参数定义不匹配**:通过 `positional_options_description` 定义的位置参数数量与实际输入不符。 - **未处理剩余参数**:未显式声明允许额外参数,或未设置参数容器为多值类型(如 `std::vector`)。 --- #### **2. 解决方案** ##### **(1) 调整位置参数定义** 在定义位置参数时,明确指定最大允许数量: ```cpp namespace po = boost::program_options; po::positional_options_description pos_desc; pos_desc.add("input-file", 3); // 允许最多3个位置参数 pos_desc.add("output-file", 1); // 下一位置参数为输出文件 ``` ##### **(2) 使用多值容器存储参数** 若需要接受任意数量的位置参数,使用 `std::vector` 并配合 `multitoken()`: ```cpp po::options_description desc("Options"); desc.add_options() ("input-files", po::value<std::vector<std::string>>()->multitoken(), "输入文件列表"); // 解析时允许将多个参数绑定到 "input-files" po::variables_map vm; po::store(po::command_line_parser(argc, argv).options(desc).run(), vm); po::notify(vm); ``` ##### **(3) 允许未注册参数** 通过 `allow_unregistered()` 忽略未知参数(需谨慎使用): ```cpp auto parsed = po::command_line_parser(argc, argv) .options(desc) .allow_unregistered() .run(); // 提取未注册参数 auto unrecognized = po::collect_unrecognized(parsed.options, po::include_positional); ``` ##### **(4) 异常捕获与提示** 显式捕获异常并提供友好提示: ```cpp try { po::store(po::parse_command_line(argc, argv, desc), vm); po::notify(vm); } catch (const po::too_many_positional_options_error& e) { std::cerr << "错误:输入参数过多。请检查格式。\n"; std::cerr << "示例: ./program <input1> <input2> <output>\n"; return 1; } ``` --- #### **3. 完整示例** ```cpp #include <boost/program_options.hpp> #include <vector> int main(int argc, char* argv[]) { namespace po = boost::program_options; po::options_description desc("Allowed options"); desc.add_options() ("help", "显示帮助信息") ("input", po::value<std::vector<std::string>>()->multitoken(), "输入文件") ("output", po::value<std::string>(), "输出文件"); po::positional_options_description pos_desc; pos_desc.add("input", 3); // 最多3个输入文件 pos_desc.add("output", 1); // 1个输出文件 try { po::variables_map vm; auto parsed = po::command_line_parser(argc, argv) .options(desc) .positional(pos_desc) .run(); po::store(parsed, vm); po::notify(vm); if (vm.count("help")) { std::cout << desc << "\n"; return 0; } // 处理参数... } catch (const po::error& e) { std::cerr << "参数错误: " << e.what() << "\n"; return 1; } return 0; } ``` --- #### **4. 常见错误场景** - **未设置 `multitoken()`**:若输入参数数量超过默认限制(通常为1),需显式声明多值参数。 - **位置参数顺序错误**:`positional_options_description` 中参数的顺序需与命令行输入顺序一致。 ---
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值