- Java 双重比较
- java - 比较器与 Apache BeanComparator
- Objective-C 完成 block 导致额外的方法调用?
- database - RESTful URI 是否应该公开数据库主键?
我有一个非常基本的 Boost Spirit Qi 语法来解析 IP 端口或 IP 端口范围,即 "6322"
或 "6322-6325"
.
语法如下:
template<class It>
void init_port_rule(u16_rule<It>& port)
{
port = boost::spirit::qi::uint_parser<uint16_t, 10, 2, 5>();
port.name("valid port range: (10, 65535)");
}
typedef boost::fusion::vector
< std::uint16_t
, boost::optional<std::uint16_t>
> port_range_type
;
template<class It>
struct port_range_grammar
: boost::spirit::qi::grammar
< It
, port_range_type()
>
{
typedef typename port_range_grammar::base_type::sig_type signature;
port_range_grammar()
: port_range_grammar::base_type(start, "port_range")
{
init_port_rule(port);
using namespace boost::spirit::qi;
start = port > -(lit("-") > port);
}
private:
boost::spirit::qi::rule<It, signature> start;
boost::spirit::qi::rule<It, std::uint16_t()> port;
};
我有点难以定义,在 port1
范围内必须小于 port2
.我想我必须使用 eps
解析器在这里,但似乎没有找到指定它的正确方法。非常欢迎任何建议。
最佳答案
你确实可以使用语义 Action 。不过,您并不总是需要将它们附加到 eps
节点。如果这样做,您会得到以下结果:
port %= uint_parser<uint16_t, 10, 2, 5>() >> eps[ _pass = (_val>=10 && _val<=65535) ];
start = (port >> -('-' >> port)) >> eps(validate(_val));
请注意,一条规则使用 Simple Form eps
附有语义 Action 。这需要 operator%=
到 still invoke automatic attribute propagation .
第二个实例使用 Semantic Predicate form of eps
. validate
函数需要是 Phoenix Actor,我这样定义它:
struct validations {
bool operator()(PortRange const& range) const {
if (range.end)
return range.start<*range.end;
return true;
}
};
boost::phoenix::function<validations> validate;
请注意,您可以像这样在两条规则上使用第二种规则样式:
port %= uint_parser<Port, 10, 2, 5>() >> eps(validate(_val));
start = (port >> -('-' >> port)) >> eps(validate(_val));
如果您只是添加一个重载来验证单个端口:
struct validations {
bool operator()(Port const& port) const {
return port>=10 && port<=65535;
}
bool operator()(PortRange const& range) const {
if (range.end)
return range.start<*range.end;
return true;
}
};
让我们定义一些很好的边缘情况并测试它们!
#include <boost/fusion/adapted/struct.hpp>
#include <boost/optional/optional_io.hpp>
#include <boost/spirit/include/qi.hpp>
#include <boost/spirit/include/phoenix.hpp>
namespace qi = boost::spirit::qi;
using Port = std::uint16_t;
struct PortRange {
Port start;
boost::optional<Port> end;
};
BOOST_FUSION_ADAPT_STRUCT(PortRange, start, end)
template <class It, typename Attr = PortRange> struct port_range_grammar : qi::grammar<It, Attr()> {
port_range_grammar() : port_range_grammar::base_type(start, "port_range") {
using namespace qi;
port %= uint_parser<Port, 10, 2, 5>() >> eps(validate(_val));
start = (port >> -('-' >> port)) >> eps(validate(_val));
port.name("valid port range: (10, 65535)");
}
private:
struct validations {
bool operator()(Port const& port) const {
return port>=10 && port<=65535;
}
bool operator()(PortRange const& range) const {
if (range.end)
return range.start<*range.end;
return true;
}
};
boost::phoenix::function<validations> validate;
qi::rule<It, Attr()> start;
qi::rule<It, Port()> port;
};
int main() {
using It = std::string::const_iterator;
port_range_grammar<It> const g;
std::string const valid[] = {"10", "6322", "6322-6325", "65535"};
std::string const invalid[] = {"9", "09", "065535", "65536", "-1", "6325-6322"};
std::cout << " -------- valid cases\n";
for (std::string const input : valid) {
It f=input.begin(), l = input.end();
PortRange range;
bool accepted = parse(f, l, g, range);
if (accepted)
std::cout << "Parsed '" << input << "' to " << boost::fusion::as_vector(range) << "\n";
else
std::cout << "TEST FAILED '" << input << "'\n";
}
std::cout << " -------- invalid cases\n";
for (std::string const input : invalid) {
It f=input.begin(), l = input.end();
PortRange range;
bool accepted = parse(f, l, g, range);
if (accepted)
std::cout << "TEST FAILED '" << input << "' (returned " << boost::fusion::as_vector(range) << ")\n";
}
}
打印:
-------- valid cases
Parsed '10' to (10 --)
Parsed '6322' to (6322 --)
Parsed '6322-6325' to (6322 6325)
Parsed '65535' to (65535 --)
-------- invalid cases
TEST FAILED '065535' (returned (6553 --))
CONGRATULATIONS We found a broken edge case
事实证明,通过将 uint_parser 限制为 5 个位置,我们可能会在输入中保留字符,因此 065535
解析为 6553
(保留 '5'
未解析...)。修复很简单:
start = (port >> -('-' >> port)) >> eoi >> eps(validate(_val));
或者确实是:
start %= (port >> -('-' >> port)) >> eoi[ _pass = validate(_val) ];
固定版本 Live On Coliru
您会注意到我修改了您的属性类型。这大部分是“好品味”。请注意,在实践中,您可能希望将范围表示为单端口或范围:
using Port = std::uint16_t;
struct PortRange {
Port start, end;
};
using PortOrRange = boost::variant<Port, PortRange>;
然后你会像这样解析:
port %= uint_parser<Port, 10, 2, 5>() >> eps(validate(_val));
range = (port >> '-' >> port) >> eps(validate(_val));
start = (range | port) >> eoi;
完整演示 Live On Coliru
您可能认为这会变得难以使用。 我同意!
让我们首先不使用 variant
或 optional
。让我们让一个端口只是一个范围恰好有start==end
:
using Port = std::uint16_t;
struct PortRange {
Port start, end;
};
像这样解析它:
start = port >> -('-' >> port | attr(0)) >> eoi >> eps(validate(_val));
我们在validate
中所做的就是检查end
是否为0
:
bool operator()(PortRange& range) const {
if (range.end == 0)
range.end = range.start;
return range.start <= range.end;
}
现在输出是: Live On Coliru
-------- valid cases
Parsed '10' to (10-10)
Parsed '6322' to (6322-6322)
Parsed '6322-6325' to (6322-6325)
Parsed '65535' to (65535-65535)
-------- invalid cases
请注意,您现在如何始终枚举 start
..end
而不知道是否存在端口或端口范围。这可能很方便(取决于您正在实现的逻辑)。
关于c++ - Boost Spirit Qi 验证输入解析器,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/47243884/
我之前让 dll 注入(inject)器变得简单,但我有 Windows 7,我用 C# 和 C++ 做了它,它工作得很好!但是现在当我在 Windows 8 中尝试相同的代码时,它似乎没有以正确的方
我正在尝试制作一个名为 core-splitter 的元素,该元素在 1.0 中已弃用,因为它在我们的项目中起着关键作用。 如果您不知道 core-splitter 的作用,我可以提供一个简短的描述。
我有几个不同的蜘蛛,想一次运行所有它们。基于 this和 this ,我可以在同一个进程中运行多个蜘蛛。但是,我不知道如何设计一个信号系统来在所有蜘蛛都完成后停止 react 器。 我试过了: cra
有没有办法在达到特定条件时停止扭曲 react 器。例如,如果一个变量被设置为某个值,那么 react 器应该停止吗? 最佳答案 理想情况下,您不会将变量设置为一个值并停止 react 器,而是调用
https://code.angularjs.org/1.0.0rc9/angular-1.0.0rc9.js 上面的链接定义了外部js文件,我不知道Angular-1.0.0rc9.js的注入(in
我正在尝试运行一个函数并将服务注入(inject)其中。我认为这可以使用 $injector 轻松完成.所以我尝试了以下(简化示例): angular.injector().invoke( [ "$q
在 google Guice 中,我可以使用函数 createInjector 创建基于多个模块的注入(inject)器。 因为我使用 GWT.create 在 GoogleGin 中实例化注入(in
我在 ASP.NET Core 1.1 解决方案中使用配置绑定(bind)。基本上,我在“ConfigureServices Startup”部分中有一些用于绑定(bind)的简单代码,如下所示: s
我在 Spring MVC 中设置 initBinder 时遇到一些问题。我有一个 ModelAttribute,它有一个有时会显示的字段。 public class Model { privat
我正在尝试通过jquery post发布knockoutjs View 模型 var $form = $('#barcodeTemplate form'); var data = ko.toJS(vm
如何为包含多态对象集合的复杂模型编写自定义模型绑定(bind)程序? 我有下一个模型结构: public class CustomAttributeValueViewModel { publi
您好,我正在尝试实现我在 this article 中找到的扩展方法对于简单的注入(inject)器,因为它不支持开箱即用的特定构造函数的注册。 根据这篇文章,我需要用一个假的委托(delegate)
你好,我想自动注册我的依赖项。 我现在拥有的是: public interface IRepository where T : class public interface IFolderReposi
我正在使用 Jasmine 测试一些 Angular.js 代码。为此,我需要一个 Angular 注入(inject)器: var injector = angular.injector(['ng'
我正在使用 Matlab 代码生成器。不可能包含代码风格指南。这就是为什么我正在寻找一个工具来“ reshape ”、重命名和重新格式化生成的代码,根据我的: 功能横幅约定 文件横幅约定 命名约定 等
这个问题在这里已经有了答案: Where and why do I have to put the "template" and "typename" keywords? (8 个答案) 关闭 8
我开发了一种工具,可以更改某些程序的外观。为此,我需要在某些进程中注入(inject)一个 dll。 现在我基本上使用这个 approach .问题通常是人们无法注入(inject) dll,因为他们
我想使用 swing、spring 和 hibernate 编写一个 java 应用程序。 我想使用数据绑定(bind)器用 bean 的值填充 gui,并且我还希望它反射(reflect) gui
我有这段代码,当两个蜘蛛完成后,程序仍在运行。 #!C:\Python27\python.exe from twisted.internet import reactor from scrapy.cr
要点是 Spring Batch (v2) 测试框架具有带有 @Autowired 注释的 JobLauncherTestUtils.setJob。我们的测试套件有多个 Job 类提供者。因为这个类不
我是一名优秀的程序员,十分优秀!