精神X3,如何获得属性类型以匹配规则类型
Spirit X3, How to get attribute type to match rule type?
为了开发精神x3解析器,我想使用语义动作(脚注1(。对我而言,重要的是要控制如何将属性存储到STL容器中。
这个问题是关于如何控制解析器属性的:_attr(ctx(匹配规则类型:_val(ctx(,以便可以正确分配它。也许这个问题归结为如何应用无证件transform_attribute功能。但是请与我一起阅读,看看这实际上是在示例代码中为我解决的问题。
打印对象/变量的类型
我发现非常有用的是在语义动作中打印_attr(ctx(和_val(ctx(的能力,当我正在尝试不同的语法表达式时。
因此,根据霍华德·辛南特(Howard Hinnant(的答案,我写了一个实用程序标头文件,以根据我的喜好提供这样的设施。
code below is to be put in a file named utility.h
#include <string>
#include <type_traits>
#include <typeinfo>
#include <cxxabi.h>
namespace utility
{
template<typename T>
std::string type2string()
{
std::string r;
typedef typename std::remove_reference<T>::type TR;
std::string space = "";
if ( std::is_const<TR>::value )
{ r = "const"; space = " "; }
if ( std::is_volatile<TR>::value )
{ r += space + " volatile"; space = " "; }
int status;
char* demangled =
abi::__cxa_demangle( typeid(TR).name(), nullptr, nullptr, &status );
switch ( status )
{
case 0: { goto proceed; }
case -1: { r = "type2string failed: malloc failure"; goto fail; }
case -2: { r = "type2string failed: " + std::string(typeid(TR).name()) +
" nonvalid C++ ABI name"; goto fail; }
case -3: { r = "type2string failed: invalid argument(s)"; goto fail; }
default: { r = "type2string failed: unknown status " +
status; goto fail; }
}
proceed:
r += space + demangled;
free( demangled );
/* references are without a space */
if ( std::is_lvalue_reference<T>::value ) { r += '&'; }
if ( std::is_rvalue_reference<T>::value ) { r += "&&"; }
fail:
return r;
}
}
现在实际的工作示例代码:
#include <cstddef>
#include <cstdio>
#include <cstdint>
#define BOOST_SPIRIT_X3_DEBUG
#include <boost/config/warning_disable.hpp>
#include <boost/spirit/home/x3.hpp>
#include <string>
#include <vector>
#include <utility> // this is for std::move
#include "utility.h" // to print types
namespace client
{
namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
namespace semantic_actions
{
using x3::_val; // assign to _val( ctx )
using x3::_attr; // from _attr( ctx )
struct move_assign
{
template <typename Context>
void operator()(const Context& ctx) const
{
printf( "move_assignn" );
_val( ctx ) = std::move( _attr( ctx ) );
}
};
struct print_type
{
template <typename Context>
void operator()(const Context& ctx) const
{
printf( "print_typen" );
std::string str;
str = utility::type2string< decltype( _attr( ctx ) ) >();
printf( "_attr type: %sn", str.c_str() );
// reuse str
str = utility::type2string< decltype( _val( ctx ) ) >();
printf( "_val type: %sn", str.c_str() );
}
};
}
namespace parser
{
using x3::char_;
using x3::lit;
using namespace semantic_actions;
x3::rule<struct main_rule_class, std::string> main_rule_ = "main_rule";
const auto main_rule__def = (*( !lit(';') >> char_) >> lit(';'))[print_type()][move_assign()];
BOOST_SPIRIT_DEFINE( main_rule_ )
const auto entry_point = x3::skip(x3::space)[ main_rule_ ];
}
}
int main()
{
printf( "Give me a string to test rule.n" );
printf( "Type [q or Q] to quit.n" );
std::string input_str;
std::string output_str;
while (getline(std::cin, input_str))
{
if ( input_str.empty() || input_str[0] == 'q' || input_str[0] == 'Q')
{ break; }
auto first = input_str.begin(), last = input_str.end();
if ( parse( first, last, client::parser::entry_point, output_str) )
{
printf( "Parsing succeededn" );
printf( "input: "%s"n", input_str.c_str() );
printf( "output: "%s"n", output_str.c_str() );
}
else
{
printf( "Parsing failedn" );
}
}
return 0;
}
输入始终是: abcd;
输出:
Give me a string to test rule.
Type [q or Q] to quit.
<main_rule>
<try>abcd;</try>
print_type
_attr type: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&
_val type: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&
move_assign
<success></success>
<attributes>[a, b, c, d]</attributes>
</main_rule>
Parsing succeeded
input: "abcd;"
output: "abcd"
好吧,到目前为止,一切都很好,但假设我想在分析的结果中包括半隆。我将语法线更改为:
const auto main_rule__def = (*( !lit(';') >> char_) >> char_(";"))[print_type()];
注意:我删除了语义动作[move_assign((],因为它由于不兼容的_attr和_val类型而无法编译。现在输出为:
Give me a string to test rule.
Type [q or Q] to quit.
<main_rule>
<try>abcd;</try>
print_type
_attr type: boost::fusion::deque<std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >, char>&
_val type: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&
<success></success>
<attributes>[]</attributes>
</main_rule>
Parsing succeeded
input: "abcd;"
output: ""
现在,_attr类型的boost :: fusion :: deque&lt;>不是我想要的,我只是std :: string。我不明白为什么如果我在语义动作括号内具有语法分配的完整右侧,_attr仍然不是_val类型。X3功能转换_attribute在这里有帮助吗?我应该如何应用?或者是解决此问题的另一种好方法,而无需使用Boost Fusion类界面或其他实现细节。
当前的解决方法
当前对我的解决方法是定义另一个规则,只是通过语义动作从第一个规则分配。只有_attr是std :: string type。
namespace parser
{
using x3::char_;
using x3::lit;
using namespace semantic_actions;
x3::rule<struct main_rule_class, std::string> main_rule_ = "main_rule";
x3::rule<struct main_rule2_class, std::string> main_rule2_ = "main_rule2";
const auto main_rule__def = *( !lit(';') >> char_) >> char_(";");
const auto main_rule2__def = main_rule_[print_type()][move_assign()];
BOOST_SPIRIT_DEFINE( main_rule_, main_rule2_ )
const auto entry_point = x3::skip(x3::space)[ main_rule2_ ];
}
输出:
Give me a string to test rule.
Type [q or Q] to quit.
<main_rule2>
<try>abcd;</try>
<main_rule>
<try>abcd;</try>
<success></success>
<attributes>[a, b, c, d, ;]</attributes>
</main_rule>
print_type
_attr type: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&
_val type: std::__cxx11::basic_string<char, std::char_traits<char>, std::allocator<char> >&
move_assign
<success></success>
<attributes>[a, b, c, d, ;]</attributes>
</main_rule2>
Parsing succeeded
input: "abcd;"
output: "abcd;"
我希望有一种方法而不必制定另一个规则只是为了获得_attr的类型以匹配_val。
(1(我不欣赏作者在这个图书馆中投入的隐藏聪明。因为只有一个无辜的变化可以打破应用程序。而更明确,更精致的方法将更清楚地传达发生的事情。我只需要把它从胸口拿下来。
直接答案
transform_attribute
尚未 x3(https://www.boost.org/doc/libs/1_70_0/libs/spirit/spirit/doc/doc/x3/html/index.html(可以在此处找到其Qi对应物:https://www.boost.org/doc/libs/1_70_0/libs/spirit/doc/doc/html/spirit/advanced/customize/customize/customize/transform.html。
X3功能转换_attribute在这里有帮助吗?我应该如何应用?
无论如何,这是您可以使用规则轻松访问的实现细节。我喜欢使用匿名规则来帮助这一点:
template <typename T>
struct as_type {
template <typename E>
constexpr auto operator[](E e) const { return x3::rule<struct _, T> {} = e; }
};
template <typename T>
static inline constexpr as_type<T> as;
现在您可以写
const auto main_rule__def = as<std::string> [ (*(char_ - ';') >> char_(';')) ];
活在coliru
#include <iostream>
//#define BOOST_SPIRIT_X3_DEBUG
#include <boost/spirit/home/x3.hpp>
#include <iomanip> // std::quoted
namespace client {
namespace x3 = boost::spirit::x3;
namespace ascii = boost::spirit::x3::ascii;
namespace parser {
using x3::char_;
using x3::lit;
x3::rule<struct main_rule_class, std::string> main_rule_ = "main_rule";
template <typename T>
struct as_type {
template <typename E>
constexpr auto operator[](E e) const { return x3::rule<struct _, T> {} = e; }
};
template <typename T>
static inline constexpr as_type<T> as;
const auto main_rule__def = as<std::string> [ (*(char_ - ';') >> char_(';')) ];
BOOST_SPIRIT_DEFINE(main_rule_)
const auto entry_point = x3::skip(x3::space)[main_rule_];
} // namespace parser
} // namespace client
int main() {
std::string output_str;
for(std::string const input_str : { "abcd;" }) {
auto first = input_str.begin(), last = input_str.end();
if (parse(first, last, client::parser::entry_point, output_str)) {
std::cout << "Parsing succeededn";
std::cout << "input: " << std::quoted(input_str) << "n";
std::cout << "output: " << std::quoted(output_str) << "n";
} else {
std::cout << "Parsing failedn";
}
}
}
打印
Parsing succeeded
input: "abcd;"
output: "abcd;"
从理论上讲,可能有绩效开销,但我强烈怀疑所有编译器都会在这里内联插图,因为没有什么都没有外部链接或VTABLE,并且一切都是const/constexpr。
替代方案,简化:
使用x3::raw
在这种情况下,您可能会使用现有指令获得想要的行为:x3 :: raw
活在coliru
const auto main_rule__def = x3::raw [ *(char_ - ';') >> ';' ];
不要使用rule<>
总是
仅当您有递归规则或需要对规则上的外部链接(在单独的翻译单元中定义它们(才需要。整个程序都会收缩为...
活在coliru
#include <iostream>
#include <boost/spirit/home/x3.hpp>
#include <iomanip> // std::quoted
namespace x3 = boost::spirit::x3;
namespace client::parser {
auto const entry_point = x3::raw [ *(x3::char_ - ';') >> ';' ];
}
int main() {
for(std::string const input : { "abcd;" }) {
std::string output;
if (parse(input.begin(), input.end(), client::parser::entry_point, output)) {
std::cout << "Parsing succeededn";
std::cout << "input: " << std::quoted(input) << "n";
std::cout << "output: " << std::quoted(output) << "n";
} else {
std::cout << "Parsing failedn";
}
}
}
最后 - 关于跳过
我不认为您想要char_ - ';'
(或更详细的拼写方式:!lit(';') >> char_
(。使用船长,它将穿越空格("ab cnd ;"
->" ABCD;"``(。
您可能要使规则更加限制(例如lexeme [+(graph - ';')]
,甚至简单地简单地raw[lexeme[+(alnum|'_')]
或lexeme[+char_("a-zA-Z0-9_")]
(。
请参阅Boost Spirit Skipper问题
带有char_(';'(,该属性有2个部分。两个部分都需要添加到_VAL中。类似:
namespace semantic_actions
{
using x3::_val; // assign to _val( ctx )
using x3::_attr; // from _attr( ctx )
using boost::fusion::at_c;
struct move_assign
{
template <typename Context>
void operator()(const Context& ctx) const
{
printf( "move_assignn" );
auto attr=_attr( ctx );
_val( ctx ) = at_c<0>( attr );
_val( ctx ) += at_c<1>( attr );
}
};
.
.
.
}
- C++中的严格别名规则和类型别名
- 使用C++11标准的哪些规则来确定({..})中表达式的类型
- 如何在不违反类型别名规则的情况下解释消息负载?
- 强制转换为不相关的引用类型是否违反严格的别名规则?
- POD 类型的二进制 I/O 如何不违反别名规则
- 在 Objective-C++ 中应用于__weak指针时,通过关键字推导类型"auto"规则是什么?
- 有关不完整类型的Sfinae的特殊规则
- 精神X3,如何获得属性类型以匹配规则类型
- C++20 中的严格别名规则是否允许标准 c++ unicode 字符和下划线类型之间"reinterpret
- 初始化中的模板转换运算符类型推导规则是什么?
- 将临时值存储为某种数据类型时,算术运算的标准规则是什么
- MISRA C++规则 14-5-1:在与类型关联的命名空间中声明的泛型函数模板的名称
- 本地类规则是否与c++14返回类型推导一致
- 将字符阵列施放到另一种类型中是否违反了严格的确定规则
- 要求将各种类型的各种类型传递给微控制器上的规则引擎.施放问题
- 隐式数值类型转换的规则
- 在makefile中更改构建规则以构建多种文件类型
- 用删除类型的指针的stof()/stoi()将打破严格的确定规则
- 显式类型转换vs使用类型规则
- 如何从另一个仅静态选择满足特定类型规则的索引的元组实例创建元组实例