《AspectC++ Language Reference》chapter 3: Match Expressions & chapter 4: Predefined Pointcut Functions

第三章 匹配表达式

匹配表达式用来描述Aspect C++程序中,静态的已知程序实体。可以用来匹配函数,也可以用来匹配类型。这里class也被视为一种类型。

对函数匹配而言,与匹配表达式被分解为:函数类型样式,作用域样式,以及名字样式。

例子:函数匹配表达式的类型、作用域、和名字部分

"const % Puma::...::parse_% (Token *)"

上例中的匹配表达式描述了在比较函数名字时的需求:

名字:函数名字要匹配名字样式:“parse_%”;

作用域:函数的作用域需要匹配:“Puma::…::”;

类型:函数类型需要匹配:“const %(Token *)”

class和其他类型不需要这样的分解。例如,类型名:“Puma::CCParser”已经足够用来描述一个class,因为这和class的名字一样。

如果实体和匹配表达式完全匹配,那么这个实体就是匹配表达式所定义的集合中的一员。

用来解析匹配表达式的语法参见P33附录B。以下几小节分别介绍名字、作用域、以及类型的匹配机制。注意,名字和作用域匹配不仅用于匹配函数名,还用于匹配类型名,例如class。

3.1 名字匹配
3.1.1 简单名字匹配

如果比较的名字是普通的C++标识符,那么名字匹配就非常简单,不值一提。如果“名字样式”中不包含通配符“%”,那么就精确匹配名字。否则,通配符可以匹配任意字符,包括空字符。

例子:简单名字匹配

Token 只匹配Token;

% 匹配任意名字;

pars_% 匹配任意以“parse_”开头的名字,例如“parse_declarator”或”parse_”;

parse_%_id% 匹配例如“parse_type_id”,“parse_private_identifier”等名字;

%_token 匹配所有以”_token”结尾的名字,例如“start_token”,“end_token”,以及“_token”。

3.1.2 运算符重载函数和类型转换函数的名字匹配

运算符重载函数和类型转换函数的名字匹配相对复杂。他们都通过名字样式“%”匹配,尽管如此,如果样式以”operator”开头,除了“%”,还可以用不同的名字样式。样式:“operator %”可以匹配任何运算符重载函数和类型转换函数。

C++中已经定义了允许重载的运算符集合,这些运算符可以跟在“operator”之后,用于匹配运算符函数。名字样式中的运算符名字不能包含通配符,如果要重载运算符“%”或“%=”,那么名字样式中应该使用“%%”或“%%=”。

例子:运算符名字样式

operator % 匹配任何运算符重载函数名(以及任何类型转换函数名);

operator += 只匹配运算符“+=”函数名;

operator %% 匹配运算符“%”函数名。

类型转换函数没有实际名字。例如,class C中定义的类型转换函数“operator int *()”,定义了从C到int *类型的转换。为了匹配类型转换操作符,名字样式中,“operator”之后会包含类型样式。类型匹配机制在3.3节中阐述。

例子:类型转换函数名字样式

operator % 匹配任何运算类型转换函数名;

operator int * 匹配转换到int *的类型转换函数;

operator %* 匹配转换到指针的类型转换函数,指针可以是任何类型的。

3.1.3 构造函数和析构函数

名字样式不能用于匹配构造函数或析构函数。

3.1.4 作用域约束

匹配表达式中,可以在名字样式的前面加上作用域样式。作用域样式(见3.2节)用于描述对匹配实体定义范围的约束。如果没有给出作用域样式,就匹配定义在全局作用域中的函数或类型。

3.2 作用域匹配

作用域样式描述了对定义域的约束。作用域样式是由“::”分隔的一系列名字样式(或任意作用域序列样式“…”),例如“Puma::…::”。作用域样式以“::”结尾,不能以“::”开头,因为作用域样式是在全局作用域中进行解析的。定义域可以是名字空间,也可以是类。

如果其他部分都匹配,则作用域样式对函数或类型的定义域进行匹配。匹配的限定名与全局域关联,不能以”::”开头。”…”样式匹配任何(包括空的)作用域名字序列。

例子:作用域样式

…:: 匹配任何定义域,包括全局域;

Puma::CCParser:: 精确匹配作用域:“Puma::CCParser”

…::%Compiler%:: 匹配任何作用域中,匹配“%Compiler%”名字的作用域;

Puma::…:: 匹配定义在Puma中(或Puma本身)的作用域,包括Puma本身,Puma是类或名字空间。

3.3 类型匹配
3.3.1 匹配机制

C++类型可用树表示。例如,函数类型int(double)是一个函数类型的节点,他有两个孩子,一个事int节点,一个是double节点,这两个孩子节点都是树的叶节点。

