spirit X3可以和BOOST_FUSION_ADAPT_ADT一起使用吗?
Can spirit X3 work with BOOST_FUSION_ADAPT_ADT?
将我的代码从 QI 更改为 X3,然后 BOOST_FUSION_ADAPT_ADT
出现了一些编译错误。我尝试了 boost 1.64 和 1.67,它们都不起作用。我修改了spirit X3的例子rexpr_min
,把getter和setter改成struct rexpr
,把BOOST_FUSION_ADAPT_STRUCT
改成BOOST_FUSION_ADAPT_ADT
,编译失败,也。
环境:
ubuntu 16.04
G++ 5.4,带有 -std=c++17
标志
提升 1.67
错误信息:
boost/spirit/home/x3/core/detail/parse_into_container.hpp:142:35: error: invalid initialization of non-const reference of type ‘boost::fusion::extension::adt_attribute_proxy<client::ast::rexpr, 0, false>&’ from an rvalue of type ‘boost::fusion::extension::deref_impl<boost::fusion::struct_iterator_tag>::apply<boost::fusion::basic_iterator<boost::fusion::struct_iterator_tag, boost::fusion::random_access_traversal_tag, client::ast::rexpr, 0> >::type {aka boost::fusion::extension::adt_attribute_proxy<client::ast::rexpr, 0, false>}’
return call_synthesize(parser, first, last, context, rcontext,
我猜 fusion::front(attr)
return 是一个 const 引用,而 call_synthesize
想要一个非常量引用(在 boost_1_64_0/boost/spirit/home/x3/core/detail/parse_into_container.hpp
,第 146 行)。但是我不知道怎么办。
我用谷歌搜索并找到了 QI 的一些回归,并且它们已在最新版本中进行了修补。但是没有关于X3的信息。
原代码spirit X3 example rexpr_min
,以及我的修改:
/*=============================================================================
Copyright (c) 2001-2015 Joel de Guzman
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
// A simple parser for X3 intended as a minimal starting point.
// 'rexpr' is a parser for a language resembling a minimal subset
// of json, but limited to a dictionary (composed of key=value pairs)
// where the value can itself be a string or a recursive dictionary.
//
// Example:
//
// {
// "color" = "blue"
// "size" = "29 cm."
// "position" = {
// "x" = "123"
// "y" = "456"
// }
// }
//
///////////////////////////////////////////////////////////////////////////////
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/fusion/include/adapt_adt.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/fusion/include/io.hpp>
#include <iostream>
#include <fstream>
#include <string>
#include <map>
///////////////////////////////////////////////////////////////////////////////
// Our AST
///////////////////////////////////////////////////////////////////////////////
namespace client { namespace ast
{
namespace fusion = boost::fusion;
namespace x3 = boost::spirit::x3;
struct rexpr;
struct rexpr_value : x3::variant<
std::string
, x3::forward_ast<rexpr>
>
{
using base_type::base_type;
using base_type::operator=;
};
typedef std::map<std::string, rexpr_value> rexpr_map;
typedef std::pair<std::string, rexpr_value> rexpr_key_value;
struct rexpr
{
rexpr_map i_entries;
const rexpr_map& entries() const { return i_entries; }
void entries(const rexpr_map& ent) { i_entries = ent; }
};
}}
// We need to tell fusion about our rexpr struct
// to make it a first-class fusion citizen
BOOST_FUSION_ADAPT_ADT(client::ast::rexpr,
(obj.entries(), obj.entries(val))
)
///////////////////////////////////////////////////////////////////////////////
// AST processing
///////////////////////////////////////////////////////////////////////////////
namespace client { namespace ast
{
///////////////////////////////////////////////////////////////////////////
// Print out the rexpr tree
///////////////////////////////////////////////////////////////////////////
int const tabsize = 4;
struct rexpr_printer
{
typedef void result_type;
rexpr_printer(int indent = 0)
: indent(indent) {}
void operator()(rexpr const& ast) const
{
std::cout << '{' << std::endl;
for (auto const& entry : ast.entries())
{
tab(indent+tabsize);
std::cout << '"' << entry.first << "\" = ";
boost::apply_visitor(rexpr_printer(indent+tabsize), entry.second);
}
tab(indent);
std::cout << '}' << std::endl;
}
void operator()(std::string const& text) const
{
std::cout << '"' << text << '"' << std::endl;
}
void tab(int spaces) const
{
for (int i = 0; i < spaces; ++i)
std::cout << ' ';
}
int indent;
};
}}
///////////////////////////////////////////////////////////////////////////////
// Our rexpr grammar
///////////////////////////////////////////////////////////////////////////////
namespace client { namespace parser
{
namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
using x3::lit;
using x3::lexeme;
using ascii::char_;
using ascii::string;
x3::rule<class rexpr_value, ast::rexpr_value>
rexpr_value = "rexpr_value";
x3::rule<class rexpr, ast::rexpr>
rexpr = "rexpr";
x3::rule<class rexpr_key_value, ast::rexpr_key_value>
rexpr_key_value = "rexpr_key_value";
auto const quoted_string =
lexeme['"' >> *(char_ - '"') >> '"'];
auto const rexpr_value_def =
quoted_string | rexpr;
auto const rexpr_key_value_def =
quoted_string >> '=' >> rexpr_value;
auto const rexpr_def =
'{' >> *rexpr_key_value >> '}';
BOOST_SPIRIT_DEFINE(rexpr_value, rexpr, rexpr_key_value);
}}
///////////////////////////////////////////////////////////////////////////////
// Main program
///////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv)
{
char const* filename;
if (argc > 1)
{
filename = argv[1];
}
else
{
std::cerr << "Error: No input file provided." << std::endl;
return 1;
}
std::ifstream in(filename, std::ios_base::in);
if (!in)
{
std::cerr << "Error: Could not open input file: "
<< filename << std::endl;
return 1;
}
std::string storage; // We will read the contents here.
in.unsetf(std::ios::skipws); // No white space skipping!
std::copy(
std::istream_iterator<char>(in),
std::istream_iterator<char>(),
std::back_inserter(storage));
using client::parser::rexpr; // Our grammar
client::ast::rexpr ast; // Our tree
using boost::spirit::x3::ascii::space;
std::string::const_iterator iter = storage.begin();
std::string::const_iterator end = storage.end();
bool r = phrase_parse(iter, end, rexpr, space, ast);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "-------------------------\n";
client::ast::rexpr_printer printer;
printer(ast);
return 0;
}
else
{
std::string::const_iterator some = iter+30;
std::string context(iter, (some>end)?end:some);
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "stopped at: \": " << context << "...\"\n";
std::cout << "-------------------------\n";
return 1;
}
}
正如我一直在做的那样 ¹ 你在经常破坏 Spirit 齿轮的事物的交叉点上推动限制:
- 单元素融合序列
- 一般ADT适应
- 在开发中修复了 ADT 的持续性错误( 1.67.0 发布后)
1。单元素难题
我不会在这上面花太多时间,因为它是一个相当陈旧、乏味、有据可查的²并且对你的问题不是必需的。
让我们通过添加一个虚拟字段来避开它:
struct rexpr
{
rexpr_map i_entries;
const rexpr_map& entries() const { return i_entries; }
rexpr_map& entries() { return i_entries; }
void entries(const rexpr_map& ent) { i_entries = ent; }
int i_dummy;
int dummy() const { return i_dummy; }
void dummy(int i) { i_dummy = i; }
};
// ... later:
BOOST_FUSION_ADAPT_ADT(client::ast::rexpr,
(obj.entries(), obj.entries(val))
(obj.dummy(), obj.dummy(val))
)
// ... even later:
auto const rexpr_def =
'{' >> *rexpr_key_value >> '}' >> x3::attr(42);
2。 ADT 代理
Spirit 的属性类别机制将 entries
属性 检测为容器属性(is_container<...>{}
计算为 true
)。
然而,必要的容器特征还没有到位。
更重要的是,由于ADT代理授予的限制性接口,属性值只能代替whole-sale,这意味着我们只能实现一个非常它的次优版本:
namespace boost { namespace spirit { namespace x3 { namespace traits {
template <typename T, auto... Other>
struct container_value<boost::fusion::extension::adt_attribute_proxy<T, Other...> >
: container_value<typename boost::fusion::extension::adt_attribute_proxy<T, Other...>::type>
{ };
template <typename T, auto... Other>
struct push_back_container<boost::fusion::extension::adt_attribute_proxy<T, Other...> >
{
using underlying_type = typename boost::fusion::extension::adt_attribute_proxy<T, Other...>::type;
template <typename X, typename Y>
static bool call(X& proxy, Y&& v) {
auto u = proxy.get();
bool b = push_back_container<underlying_type>::call(u, std::forward<Y>(v));
proxy = u;
return b;
}
};
} } } }
3。惊喜:旧错误在 1.67.0
后修复
您需要提交:
commit ae78e1ec2431517a8b0580099aeba8f9122d8abb
Author: Nikita Kniazev <nok.raven@gmail.com>
Date: Thu Mar 15 17:33:36 2018 +0300
X3: sequence: Fixed reference to temporary bug
commit e7f31017ec7c0b5584d12ec1b718d8c415b26fa1
Author: Nikita Kniazev <nok.raven@gmail.com>
Date: Wed Mar 14 18:54:35 2018 +0300
Qi: Fixed ADT support by permutation and sequence_or operator
This is follow-up to 0f2b3c49ce55a41a7d22cc5533e0f4ba59e491ae
这些比 1.67.0 更新,目前在 develop
分支中。他们(部分)解决了一个老问题:https://github.com/boostorg/spirit/pull/153#issuecomment-152879056。当前行为也可能受到 commit
的影响
commit a0df3c098ff4e42c0958796c4f47d4d72a20c164
Merge: f73b121 fac9dfa
Author: Nikita Kniazev <nok.raven@gmail.com>
Date: Thu Mar 1 13:44:27 2018 +0300
Merge pull request #370 from Kojoley/x3-pass-container-attribute-through-sequence
X3: Pass container attribute through sequence
很难判断这种影响是积极的还是消极的……动荡的局势。
演示
只要说 iff 你
- 针对 ae78e1ec243151 或更高版本 (
develop
) 进行编译
- 应用两者上述解决方法
然后 我看到了预期的输出:
-------------------------
Parsing succeeded
-------------------------
{
"color" = "blue"
"position" = {
"x" = "123"
"y" = "456"
}
"size" = "29 cm."
}
(基于 libs/spirit/example/x3/rexpr/rexpr_examples/a.rexpr
输入)。
总结
希望你不要以为这是"fine"。请考虑在邮件中提出问题 list/github。还要考虑这些:
鉴于最近反复出现围绕容器属性的问题,我认为围绕 boost 1.65.1 的更改导致容器属性全面倒退:
¹ 更不用说我在大多数情况下不喜欢它了:
²Spirit Qi attribute propagation issue with single-member struct, ,
将我的代码从 QI 更改为 X3,然后 BOOST_FUSION_ADAPT_ADT
出现了一些编译错误。我尝试了 boost 1.64 和 1.67,它们都不起作用。我修改了spirit X3的例子rexpr_min
,把getter和setter改成struct rexpr
,把BOOST_FUSION_ADAPT_STRUCT
改成BOOST_FUSION_ADAPT_ADT
,编译失败,也。
环境:
ubuntu 16.04
G++ 5.4,带有
-std=c++17
标志提升 1.67
错误信息:
boost/spirit/home/x3/core/detail/parse_into_container.hpp:142:35: error: invalid initialization of non-const reference of type ‘boost::fusion::extension::adt_attribute_proxy<client::ast::rexpr, 0, false>&’ from an rvalue of type ‘boost::fusion::extension::deref_impl<boost::fusion::struct_iterator_tag>::apply<boost::fusion::basic_iterator<boost::fusion::struct_iterator_tag, boost::fusion::random_access_traversal_tag, client::ast::rexpr, 0> >::type {aka boost::fusion::extension::adt_attribute_proxy<client::ast::rexpr, 0, false>}’
return call_synthesize(parser, first, last, context, rcontext,
我猜 fusion::front(attr)
return 是一个 const 引用,而 call_synthesize
想要一个非常量引用(在 boost_1_64_0/boost/spirit/home/x3/core/detail/parse_into_container.hpp
,第 146 行)。但是我不知道怎么办。
我用谷歌搜索并找到了 QI 的一些回归,并且它们已在最新版本中进行了修补。但是没有关于X3的信息。
原代码spirit X3 example rexpr_min
,以及我的修改:
/*=============================================================================
Copyright (c) 2001-2015 Joel de Guzman
Distributed under the Boost Software License, Version 1.0. (See accompanying
file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
=============================================================================*/
///////////////////////////////////////////////////////////////////////////////
//
// A simple parser for X3 intended as a minimal starting point.
// 'rexpr' is a parser for a language resembling a minimal subset
// of json, but limited to a dictionary (composed of key=value pairs)
// where the value can itself be a string or a recursive dictionary.
//
// Example:
//
// {
// "color" = "blue"
// "size" = "29 cm."
// "position" = {
// "x" = "123"
// "y" = "456"
// }
// }
//
///////////////////////////////////////////////////////////////////////////////
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
#include <boost/spirit/home/x3/support/ast/variant.hpp>
#include <boost/fusion/include/adapt_adt.hpp>
#include <boost/fusion/include/std_pair.hpp>
#include <boost/fusion/include/io.hpp>
#include <iostream>
#include <fstream>
#include <string>
#include <map>
///////////////////////////////////////////////////////////////////////////////
// Our AST
///////////////////////////////////////////////////////////////////////////////
namespace client { namespace ast
{
namespace fusion = boost::fusion;
namespace x3 = boost::spirit::x3;
struct rexpr;
struct rexpr_value : x3::variant<
std::string
, x3::forward_ast<rexpr>
>
{
using base_type::base_type;
using base_type::operator=;
};
typedef std::map<std::string, rexpr_value> rexpr_map;
typedef std::pair<std::string, rexpr_value> rexpr_key_value;
struct rexpr
{
rexpr_map i_entries;
const rexpr_map& entries() const { return i_entries; }
void entries(const rexpr_map& ent) { i_entries = ent; }
};
}}
// We need to tell fusion about our rexpr struct
// to make it a first-class fusion citizen
BOOST_FUSION_ADAPT_ADT(client::ast::rexpr,
(obj.entries(), obj.entries(val))
)
///////////////////////////////////////////////////////////////////////////////
// AST processing
///////////////////////////////////////////////////////////////////////////////
namespace client { namespace ast
{
///////////////////////////////////////////////////////////////////////////
// Print out the rexpr tree
///////////////////////////////////////////////////////////////////////////
int const tabsize = 4;
struct rexpr_printer
{
typedef void result_type;
rexpr_printer(int indent = 0)
: indent(indent) {}
void operator()(rexpr const& ast) const
{
std::cout << '{' << std::endl;
for (auto const& entry : ast.entries())
{
tab(indent+tabsize);
std::cout << '"' << entry.first << "\" = ";
boost::apply_visitor(rexpr_printer(indent+tabsize), entry.second);
}
tab(indent);
std::cout << '}' << std::endl;
}
void operator()(std::string const& text) const
{
std::cout << '"' << text << '"' << std::endl;
}
void tab(int spaces) const
{
for (int i = 0; i < spaces; ++i)
std::cout << ' ';
}
int indent;
};
}}
///////////////////////////////////////////////////////////////////////////////
// Our rexpr grammar
///////////////////////////////////////////////////////////////////////////////
namespace client { namespace parser
{
namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
using x3::lit;
using x3::lexeme;
using ascii::char_;
using ascii::string;
x3::rule<class rexpr_value, ast::rexpr_value>
rexpr_value = "rexpr_value";
x3::rule<class rexpr, ast::rexpr>
rexpr = "rexpr";
x3::rule<class rexpr_key_value, ast::rexpr_key_value>
rexpr_key_value = "rexpr_key_value";
auto const quoted_string =
lexeme['"' >> *(char_ - '"') >> '"'];
auto const rexpr_value_def =
quoted_string | rexpr;
auto const rexpr_key_value_def =
quoted_string >> '=' >> rexpr_value;
auto const rexpr_def =
'{' >> *rexpr_key_value >> '}';
BOOST_SPIRIT_DEFINE(rexpr_value, rexpr, rexpr_key_value);
}}
///////////////////////////////////////////////////////////////////////////////
// Main program
///////////////////////////////////////////////////////////////////////////////
int main(int argc, char **argv)
{
char const* filename;
if (argc > 1)
{
filename = argv[1];
}
else
{
std::cerr << "Error: No input file provided." << std::endl;
return 1;
}
std::ifstream in(filename, std::ios_base::in);
if (!in)
{
std::cerr << "Error: Could not open input file: "
<< filename << std::endl;
return 1;
}
std::string storage; // We will read the contents here.
in.unsetf(std::ios::skipws); // No white space skipping!
std::copy(
std::istream_iterator<char>(in),
std::istream_iterator<char>(),
std::back_inserter(storage));
using client::parser::rexpr; // Our grammar
client::ast::rexpr ast; // Our tree
using boost::spirit::x3::ascii::space;
std::string::const_iterator iter = storage.begin();
std::string::const_iterator end = storage.end();
bool r = phrase_parse(iter, end, rexpr, space, ast);
if (r && iter == end)
{
std::cout << "-------------------------\n";
std::cout << "Parsing succeeded\n";
std::cout << "-------------------------\n";
client::ast::rexpr_printer printer;
printer(ast);
return 0;
}
else
{
std::string::const_iterator some = iter+30;
std::string context(iter, (some>end)?end:some);
std::cout << "-------------------------\n";
std::cout << "Parsing failed\n";
std::cout << "stopped at: \": " << context << "...\"\n";
std::cout << "-------------------------\n";
return 1;
}
}
正如我一直在做的那样
- 单元素融合序列
- 一般ADT适应
- 在开发中修复了 ADT 的持续性错误( 1.67.0 发布后)
1。单元素难题
我不会在这上面花太多时间,因为它是一个相当陈旧、乏味、有据可查的²并且对你的问题不是必需的。
让我们通过添加一个虚拟字段来避开它:
struct rexpr
{
rexpr_map i_entries;
const rexpr_map& entries() const { return i_entries; }
rexpr_map& entries() { return i_entries; }
void entries(const rexpr_map& ent) { i_entries = ent; }
int i_dummy;
int dummy() const { return i_dummy; }
void dummy(int i) { i_dummy = i; }
};
// ... later:
BOOST_FUSION_ADAPT_ADT(client::ast::rexpr,
(obj.entries(), obj.entries(val))
(obj.dummy(), obj.dummy(val))
)
// ... even later:
auto const rexpr_def =
'{' >> *rexpr_key_value >> '}' >> x3::attr(42);
2。 ADT 代理
Spirit 的属性类别机制将 entries
属性 检测为容器属性(is_container<...>{}
计算为 true
)。
然而,必要的容器特征还没有到位。
更重要的是,由于ADT代理授予的限制性接口,属性值只能代替whole-sale,这意味着我们只能实现一个非常它的次优版本:
namespace boost { namespace spirit { namespace x3 { namespace traits {
template <typename T, auto... Other>
struct container_value<boost::fusion::extension::adt_attribute_proxy<T, Other...> >
: container_value<typename boost::fusion::extension::adt_attribute_proxy<T, Other...>::type>
{ };
template <typename T, auto... Other>
struct push_back_container<boost::fusion::extension::adt_attribute_proxy<T, Other...> >
{
using underlying_type = typename boost::fusion::extension::adt_attribute_proxy<T, Other...>::type;
template <typename X, typename Y>
static bool call(X& proxy, Y&& v) {
auto u = proxy.get();
bool b = push_back_container<underlying_type>::call(u, std::forward<Y>(v));
proxy = u;
return b;
}
};
} } } }
3。惊喜:旧错误在 1.67.0
后修复您需要提交:
commit ae78e1ec2431517a8b0580099aeba8f9122d8abb
Author: Nikita Kniazev <nok.raven@gmail.com>
Date: Thu Mar 15 17:33:36 2018 +0300
X3: sequence: Fixed reference to temporary bug
commit e7f31017ec7c0b5584d12ec1b718d8c415b26fa1
Author: Nikita Kniazev <nok.raven@gmail.com>
Date: Wed Mar 14 18:54:35 2018 +0300
Qi: Fixed ADT support by permutation and sequence_or operator
This is follow-up to 0f2b3c49ce55a41a7d22cc5533e0f4ba59e491ae
这些比 1.67.0 更新,目前在 develop
分支中。他们(部分)解决了一个老问题:https://github.com/boostorg/spirit/pull/153#issuecomment-152879056。当前行为也可能受到 commit
commit a0df3c098ff4e42c0958796c4f47d4d72a20c164
Merge: f73b121 fac9dfa
Author: Nikita Kniazev <nok.raven@gmail.com>
Date: Thu Mar 1 13:44:27 2018 +0300
Merge pull request #370 from Kojoley/x3-pass-container-attribute-through-sequence
X3: Pass container attribute through sequence
很难判断这种影响是积极的还是消极的……动荡的局势。
演示
只要说 iff 你
- 针对 ae78e1ec243151 或更高版本 (
develop
) 进行编译 - 应用两者上述解决方法
然后 我看到了预期的输出:
-------------------------
Parsing succeeded
-------------------------
{
"color" = "blue"
"position" = {
"x" = "123"
"y" = "456"
}
"size" = "29 cm."
}
(基于 libs/spirit/example/x3/rexpr/rexpr_examples/a.rexpr
输入)。
总结
希望你不要以为这是"fine"。请考虑在邮件中提出问题 list/github。还要考虑这些:
鉴于最近反复出现围绕容器属性的问题,我认为围绕 boost 1.65.1 的更改导致容器属性全面倒退:
¹ 更不用说我在大多数情况下不喜欢它了:
²Spirit Qi attribute propagation issue with single-member struct,