7 action
action是用来解析完成以后执行某种动作的类,所有的action类使用起来的形式为parser[action]。
一望而知,parser类必定实现了一个operator[]。这个函数实现在基类parser中,被所有的parser继承。
template <typename ActionT>
action<DerivedT, ActionT>
operator[](ActionT const& actor) const
{
return action<DerivedT, ActionT>(derived(), actor);
}
parser::operator[]有一个参数actor,就是解析成功以后调用的动作函数,是一个function object。
返回模板类action的一个对象。
7.1 action template class
action模板类实际上是一个标准的装饰器(decorator)模式的实现。回想一下decorator的定义:继承一个类的接口,并给这个类动态透明的添加职责。
对于parser来说,它是完全无须知道action的存在的。action本身继承自parser,实现了parse接口,在parse中,在调用了parser本身的parse功能以后,以parse_result为参数调用actor.operator()(result)。
看看action::parse的实现:
template <typename ScannerT>
typename parser_result<self_t, ScannerT>::type
parse(ScannerT const& scan) const
{
typedef typename ScannerT::iterator_t iterator_t;
typedef typename parser_result<self_t, ScannerT>::type result_t;
scan.at_end(); // allow skipper to take effect
iterator_t save = scan.first;
//调用subject(parser)的parse方法做解析工作。
result_t hit = this->subject().parse(scan);
//如果解析成功,执行actor动作。通过调用scan.do_action方法实现,具体实现见后。
if (hit)
{
typename result_t::return_t val = hit.value();
scan.do_action(actor, val, save, scan.first);
}
return hit;
}
7.2 scanner::do_action
可以看出,action给parser添加的职责是调用scanner::do_action来实现的,可以回忆一下scanner的action_policy。 实际上,scanner是通过继承action_policy类来继承do_action方法的。
struct action_policy
{
template <typename ActorT, typename AttrT, typename IteratorT>
void
do_action(
ActorT const& actor,
AttrT& val,
IteratorT const& first,
IteratorT const& last) const
{
attributed_action_policy<AttrT>::call(actor, val, first, last);
}
};
调用流转到了模板类attributed_action_policy,这个模板类有两个特化版本:
// 对于有Attribute的parser而言,(比如primitives)可以推导出确定的AttrT,调用这个版本。最终调用actor.operator()(val)。这个时候,需要提供的actor是接受一个确定类型参数(parser解析得到的结果类型)的function object。
template <typename AttrT>
struct attributed_action_policy
{
template <typename ActorT, typename IteratorT>
static void
call(
ActorT const& actor,
AttrT& val,
IteratorT const&,
IteratorT const&)
{
actor(val);
}
};
//对于没有Attribute的parser(比如composite),AttrT=nil_t,调用这个版本,这个时候调用actor.operator(first, last);这个时候,需要提供的actor是接受一对迭代器的function object。
template <>
struct attributed_action_policy<nil_t>
{
template <typename ActorT, typename IteratorT>
static void
call(
ActorT const& actor,
nil_t,
IteratorT const& first,
IteratorT const& last)
{
actor(first, last);
}
};
7.3 actors
spirit中定义了很多actors,这个actor都实现了operator(AttrT)或是operator(IteratorT, IteratorT)。
spirit实现了5类actor:
实际上,这5类actor都是模板类,都有一个模板参数ActionT,而且都从ActionT继承。actor和ActionT的功能完全正交;actor的功能是决定如何操作三类对象,而ActionT提供实际的操作方式。
actor要操作的三类对象有:
1. 目的对象(actor操作的对象):这个对象一般作为actor的成员
2. 源对象(parse的结果):这个对象一般作为operator()函数的参数
3. 其他对象(这些对象作为function object的附加参数):这些对象一般作为actor的成员
通过继承模板参数类的方式:
template <typename ActionT>
struct any_actor : public ActionT { /*…*/ };
将ActionT的功能混入(mixin) actor中;通过用不同的ActionT实例化actor类,使actor得到不同的操作能力。各种ActionT与各种actor之间的任意混合,我们只需实现n种actor和m种ActionT,就能得到n*m个actor<ActionT>类。
1. ref_actor:只持有目的对象,在operator()中忽略源对象。适合混入只操作一个单一引用的ActionT,比如increment_action。
2. ref_value_actor:最为常用,持有目的对象,并在operator()中引用源对象;适合混入标准ActionT,操作两个引用对象。比如assign_action。
3. ref_const_ref_actor:持有目的和其他对象,在operator()忽略源对象。当然源对象也可以作为其他对象使用,不过需要在构造的时候传入,并不是在operator()中引用。混入的ActionT需要接受两个引用参数。
4. ref_const_ref_value_actor:持有目的对象和其他对象,并且在operator()中引用源对象,混入的ActionT接受三个参数。
5. ref_const_ref_const_ref_actor:持有目的对象和两个其他对象,但是在operator()忽略源对象,混入的ActionT需接受三个参数。