匹配表达式中使用的类型也可以被理解为树。作为普通C++类型的扩展,匹配表达式中的类型也可以使用通配符“%”、名字样式、以及作用域样式。类型样式中的单个通配符在树中表示“任意类型节点”。

例子:包含通配符的类型样式

% 匹配任何类型;

void (*)(%) 匹配任何函数指针类型,该函数指针所指向的函数具有单个参数,返回值为void;

%* 匹配任何类型的指针;

3.3.2 命名的类型的匹配(Matching of Named Types)

类型样式也可以包含名字样式和作用域样式。成为树中命名的类型节点,匹配任何union、struct、class或枚举类型,前提是这些类型的名字样式和作用域样式都已经匹配。

3.3.3 “指向成员的指针”类型的匹配(Matching of “pointer to member” Types)

指向成员的指针的样式也包含作用域样式,例如:“% (Puma::CSyntax::*) ()”。这里的作用域样式是必须要有的。

3.3.4 被限定词修饰的类型的匹配(const/volatile)

许多C++类型被限定为const或volatile。在类型样式中,也可以使用这些限定词作为约束。如果类型样式中没有const或volatile限定,样式也匹配具有这些限定词的类型。

例子:包含const和volatile的类型样式

% 匹配任意类型,包括被const或volatile修饰的类型;

const % 只匹配被const修饰的类型;

% (*)() const volatile 匹配被const和volatile修饰的函数指针类型。

3.3.5 处理转换函数类型

类型转换函数的结果类型是特殊的未定义类型。这个未定义类型只和“任意类型”和“未定义类型”匹配。

3.3.6 函数类型样式中的省略号

在函数参数列表中,类型样式“…”用于匹配任意(包括空)的类型列表。参数类型列表中,“…”之后不能再跟其他参数类型样式。

3.3.7 虚函数的匹配

函数类型匹配表达式可以包括关键字“virtual”。这是,函数类型匹配表达式只匹配虚函数或纯虚函数。和const、volatile一样,virtual关键字也是一种约束。没有virtual关键字的函数类型匹配表达式既可以匹配虚函数,也可以匹配非虚函数。

例子:包括virtual的类型样式

virtual % …::%(…) 匹配任意作用域中的虚函数和纯虚函数;

% C::%(…) 匹配C类中的所有成员函数,包括虚函数。

3.3.8 参数类型调整

类型样式中的参数类型根据正常C++规则进行调整,即,数组和函数类型被转换为给定类型的指针,且限定词const/volatile被移除。另外,如果类型样式中的参数列表是void,也就是诶有参数,那么就会被转化为空参数类型列表。

第四章 预定义的Pointcut函数

本章介绍Aspect C++中定义的所有pointcut函数。介绍了每个函数所需要的参数类型和返回值类型。“N”表示name pointcut,“C”表示code pointcut。clip_image002:Code (any, only Call, only Execution, only Set, only Get); clip_image004:Names (any, only Namespace, only Class, only Function, only Type)

4.1 类型

base(pointcut) clip_image006

返回所有pointcut类的所有基类;

derived(pointcut) clip_image008

返回pointcut中的所有类和它们的派生类。

例子:类型匹配

代码中可能包含以下类层次:

class Shape { ... };

class Point : public Shape { ... };

...

class Rectangle : public Line, public Rotatable { ... };

通过下面这个aspect,会向上面这个类层次中的某些类中添加特殊属性。

aspect Scale {

pointcut scalable() = (base("Rectangle") && derived("Point")) || "Rectangle";

advice "Point" : baseclass("Scalable");

advice scalable() : void scale(int value) { ... }

};

上面这个pointcut指定了类Point、Rectangle以及所有派生自Point且是Rectangle直接或间接基类的类。第一个advice使Point获得一个新基类。第二个advice为pointcut中的所有类增加了一个函数scale。

4.2 控制流(Control Flow)

cflow(pointcut) C->C

捕获动态执行环境中join point的发生。参数不能包含环境变量绑定(参见4.6),不能包含其他需要在运行时计算的pointcut函数,例如cflow(pointcut)。

例子:依赖控制流的advice执行

下例展示如何使用cflow函数。

class Bus {

void out (unsigned char);

unsigned char in ();

};

考虑上例中所示的Bus类。它可能是os内核的一部分,用于通过I/O总线访问外围设备。成员函数in()和out()的执行不能被中断,否则会打断总线通信的计时。因此需要实现一个中断同步aspect,从而禁止in()和out()执行过程中的中断。

aspect BusIntSync {

pointcut critical() = execution("% Bus::%(...)");

advice critical() && !cflow(execution("% os::int_handler()")) :around() { /*在in() out()执行且非中断处理时,先关中断,执行,再开中断。*/

os::disable_ints();

tjp->proceed();

os::enable_ints();

}

};

