《AspectC++ Language Reference》Chapter 2 Basic Concepts

这是我对pure system的《AspectC++ Language Reference》第二章基本概念的翻译。蓝色表示重点概念,黄色是我不太清楚或我的理解。不知道理解的是否正确。希望csdn上有同学可以一起探讨

第二章 基本概念

2.1   Pointcuts

AspectC++中,通过Aspects以模块化的方法实现横切点。基于这一点,AspectC++语言中最重要的元素就是pointcutPointcut描述了join point的集合,该集合决定了aspect作用的条件。因此join point既可以是函数、属性、类型、变量,也可以是join point开始作用的点,例如到达指定的代码处。根据pointcut类型,pointcu可以在编译时或运行时作用。

2.1.1    匹配表达式

Aspect C++中有两种类型的pointcutcode pointcutname pointcutName pointcut描述一系列静态的已定程序入口,例如类型、属性、函数、变量或名字空间。name pointcut基于匹配表达式。匹配表达式可以被理解为搜索模式。在该搜索模式中,字符“%”为通配符,”用来匹配函数声明中任何数目的参数。匹配表达式是用引号勾住的字符串。

例子:匹配表达式(name pointcut

"int C::%(...)"

       匹配所有类C中返回值为int的成员函数;

"%List"

       匹配任意以“List”为结尾的类、结构、联合或枚举;

"% printf(const char *, ...)"

       匹配全局域中名字为print、拥有至少一个const char *型形参、返回任意类型的函数;

"const %& ...::%(...)"

       匹配所有返回值为const reference的函数;

 

匹配表达式根据定义域、类型和名字选择程序入口点。匹配表达式的细节描述见第三章。

2.1.2    pointcut 表达式

code pointcutpointcut的另一种类型。code pointcut可以指函数的调用点或执行点。必须通过name pointcut才能创建code pointcut,因为AspectC++中定义join point时都需要一个名字。pointcut函数是例如:within(pointcut)这样的东西, pointcut需要在调用该函数之前定义。在pointcut表达式中包含了pointcut函数。

name pointcutcode pointcut通过代数运算符“&&”、“||”和“!”组合。

例子:pointcut表达式

"%List" && !derived("Queue")

       描述以List结尾、不派生自基类Queue的所有类;

call("void draw()") && within("Shape")

       描述所有对类Shapevoid draw()方法的调用;

2.1.3    join point的类型

由于Aspect C++中有两种类型的pointcut,因此也有两种类型的join point(code&name)。这两种类型间的区别和联系通过以下代码说明:

 

class Shape;

void draw(Shape&);

namespace Circle {

typedef int PRECISION;

class S_Circle : public Shape {

PRECISION m_radius;

public:

...

void radius(PRECISION r) { m_radius=r; }

};

void draw(PRECISION r) {

S_Circle circle;

circle.radius(r);

draw(circle);

}

}

execution join point与可执行函数名关联。纯虚函数不可执行,因此可执行join pointadvice代码不能应用于纯虚函数。但纯虚函数的调用,即这类函数作为call join point是可以的。

call join point与两个名字相关:源(调用者)名字和调用的目标函数(被调用者)的名字。由于同一个函数中会有很多函数调用,每个函数名可以被关联到一系列的call join pointconstruction join point为当创建实例时。同理,destruction join point对应于析构实例。

2.1.4    pointcut声明

Aspect C++中利用pointcut声明给pointcut表达式指定名字,方便pointcut表达式重用。poitncut声明的被允许的情况和C++中的声明一样。因此C++中的名字查找和继承规则同样适用于pointcut声明。

pointcut声明以关键字pointcut标识。

例子:pointcu声明

pointcut lists() = derived("List");

声明后,程序中需要derived(“List”)的地方就可以用lists表示。

 

通过pointcut声明,可定义纯虚pointcut,因此可以实现可重用的抽象aspect。纯虚pointcut的语法和一般pointcut的相同,但要在关键字pointcut前面加virtual,且pointcut表达式为0

例子:纯虚pointcut声明

pointcut virtual methods() = 0;

methods为纯虚pointcut,需要在派生aspect中重新定义为实际的pointcut表达式。

2.2    Slices

sliceC++语言元素中定义一个范围的段落。advice可利用slice扩展程序的静态结构。例如,利用adviceclass类型slice中的元素可被加到一个或多个目标类中。下例说明如何声明class类型的slice

例子:class类型slice的声明

slice class Chain {

Chain *_next;

public:

Chain *next () const { return _next; }

};

2.3     Advice代码

advice代码可绑定到code join pointadvice代码可被理解成aspect中的一个活动,当到达程序中相应code join point时,就会触发这个活动。advice代码活动可以发生在code join point到达之前、之后、或者之前和之后。Aspect C++语言通过advice声明指定advice代码。advice声明以关键字advice打头,后跟pointcut表达式,pointcut表达式定义了advice代码活动的地方和活动的条件。

例子:advice声明

advice execution("void login(...)") : before() {

cout < < "Logging in." < < endl;

}

pointcut表达式后的代码段“:before()”表明,advice代码再code join point到达前执行。“:after()”同理。“:around()”表示执行advice代码,不执行code join point代码。这种情况下,如果需要执行code join point代码,需要显示的通过proceed()指明。程序中的代码没有访问advice代码的权限。

除了单纯描述join pointpointcut也可以绑定表示join point的上下文信息的变量(我想也就是函数参数吧)。这样advice代码就可以访问这个函数的实参了。

例子:可以访问上下文信息的advice声明

pointcut new_user(const char *name) = execution("void login(...)") && args(name);

advice new_user(name) : before(const char *name) {

cout < < "User " < < name < < " is logging in." < < endl;

}

上例中,首先,定义new_user为包含上下文变量“name”的pointcut。这意味着,每当到达new_user所规定的join point时,都会被提供const char *类型的变量。表达式中的argspointcut函数,它传递程序中所有包含const char *类型参数的join point。因此和execution join point联系的args(name)会绑定name到函数login的第一个(并且只绑定到这一个)参数。

上例中advice声明将advice代码绑定到一个事件,这个事件就是:到达new_user所描述的join point。持有join point的参数实际值的上下文变量(也就是name)必须在beforeafteraround中作为正式参数声明。该参数(name)可在advice代码中作为普通函数参数使用。

除了pointcut函数args,还有thattargetresult可以绑定上下文变量。这些pointcut函数还可以根据变量类型进行筛选。例如例子中的args筛选具有const char *类型参数的join point

2.3.1    Introduction

AspectC++支持的第二种adviceintroductionintroduction用于扩展程序代码和数据。下例中,对两个class扩展了属性和方法。

例子:introduction

pointcut shapes() = "Circle" || "Polygon";

advice shapes() : slice class {

bool m_shaded; 1         

void shaded(bool state) {

m_shaded = state;

}

};

       和普通advice声明相同,introduction也以关键字advice打头。如果advice后的pointcutname poitncut,那么“:”之后的slice声明就被引入(introduce)到pointcut所规定类或aspects中。使用引入的代码和使用普通程序代码一样。introduction中的advice代码拥有join point处程序代码的完全访问权限,即,引入到class中的方法能够访问该class的私有成员。

       slice还能用于为class引入新基类。下例中第一行使得以”Object”结尾的类都派生自类”MemoryPool” ”MemoryPool”可能通过从在newdelete运算符实现了独立的内存管理。继承自MemoryPool的类必须重定义纯虚函数releaserelease是实现内存管理的一部分。这在所有pointcut中规定的类的第二行完成。

例子:引入基类

advice "%Object" : slice class : public MemoryPool {

virtual void release() = 0;

}

2.3.2    Advice顺序

如果对同一个join point有多个advice,且advice代码间存在依赖性(aspect交互),那么就需要定义advice的执行顺序。下例说明了如何定义advice顺序。

例子:advice排序

advice execution("void send(...)") : order("Encrypt", "Log");

EncryptLogaspect,如果void send(…)函数被执行时,这两个aspect中都有advic需要执行,那么Encrypt中的具有高优先级。关于advice的顺序详见第八章。

2.4  Aspect

AspectC++中的aspect集合了introductionadvice代码,以模块化方式实现普通横切关注点。aspect是管理普通状态信息的。aspect通过声明呈现,好像C++class概念的扩展一样。aspect的基本结构定义和C++class定义一样,除了用aspect替换classstructunion。因此,aspect也有属性,也有方法,也能继承自其它class或其他aspect

例子:aspect声明

aspect Counter {

static int m_count;

Counting() : m_count(0) {}

 

pointcut counted() = "Circle" || "Polygon";

advice counted() : class Helper {

Helper() { Counter::m_count++; }

} m_counter;      /*说明:这个advice为每个join point加入一个data成员:m_counter,这个成员是Helper类类型的对象,它在初始化的时候调用Helper构造函数,因此m_counter就会加1*/

 

advice execution("% main(...)") : after() {

cout < < "Final count: " < < m_count < < " objects" < < endl;

}

};

上例用于计算Circle Polygon 这两种class的实例对象的数目。因此在pointcut所描述的class中引入一个属性(m_counter),用来在每次构造函数调用的时候使全局量m_count1。通过在main函数上应用advice码,可以在程序结束时显示对象实例个数。

计算对象实例个数的例子也可以被重新改写为抽象的aspect,这样可以存档以便重用。只需要将pointcut声明为纯虚就可以了。

例子:抽象aspect

aspect Counter {

static int m_count;

Counting() : m_count(0) {}

pointcut virtual counted() = 0;

...

};

这样以后可以重用这个计数功能,只要通过继承Counter,并重新实现counted,使他指向实际的pointcut表达式。

例子:重用抽象aspect

aspect MyCounter : public Counter {

pointcut counted() = derived("Shape");

};

2.4.1    aspect实例化

默认情况下,Aspect C++中的aspect被自动实例化为全局对象。这样做的思想是:aspect也能够提供全局程序属性,因此应该是永远可访问的。但是,在一些特殊情况下,可能需要改变这种全局的行为,例如,在操作系统环境中,可能需要每个进程(或线程)都实例化一个aspect对象。

默认的实例化机制可通过定义静态方法“aspectof”改变。aspectof可返回aspect的实例。

例子:使用aspectof获得aspect实例

aspect ThreadCounter : public Counter {

pointcut counted() = "Thread";

 

advice counted() : ThreadCounter m_instance;  /*给每个Thread引入一个m_instance,它是aspect的实例*/

static ThreadCounter *aspectof() {      //为什么返回的是个指针呢?

return tjp->target()->m_instance;

}

};

Thread中引入m_instance保证了每个thread对象都有一个aspect实例。通过调用aspectof,可以获得该实例,这对访问advice代码和aspect成员非常关键。aspectof中的代码拥有访问实际join point的能力。(类似singleton设计模式)

2.5   运行时支持

2.5.1    advice代码的支持

对一些aspect而言,仅仅访问上下文变量(函数参数)或许不能获得关于join point的足够多的信息。例如,用于完整记录程序中函数调用的控制流aspect,会需要关于函数参数和参数运行时类型的信息,才能产生类型兼容的输出。

Aspect C++中,这些信息可以通过类JoinPoint中的成员获得。

对于每次advice代码执行,上表中的类型和静态方法所返回的信息都是一样的,非静态方法返回的信息则是不同的。这些方法通过对象tjpthisJoinPoint)访问,tjpJoinPoint类型对象,在advice代码中可以访问。

下例说明了如何使用JoinPoint API实现可重用的控制流aspect

例子:可重用的trace aspect

aspect Trace {

pointcut virtual methods() = 0;

advice execution(methods()) : around() {

cout < < "before " < < JoinPoint::signature() < < "(";

for (unsigned i = 0; i < JoinPoint::args(); i++)

printvalue(tjp->arg(i), JoinPoint::argtype(i));

cout < < ")" < < endl;

tjp->proceed();

cout < < "after" < < endl;

}

};

这个aspecttrace代码织入到每个pointcut指定的函数中,pointcut是虚的,需要在派生aspect中重新定义。辅助函数printvalue用于将函数调用中的参数格式化输出。对所有参数调用过printvalue后,通过在JoinPoint上调用proceed函数,执行实际的join point

2.5.2    Actions

Aspect C++中,action是指:在程序运行时,跟随在到达的join point之后的语句序列。tjp->proceed()触发join point程序代码的执行。可以是函数调用或函数执行。action概念通过AC::Action结构实现。实际上,proceed()等价于action().trigger(),因此tjp->proceed()可以被tjp->action()->trigger()替换。JoinPoint APIaction()函数,返回join point的实际action对象。

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 2
    评论
评论 2
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值