检测Spirit语义动作中的参数类型
Detecting the parameter types in a Spirit semantic action
一般情况:我不明白为什么Spirit语法/语义操作没有编译。
有时,编译器会抱怨赋值或类型不兼容,而我不知道出了什么问题。问题主要发生在两个方面:
- 预测规则/表达式的合成属性类型
- 因此,预测哪些类型的属性可以合法地定义为规则的公开属性(依赖于内置转换、融合适配器或Spirit自定义点)
- 匹配我的语义操作的参数类型,以便
- 编译器将能够编译函数调用
- 调用不会在流程中调用不必要的隐式转换
编译器错误不完全可以处理,要么是文档错误,要么是我误解了它
有没有办法找到到底Spirit传递到我的语义动作中
示例代码:
struct mybase { int a,b; };
struct myderived : mybase { int c,d; };
BOOST_FUSION_ADAPT_STRUCT(mybase, (int,a)(int,b));
BOOST_FUSION_ADAPT_STRUCT(myderived, (int,a)(int,b)(int,c)(int,d));
auto base_expr = int_ >> int_; // avoids assigning to struct attribute
rule<decltype(f), mybase() , space_type> base_ = int_ >> int_;
rule<decltype(f), myderived(), space_type> derived_ = base_ >> int_ >> int_;
myderived data;
bool ok = phrase_parse(f,l,derived_,space,data);
这段代码无法编译,有大量无法理解的错误。
(大致改编自spirit general list上的一篇帖子)
我可以解决这个特殊情况下的问题(事实上我们讨论了列表上的选项),但实际上"神秘"错误在Boost Spirit中更频繁地出现,这会很好以处理一般类别的问题。
您的第一个资源应该是优秀的精神文档详细说明给定解析器的合成属性原语、运算符或指令。参见参考部分《灵气经》。
在某些情况下,我已经开始将重点从"试图窥探编译器错误列表中的信息"到"正在主动查询Spirit类型"。我使用的技术是多态可调用类型(参见Spirit/Fusion文档)。
这里有一个使用GCC特定API来漂亮地打印它检测到的类型:
函数what_is_the_attr
#include <cxxabi.h>
#include <stdlib.h>
#include <string>
#include <iostream>
template <typename T> std::string nameofType(const T& v) {
int status;
char *realname = abi::__cxa_demangle(typeid(v).name(), 0, 0, &status);
std::string name(realname? realname : "????");
free(realname);
return name;
}
struct what_is_the_attr {
template <typename> struct result { typedef bool type; };
template <typename T> bool operator()(T& attr) const {
std::cerr << "what_is_the_attr: " << nameofType(attr) << std::endl;
return true;
}
};
示例用途:检测合成属性类型
您可以使用它来准确地检测解析器表达式最终实际上是:
template <typename Exp>
void detect_attr_type(const Exp& exp)
{
using namespace boost::spirit::qi;
const char input[] = "1 2 3 4";
auto f(std::begin(input)), l(std::end(input)-1);
bool dummy = phrase_parse(
f, l,
exp [ what_is_the_attr() ],
space);
}
(注意:这显示了该方法的局限性-该技术假设有一个"否则"的工作语法,你知道如何传递一个输入满足表达式足以触发语义动作。在大多数情况下,当你在Spirit解析器上进行黑客攻击时,这将是真的,尽管)
让我们测试一下。例如,让我们看看中等复杂度,并且封装在qi::raw[]
指令中:
int main()
{
detect_attr_type( -(int_ >> *int_) );
detect_attr_type( raw [ -(int_ >> *int_) ] );
}
输出:
what_is_the_attr: boost::optional<boost::fusion::vector2<int, std::vector<int, std::allocator<int> > > >
what_is_the_attr: boost::iterator_range<char const*>
在底部,我们将把这个应用到操作中的问题。
示例用法:检测传递到语义动作中的类型
我们可以使用相同的一元函数对象(what_is_the_attr
)来检测然而,这些语义操作可以采用任意数量的参数,因此我们需要以偏概全。如果不是因为variadic,这将是一项乏味的工作模板(用于c++0x的woot!):
struct what_are_the_arguments {
template <typename...> struct result { typedef bool type; };
template <typename... T> bool operator()(const T&... attr) const {
std::vector<std::string> names { nameofType(attr)... };
std::cerr << "what_are_the_arguments:nt";
std::copy(names.begin(), names.end(), std::ostream_iterator<std::string>(std::cerr, "nt"));
std::cerr << 'n';
return true;
}
};
对上述测试用例的重复显示Spirit实际上试图调用带有三个参数的语义操作(如有记录):
what_are_the_arguments:
boost::optional<boost::fusion::vector2<int, std::vector<int, std::allocator<int> > > >
boost::spirit::unused_type
bool
what_are_the_arguments:
boost::iterator_range<char const*>
boost::spirit::unused_type
bool
但好的是,您现在可以将其应用于任何语义操作:
template <typename ExpWSA> void test(const ExpWSA& exp)
{
const char input[] = "1 2 3 4";
auto f(std::begin(input)), l(std::end(input)-1);
qi::phrase_parse(f, l, exp, qi::space);
}
int main()
{
test(-(-double_ >> *int_) [ phx::bind(what_are_the_arguments(), _1, _2, _0, phx::ref(std::cout), 42) ]);
}
打印,对于这个(抱歉)非常做作的例子:
what_are_the_arguments:
boost::optional<double>
std::vector<int, std::allocator<int> >
boost::fusion::vector2<boost::optional<double>, std::vector<int, std::allocator<int> > >
std::ostream
int
应用于OP
derived
规则的合成属性是而不是与int_>>int_>>int_>>int_
:相同
auto base_expr = int_ >> int_; // avoids assigning to struct attribute
rule<const char*, mybase(), space_type> base_ = base_expr;
test(base_ >> int_ >> int_ [ what_is_the_attr() ] );
test(base_expr >> int_ >> int_ [ what_is_the_attr() ] );
将打印
what_is_the_attr: boost::fusion::vector3<mybase, int, int>
what_is_the_attr: boost::fusion::vector4<int, int, int, int>
这是你的问题。我们在原始线程中讨论了一些基于此诊断的解决方案(请参阅此处的其他答案)。但这篇文章应该有助于回答一般情况下的问题。
完整代码列表
集成形式,用gcc 4.6.1--std=c++0x和boost 1_48:编译
#include <cxxabi.h>
#include <iostream>
#include <iterator>
#include <stdlib.h>
#include <string>
#include <vector>
template <typename T> std::string nameofType(const T& v)
{
int status;
char *realname = abi::__cxa_demangle(typeid(v).name(), 0, 0, &status);
std::string name(realname? realname : "????");
free(realname);
return name;
}
struct what_is_the_attr {
template <typename> struct result { typedef bool type; };
template <typename T> bool operator()(T& attr) const {
std::cerr << "what_is_the_attr: " << nameofType(attr) << std::endl;
return true;
}
};
struct what_are_the_arguments {
template <typename...> struct result { typedef bool type; };
template <typename... T> bool operator()(const T&... attr) const {
std::vector<std::string> names { nameofType(attr)... };
std::cerr << "what_are_the_arguments:nt";
std::copy(names.begin(), names.end(), std::ostream_iterator<std::string>(std::cerr, "nt"));
std::cerr << 'n';
return true;
}
};
#include <boost/fusion/adapted.hpp>
#include <boost/spirit/include/phoenix.hpp>
#include <boost/spirit/include/qi.hpp>
struct mybase { int a,b; };
struct myderived : mybase { int c,d; };
BOOST_FUSION_ADAPT_STRUCT(mybase, (int,a)(int,b));
BOOST_FUSION_ADAPT_STRUCT(myderived, (int,a)(int,b)(int,c)(int,d));
template <typename ExpWSA>
void test(const ExpWSA& exp)
{
using namespace boost::spirit::qi;
const char input[] = "1 2 3 4";
auto f(std::begin(input)), l(std::end(input)-1);
bool dummy = phrase_parse(f, l, exp, space);
}
int main()
{
using namespace boost::spirit::qi;
// Diagnostics for the OP case
auto base_expr = int_ >> int_; // avoids assigning to struct attribute
rule<const char*, mybase(), space_type> base_ = base_expr;
// Derived rule, different formulations
test((base_ >> int_ >> int_) [ what_is_the_attr() ] );
test((base_expr >> int_ >> int_) [ what_is_the_attr() ] );
// Applied to attribute types
test(raw [ -(int_ >> *int_) ] [ what_is_the_attr() ] );
test(-(int_ >> *int_) [ what_is_the_attr() ] );
// applied to semantic actions - contrived example
namespace phx = boost::phoenix;
test(-(-double_ >> *int_) [ phx::bind(what_are_the_arguments(), _1, _2, _0, phx::ref(std::cout), 42) ]);
return 0;
}
为了清楚起见,这里的错误是base_ >> int_ >> int_
被用作创建myderived
的规则的表达式,并且由于base_
被固定为类型mybase
,我们必须从一个mybase
和两个int
创建一个myderrived
,但没有什么可以告诉Spirit如何做到这一点。
您可以通过定义一个接受任何参数的函子,让boost打印出boost在解析base_ >> int_ >> int_
时创建的值的类型,并告诉您它们是什么(以下代码改编自他在SO聊天中发布的一些代码):
struct what_is_the_attr
{
template <typename> struct result { typedef bool type; };
template <typename T>
static void print_the_type()
{
std::cout << " ";
std::cout << typeid(T).name();
if(std::is_const<typename std::remove_reference<T>::type>::value)
std::cout << " const";
if(std::is_rvalue_reference<T>::value)
std::cout << " &&";
else if(std::is_lvalue_reference<T>::value)
std::cout << " &";
}
template <typename Th, typename Th2, typename... Tt>
static void print_the_type()
{
print_the_type<Th>();
std::cout << ",n";
print_the_type<Th2, Tt...>();
}
template <typename... Ts>
void operator()(Ts&&...) const
{
std::cout << "what_is_the_attr(n";
print_the_type<Ts...>();
std::cout << ")" << std::endl;
}
};
然后要使用它,请在错误规则的初始值设定项上的语义操作中使用上面的actor:
std::string input = "1 2 3 4";
auto f(std::begin(input)), l(std::end(input));
rule<decltype(f), mybase() , space_type> base_ = int_ >> int_;
rule<decltype(f), myderived(), space_type> derived_ = (base_ >> int_ >> int_)[what_is_the_attr()];
myderived data;
bool ok = phrase_parse(f,l,derived_,space,data);
注意,不能将自动属性传播与%=
一起使用(除非从规则的声明类型中删除公开的属性类型)
运行该程序将生成一个编码类型,可以使用c++filt -t
:Live On Coliru对其进行解码
$ g++ 9404189.cpp -std=c++0x
$ ./a.out |c++filt -t
what_is_the_attr(
boost::fusion::vector3<mybase, int, int> &,
boost::spirit::context<boost::fusion::cons<boost::spirit::unused_type&, boost::fusion::nil>, boost::fusion::vector0<void> > &,
bool &)
第一行boost::fusion::vector3<mybase, int, int>
至少告诉您boost正在尝试从mybase
、int
和int
类型的3个对象创建返回类型。
- 扩展C++生成的代码的模板参数类型名称
- 如何在 c++ 中定义接受不同参数类型的函数向量?
- 在 C++ 中运行时调用模板时,是否可以切换模板的参数类型?
- 将函数参数类型声明为 auto
- 将函数的参数 - 签名从使用 'std::function<T>' 转换为模板参数类型
- 在 C++17 中调用具有不同参数类型的构造函数
- 具有先前参数类型匹配的参数包
- 我想知道为什么"std::unique_ptr<int> foo(新 int)"是合法的,因为"std::<int>unique_ptr"要求输入参数类型应该是"int"?
- 将可变参数类型列表的扩展打包为复杂类型的初始值设定项列表 - 合法吗?
- MSVC 错误:4 个重载中的任何一个都无法转换所有参数类型
- 使用constexpr + auto作为返回和参数类型的奇怪类型推导
- 如何从第一个参数推断第二个参数类型?
- C++模板函数中,指定回调函子/lambda 的参数类型,同时仍允许内联?
- 如何用不同的参数类型和数字回调函数
- C++stoi:这两个重载都无法转换所有参数类型
- 为什么std::{container}::template不能推导其参数类型
- 为模板参数类型中的新对象分配内存
- 为指向成员模板参数的指针推导额外模板参数类型的紧凑方式
- 使用std::conditional中的模板来确定函数参数类型
- C++中的短参数类型