因为中断处理(interrupt)也会调用总线驱动,所以不能不管什么时候都关中断。因此在pointcut表达式中引入cflow()函数,从而为advice代码的执行添加运行时条件。advice只有在控制流不是来自中断处理os::int_handle()的时候才会执行,因为这是不能中断的,而且如果是中断处理中,执行advice代码中的os::enable_ints()会造成中断过早的被打开。

4.3 范围(Scope)

within(pointcut) N->C

筛选在pointcut中的joint point,pointcut可以是函数或类。

例子:在范围中匹配

aspect Logger {

pointcut calls() = call("void transmit()") && within("Transmitter");

advice calls() : around() {

cout < < "transmitting ... " < < flush;

tjp->proceed();

cout < < "finished." < < endl;

}

};

这个Aspect向类Transmitter中的成员函数transmit中插入代码,记录对transmit的调用。

4.4 函数

call(pointcut) N->Cc

它所指定的join point是:pointcut指定的实体被调用。这个pointcut既可以包含函数名也可以包含类名。如果是类名,那么所有对该类方法的调用都是join point。

execution(pointcut) clip_image010

它所指定的join point是:pointcut指定的实体被实现。这个pointcut既可以包含函数名也可以包含类名。如果是类名,那么所有对该类方法的实现都是join point。

例子:函数匹配

这个aspect向程序中织入调试代码,以检测方法是否是对null指针的调用以及参数是否为null。

aspect Debug {

pointcut fct() = "% MemPool::dealloc(void*)";

pointcut exec() = execution(fct());

pointcut calls() = call(fct());

advice exec() && args(ptr) : before(void *ptr) {

assert(ptr && "argument is NULL");

}

advice calls() : before() {

assert(tjp->target() && "’this’ is NULL");

}

};

第一个advice在函数dealloc执行前检查函数的参数是否为NULL。第二个advice检查dealloc是否是对null对象进行的调用。通过检查call的target实现。

4.5 对象构造和析构

construction(pointcut) clip_image012

join point是:pointcut所给定的class的对象被构造。construction join point在所有基类和成员construction join point之后开始。construction(pointcut)可以认为是构造函数的执行。但即使没有显示定义构造函数,construction joinpoint的advice也会执行。construction join point拥有参数和参数类型,可以被引用或筛选,例如,通过使用args函数。

destruction(pointcut) clip_image014

join point是:pointcut所给定的class的对象被析构。destruction join point在成员和基类的destruction join point之前结束。destruction join point可以被视为析构函数的执行,尽管析构函数可以不显式定义。destruction join point没有参数。

例子:计算实例对象个数

下面这个aspect计算有多少个ClassOfInterest类的实例对象被创建和解析。

aspect InstanceCounting {

// the class for which instances should be counted

pointcut observed() = "ClassOfInterest";

// count constructions and destructions

advice construction (observed ()) : before () { _created++; }

advice destruction (observed ()) : after () { _destroyed++; }

public:

// Singleton aspects can have a default constructor

InstanceCounting () { _created = _destroyed = 0; }

private:

// counters

int _created;

int _destroyed;

};

这个aspect的实现非常直观。两个计数器在aspect构造函数中被初始化,并分别在construction/destruction advice中被递增。将observed()定义为纯虚pointcut,方便以后重用。

4.6 上下文(Context)

that(type pattern) N->C

返回join point,在这个join point处,this指针指向一个对象,这个对象的类型是与type pattern中描述的匹配的类型。

target(type pattern) N->C

返回join point,在这个join point处,call的目标对象是一个对象,这个对象的的类型是与type pattern中描述的匹配的类型。

result(type pattern) N->C

返回join point,在这个join point处,call/execution的结果类型是一个对象,这个对象的的类型是与type pattern中描述的匹配的类型。

args(type pattern,…) (N,…)->C

args的参数列表包含了type pattern,这些type pattern用于筛选join point。例如,用于包含声明式匹配的函数调用或函数执行中。

除了type pattern,也可以传递绑定上下文信息的变量名字(上下文变量)。在这种情况下,变量的类型用于类型匹配。上下文变量需要在before()、after()、arount()的参数列表中声明,并可以在advice体内像使用函数参数一样去使用这些变量。

that()和target()这两个函数比较特别,因为他们会引起运行时类型检查。args()和result()函数在编译期间求值。

4.7 逻辑操作符

pointcut && pointcut (N,N) -> N, (C,C) -> C 且关系

pointcut || pointcut (N,N) -> N, (C,C) -> C 或关系

! pointcut N -> N, C -> C 非关系

  • 1
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值