提高:: program_options:参数具有固定和可变令牌? [英] boost::program_options: parameters with a fixed and a variable token?
问题描述
是否有可能使用这种具有升压:: program_options的参数?
程序-p1 123 -p2 234 -p3 345 -p12 678
即,是否有可能与第一令牌指定参数名称(例如, -p
),其次是一个数字,动态?结果
我想避免这种情况:
程序-p 1 123 -p 2 234 -p 3 -p 345 678 12
Boost.ProgramOptions不提供这种直接的支持。尽管如此,一般有两种解决方案,每个人都有自己的权衡:
- 通配符选项。
- 自定义解析器。
通配符选项
如果它是一个可以接受的使用 - P
而不是 -p
,然后一个通配符选项可用过的。这需要提取期间通过 variables_map
迭代,因为Boost.ProgramOptions不提供支持接收无论是在重载确认键和值()
功能。
的#include<&iostream的GT;
#包括LT&;地图和GT;
#包括LT&;串GT;
#包括LT&;实用>
#包括LT&;矢量>#包括LT&;提升/算法/ string.hpp>
#包括LT&;升压/ foreach.hpp>
#包括LT&;升压/ lexical_cast.hpp>
#包括LT&;升压/ program_options.hpp>的typedef的std ::地图< INT,INT> p_options_type;///从变图@brief提取选项用的关键
///< preFIX>#*。
p_options_type get_p_options(
常量的boost :: program_options :: variables_map&安培; VM,
常量标准::字符串preFIX)
{
p_options_type p_options; 常量的std ::为size_t prefix_size = prefix.size(); 提高:: iterator_range的<的std ::字符串::为const_iterator>范围;
命名空间PO =的boost :: program_options;
BOOST_FOREACH(常量PO :: variables_map :: VALUE_TYPE&安培;对,VM)
{
常量标准::字符串&安培;关键= pair.first; //关键是太小,无法包含preFIX和值,继续下一个。
如果(key.size()≤(1 + prefix.size()))继续; //创建一个分区键分为两部分范围。给定一个键
的P12//所产生的分区将是:
//
//,--------- key.begin - ,preFIX =p
// /,-------- result.begin - :,后preFIX =12
// / /,----- key.end,result.end -
// | P | 1 | 2 |
范围=的boost :: make_iterator_range(key.begin()+ prefix_size,
key.end()); //迭代到如果该键下一个关键:
// - 不以preFIX启动
// - 包含后preFIX非数字
如果(!提振:: STARTS_WITH(键,preFIX)||
!提振::所有(范围,提升:: is_digit()))
继续; //创建对,并插入到地图。
p_options.insert(
的std :: make_pair(
提高:: lexical_cast的< INT>(升压:: copy_range<标准::字符串>(范围))
pair.second.as&所述; INT>()));
}
返回p_options;
}INT主(INT交流,字符* AV [])
{
命名空间PO =的boost :: program_options;
PO :: options_description递减;
desc.add_options()
(P *,PO ::值< INT>())
; PO :: variables_map VM;
存储(PO :: command_line_parser(AC,AV)可供选项(DESC).RUN(),VM); BOOST_FOREACH(常量p_options_type :: VALUE_TYPE&安培; P,get_p_options(VM,P))
{
性病::法院LT&;< P和下;&下; p.first<< =&所述;&下; p.second<<的std :: ENDL;
}
}
和其用法:
./ a.out的--p1 123 --p2 234 --p3 = 345 = --p12 678
P1 = 123
P2 = 234
P3 = 345
P12 = 678
此方法需要遍历整个地图,以确定通配符匹配,造成了 O(n)的复杂性
。此外,它需要修改所需的语法,其中 - P1 123
必须使用,而不是 -p1 123
。这个限制是结果Boost.ProgramOptions的默认解析器行为,其中一个连字符,预计后跟一个字符。
自定义解析器
的另一种方法是添加一个<一个href=\"http://www.boost.org/doc/libs/1_48_0/doc/html/boost/program_options/basic_command_line_parser.html#id1101702-bb\"相对=nofollow>自定义分析器到 command_line_parser
。自定义解析器将允许 -p1
语法,以及其他常见形式,如 - P1 123
和 -p1 = 123
。有迹象表明,需要处理几行为:
- 系统分析器将在同一时间收到一个令牌。因此,将获得
P1
和123
个人调用。这是解析器责任配对P1
到123
。 - Boost.ProgramOptions预计至少有一个解析器来处理令牌。否则
的boost :: program_options :: unknown_option
将被抛出。
要考虑这些行为,自定义解析器将管理国家和执行编码/解码:
- 当解析器收到
P1
,它提取1
,存储解析器状态。另外,EN codeS中的无操作的价值为P
。 - 当解析器收到
123
,它连接codeS它旁边的存储状态值P
因此,如果解析器收到 -p1
和 123
2的值插入 variables_map
为 p
:在无操作的价值和 1:123
。
{P:无操作,
1:123]}
此编码可以通过提供一个辅助函数的EN codeD P
矢量变换成一个图是对用户透明。解码的结果将是:
{1:123}
下面是例子code:
的#include&LT;&iostream的GT;
#包括LT&;地图和GT;
#包括LT&;串GT;
#包括LT&;实用&GT; //的std ::对,性病:: make_pair
#包括LT&;矢量&GT;#包括LT&;提升/算法/ string.hpp&GT;
#包括LT&;升压/ foreach.hpp&GT;
#包括LT&;升压/ lexical_cast.hpp&GT;
#包括LT&;升压/ program_options.hpp&GT;的typedef的std ::地图&LT; INT,INT&GT; p_options_type;/// @brief分析器,它提供了解析-p ##选项的能力。
///
/// @note键和值在分别传递到解析器。
///因此,结构必须是有状态的。
类p_parser
{
上市: 明确的
p_parser(常量标准::字符串&放大器; preFIX)
:$ P $ _ PFIX(preFIX)
hyphen_ $ P $ _ PFIX( - + preFIX)
{} 性病::对&LT;的std ::字符串,性病::字符串&GT;运算符()(常量标准::字符串&安培;令牌)
{
//为了支持-p#=#语法,分裂令牌。
的std ::矢量&lt;标准::字符串&GT;令牌(2);
提高::分(令牌,令牌,提振:: is_any_of(=)); //如果分割产生了两个记号,那么键和值分别为
//作为一个单一的令牌提供。
如果(tokens.size()== 2)
解析(tokens.front()); //解析键。 //解析剩余令牌。
// - 如果tokens.size()== 2,则该标记是值。
// - 否则,它是一个关键。
返回解析(tokens.back());
} /// @brief德code单一连接codeD值。
静态p_options_type :: VALUE_TYPE德code(常量标准::字符串&安培;连接codeD)
{
//德code。
的std ::矢量&lt;标准::字符串&GT;德codeD(field_count_);
提高::分(德codeD,带codeD,提振:: is_any_of(delimiter_)); //如果大小不等于所述场计数,然后编码失败。
如果(field_count_!=去coded.size())
扔的boost :: program_options :: invalid_option_value(EN codeD); // 转变。
返回的std :: make_pair(升压:: lexical_cast的&LT; INT&GT;(DE codeD [0]),
提高:: lexical_cast的&LT; INT&GT;(DE codeD [1]));
} /// @brief德code多恩codeD值。
静态p_options_type德code(常量的std ::矢量&lt;标准::字符串&GT;&安培;连接coded_values)
{
p_options_type p_options;
BOOST_FOREACH(常量标准::字符串&安培;连接codeD,带coded_values)
{
//如果值是一个空操作,然后继续下一个。
如果(的boost ::等号(EN codeD,noop_))继续;
p_options.insert(德code(EN codeD));
}
返回p_options;
}私人的: 性病::对&LT;的std ::字符串,性病::字符串&GT;解析(常量标准::字符串&安培;令牌)
{
返回key_.empty()? parse_key(令牌)
:parse_value(标记);
} /// @brief解析选项的关键部分:P#
性病::对&LT;的std ::字符串,性病::字符串&GT; parse_key(常量标准::字符串&放大器;键)
{
//搜索为preFIX以获得划分的关键成范围
// 三个部分。鉴于--p12,该分区是:
//
//,--------- key.begin - ,pre-preFIX = -
// /,-------- result.begin - :, preFIX =-p
// / /,----- result.end - :,后preFIX =12
// / / / - key.end -
// | - | - | P | 1 | 2 |
//
提高:: iterator_range的&LT;的std ::字符串::为const_iterator&GT;结果=
提高:: find_first(键,prefix_); //不要处理如果密钥:
// - 关键端是相同的结果结束。发生这种情况时,无论
//或者钥匙找不到或存在超出键没有(或--a --P)
// - 从开始到preFIX开始的距离大于2( - P)
// - 非连字符之前preFIX存在(A - P)
// - 非数值是结果之后。
如果(result.end()== key.end()||
距离(key.begin(),result.begin())&GT; 2 ||
!提振::所有(
升压:: make_iterator_range(key.begin(),result.begin()),
升压:: is_any_of( - ))||
!提振::所有(
升压:: make_iterator_range(result.end(),key.end()),
提高:: is_digit()))
{
//一个不同的解析器将处理此令牌。
返回make_pair(标准::字符串(),标准::字符串());
} //否则,键包含预期的格式。
key_.assign(result.end(),key.end()); //返回非空对,否则会Boost.ProgramOptions
//消耗对待下一个值作为完整的价值。该
//空操作条目将在解码过程中被剥离。
返回make_pair(prefix_,noop_);
} 选项/// @brief解析值部分:#
性病::对&LT;的std ::字符串,性病::字符串&GT; parse_value(常量标准::字符串&安培;价值)
{
性病::对&LT;的std ::字符串,性病::字符串&GT; EN codeD =
make_pair(prefix_,KEY_ + delimiter_ +值);
key_.clear();
返回连接codeD;
}私人的:
静态const int的field_count_ = 2;
静态常量为std :: string delimiter_;
静态常量为std :: string noop_;
私人的:
常量标准::字符串prefix_;
常量标准::字符串hyphen_ prefix_;
标准::字符串KEY_;
};常量标准::字符串p_parser :: delimiter_ =;
常量标准::字符串p_parser :: noop_ =空操作;///从变图@brief提取物和去code选项。
p_options_type get_p_options(
常量的boost :: program_options :: variables_map&安培; VM,
常量标准::字符串preFIX)
{
返回p_parser ::德code(VM [preFIX]。作为与LT;的std ::矢量&lt;标准::字符串&GT;&GT;());
}INT主(INT交流,字符* AV [])
{
为const char * P_ preFIX =P;
命名空间PO =的boost :: program_options; //定义选项。
PO :: options_description递减;
desc.add_options()
(P_ preFIX,PO ::值&LT;的std ::矢量&lt;标准::字符串&GT;&GT;() - GT;多令牌())
; PO :: variables_map VM;
存储(PO :: command_line_parser(AC,AV)可供选项(DESC)
.extra_parser(p_parser(P_ preFIX))。运行()
,VM); //提取-p选项。
如果(vm.count(P_ preFIX))
{
//打印-p选项。
BOOST_FOREACH(常量p_options_type :: VALUE_TYPE&安培; P,
get_p_options(VM,P_ preFIX))
{
性病::法院LT&;&LT; P和下;&下; p.first&LT;&LT; =&所述;&下; p.second&LT;&LT;的std :: ENDL;
}
}
}
和其用法:
./ a.out的-p1 123 --p2 234 -p3 = 345 = --p12 678
P1 = 123
P2 = 234
P3 = 345
P12 = 678
除了是一个较大的溶液,一个缺点是需要经过解码处理,以获得所需的值的要求。不能简单地遍历 VM的结果[P]
以有意义的方式。
Is it possible to use parameters of this kind with boost::program_options?
program -p1 123 -p2 234 -p3 345 -p12 678
i.e., is it possible to specify the parameter name with a first token (e.g. -p
) followed by a number, dynamically?
I would like to avoid this:
program -p 1 123 -p 2 234 -p 3 345 -p 12 678
Boost.ProgramOptions does not provide direct support for this. Nevertheless, there are two general solutions that each have their trade-offs:
- Wildcard options.
- Custom parser.
Wildcard Options
If it is an acceptable to use --p
instead of -p
, then a wildcard option can be used. This requires iterating through the variables_map
during extraction, as Boost.ProgramOptions does not provide support receiving both the key and value in an overloaded validate()
function.
#include <iostream>
#include <map>
#include <string>
#include <utility>
#include <vector>
#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/program_options.hpp>
typedef std::map<int, int> p_options_type;
/// @brief Extract options from variable map with the a key of
/// <prefix>#*.
p_options_type get_p_options(
const boost::program_options::variables_map& vm,
const std::string prefix)
{
p_options_type p_options;
const std::size_t prefix_size = prefix.size();
boost::iterator_range<std::string::const_iterator> range;
namespace po = boost::program_options;
BOOST_FOREACH(const po::variables_map::value_type& pair, vm)
{
const std::string& key = pair.first;
// Key is too small to contain prefix and a value, continue to next.
if (key.size() < (1 + prefix.size())) continue;
// Create range that partitions key into two parts. Given a key
// of "p12" the resulting partitions would be:
//
// ,--------- key.begin -., prefix = "p"
// / ,-------- result.begin -:, post-prefix = "12"
// / / ,----- key.end, result.end -'
// |p|1|2|
range = boost::make_iterator_range(key.begin() + prefix_size,
key.end());
// Iterate to next key if the key:
// - does not start with prefix
// - contains a non-digit after prefix
if (!boost::starts_with(key, prefix) ||
!boost::all(range, boost::is_digit()))
continue;
// Create pair and insert into map.
p_options.insert(
std::make_pair(
boost::lexical_cast<int>(boost::copy_range<std::string>(range)),
pair.second.as<int>()));
}
return p_options;
}
int main(int ac, char* av[])
{
namespace po = boost::program_options;
po::options_description desc;
desc.add_options()
("p*", po::value<int>())
;
po::variables_map vm;
store(po::command_line_parser(ac, av).options(desc).run(), vm);
BOOST_FOREACH(const p_options_type::value_type& p, get_p_options(vm, "p"))
{
std::cout << "p" << p.first << "=" << p.second << std::endl;
}
}
And its usage:
./a.out --p1 123 --p2 234 --p3=345 --p12=678 p1=123 p2=234 p3=345 p12=678
This approach requires iterating over the entire map to identify wildcard matches, resulting in a complexity of O(n)
. Additionally, it requires a modification to the desired syntax, where --p1 123
needs to be use instead of -p1 123
. This limitation is the result Boost.ProgramOptions's default parser behavior, where a single hyphen is expected to be followed by a single character.
Custom Parser
The alternative approach is to add a custom parser to the command_line_parser
. A custom parser will allow -p1
syntax, as well as other common forms, such as --p1 123
and -p1=123
. There are a few behaviors that need to be handled:
- A parser will receive a single token at a time. Thus, it will receive
p1
and123
on individual invocations. It is the parsers responsibility to pairp1
to123
. - Boost.ProgramOptions expects at least one parser to handle a token. Otherwise
boost::program_options::unknown_option
will be thrown.
To account for these behaviors, the custom parser will manage state and perform encoding/decoding:
- When the parser receives
p1
, it extracts1
, storing state in the parser. Additionally, it encodes a no operation value forp
. - When the parser receives
123
, it encodes it alongside the stored state as value forp
.
Thus, if the parser receives -p1
and 123
, 2 values are inserted into the variables_map
for p
: the no operation value and 1:123
.
{ "p" : [ "no operation", "1:123" ] }
This encoding can be transparent to the user by providing a helper function to transform the encoded p
vector into a map. The result of decoding would be:
{ 1 : 123 }
Here is the example code:
#include <iostream>
#include <map>
#include <string>
#include <utility> // std::pair, std::make_pair
#include <vector>
#include <boost/algorithm/string.hpp>
#include <boost/foreach.hpp>
#include <boost/lexical_cast.hpp>
#include <boost/program_options.hpp>
typedef std::map<int, int> p_options_type;
/// @brief Parser that provides the ability to parse "-p# #" options.
///
/// @note The keys and values are passed in separately to the parser.
/// Thus, the struct must be stateful.
class p_parser
{
public:
explicit
p_parser(const std::string& prefix)
: prefix_(prefix),
hyphen_prefix_("-" + prefix)
{}
std::pair<std::string, std::string> operator()(const std::string& token)
{
// To support "-p#=#" syntax, split the token.
std::vector<std::string> tokens(2);
boost::split(tokens, token, boost::is_any_of("="));
// If the split resulted in two tokens, then key and value were
// provided as a single token.
if (tokens.size() == 2)
parse(tokens.front()); // Parse key.
// Parse remaining token.
// - If tokens.size() == 2, then the token is the value.
// - Otherwise, it is a key.
return parse(tokens.back());
}
/// @brief Decode a single encoded value.
static p_options_type::value_type decode(const std::string& encoded)
{
// Decode.
std::vector<std::string> decoded(field_count_);
boost::split(decoded, encoded, boost::is_any_of(delimiter_));
// If size is not equal to the field count, then encoding failed.
if (field_count_ != decoded.size())
throw boost::program_options::invalid_option_value(encoded);
// Transform.
return std::make_pair(boost::lexical_cast<int>(decoded[0]),
boost::lexical_cast<int>(decoded[1]));
}
/// @brief Decode multiple encoded values.
static p_options_type decode(const std::vector<std::string>& encoded_values)
{
p_options_type p_options;
BOOST_FOREACH(const std::string& encoded, encoded_values)
{
// If value is a no-op, then continue to next.
if (boost::equals(encoded, noop_)) continue;
p_options.insert(decode(encoded));
}
return p_options;
}
private:
std::pair<std::string, std::string> parse(const std::string& token)
{
return key_.empty() ? parse_key(token)
: parse_value(token);
}
/// @brief Parse key portion of option: "p#"
std::pair<std::string, std::string> parse_key(const std::string& key)
{
// Search for the prefix to obtain a range that partitions the key into
// three parts. Given --p12, the partitions are:
//
// ,--------- key.begin -., pre-prefix = "-"
// / ,-------- result.begin -:, prefix = "-p"
// / / ,----- result.end -:, post-prefix = "12"
// / / / ,-- key.end -'
// |-|-|p|1|2|
//
boost::iterator_range<std::string::const_iterator> result =
boost::find_first(key, prefix_);
// Do not handle the key if:
// - Key end is the same as the result end. This occurs when either
// either key not found or nothing exists beyond the key (--a or --p)
// - The distance from start to prefix start is greater than 2 (---p)
// - Non-hyphens exists before prefix (a--p)
// - Non-numeric values are after result.
if (result.end() == key.end() ||
distance(key.begin(), result.begin()) > 2 ||
!boost::all(
boost::make_iterator_range(key.begin(), result.begin()),
boost::is_any_of("-")) ||
!boost::all(
boost::make_iterator_range(result.end(), key.end()),
boost::is_digit()))
{
// A different parser will handle this token.
return make_pair(std::string(), std::string());
}
// Otherwise, key contains expected format.
key_.assign(result.end(), key.end());
// Return non-empty pair, otherwise Boost.ProgramOptions will
// consume treat the next value as the complete value. The
// noop entries will be stripped in the decoding process.
return make_pair(prefix_, noop_);
}
/// @brief Parse value portion of option: "#"
std::pair<std::string, std::string> parse_value(const std::string& value)
{
std::pair<std::string, std::string> encoded =
make_pair(prefix_, key_ + delimiter_ + value);
key_.clear();
return encoded;
}
private:
static const int field_count_ = 2;
static const std::string delimiter_;
static const std::string noop_;
private:
const std::string prefix_;
const std::string hyphen_prefix_;
std::string key_;
};
const std::string p_parser::delimiter_ = ":";
const std::string p_parser::noop_ = "noop";
/// @brief Extract and decode options from variable map.
p_options_type get_p_options(
const boost::program_options::variables_map& vm,
const std::string prefix)
{
return p_parser::decode(vm[prefix].as<std::vector<std::string> >());
}
int main(int ac, char* av[])
{
const char* p_prefix = "p";
namespace po = boost::program_options;
// Define options.
po::options_description desc;
desc.add_options()
(p_prefix, po::value<std::vector<std::string> >()->multitoken())
;
po::variables_map vm;
store(po::command_line_parser(ac, av).options(desc)
.extra_parser(p_parser(p_prefix)).run()
, vm);
// Extract -p options.
if (vm.count(p_prefix))
{
// Print -p options.
BOOST_FOREACH(const p_options_type::value_type& p,
get_p_options(vm, p_prefix))
{
std::cout << "p" << p.first << "=" << p.second << std::endl;
}
}
}
And its usage:
./a.out -p1 123 --p2 234 -p3=345 --p12=678 p1=123 p2=234 p3=345 p12=678
Aside from the being a larger solution, one drawback is the requirement to go through the decoding process to obtain the desired values. One cannot simply iterate over the results of vm["p"]
in a meaningful way.
这篇关于提高:: program_options:参数具有固定和可变令牌?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!