AOP and Aspect C++ presentation by Igor Kotenkov based on presentation by Andreas Gal, Daniel...

Post on 24-Dec-2015

227 views 0 download

Tags:

transcript

AOP and Aspect C++

presentation by Igor Kotenkov

based on presentation by

Andreas Gal, Daniel Lohmann and Olaf Spinczyk

Contents

• AOP with pure C++

• AspectC++

• AspectC++ Tool Support

• Summary

Templates

• Templates can be used to construct generic code.• To actually use the code, it has to be instantiated.• As preprocessor directives:

– templates are evaluated at compile time

– they do not cause any direct time overhead (if applied properly)

Templates are typically used to implement generic abstract data types.

AOP with templates

• Templates allow us to encapsulate aspect code independently from the component code.

• Aspect code is “woven into” the component code by instantiating those templates.

class Queue {

void enqueue(Item *item) {

// adding item to the queue

}

Item *dequeue() {

// remove item from the queue

}

};

temeplate <class Q>

Counting_Aspect : public Q {

int counter;

public:

void enqueue(Item *item) {

Q::enqueue(item); counter++;

}

Item *dequeue() {

Item *res = Q::dequeue(item);

if (counter > 0) counter--;

return res;

}

// this method is added to the component code

int count() const {return counter;}

};

Weaving

We can define a type alias (typedef) that combines both, component and aspect code:

typedef Counting_Aspect<Queue> CountingQueue;

int main() {

CountingQueue q;

q.add(…);

}

Combining Aspects

Weaving with multiple aspects:

typedef Exception_Aspect<Counting_Aspect<Queue> > ExceptionsCountingQueue;

or typedef Counting_Aspect<Exception_Aspect<Queue> > ExceptionsCountingQueue;

for counting first and then – checking for exceptions.

Implementation of the aspects should be independent of ordering, so it will be no problem with either ordering we will apply.

Limitations

• Joinpoint types– no distinction between function call and execution;– no advice for attribute access;– no advice for private member functions.

• Quantification– no flexible way to describe the target components;– applying the same aspect to classes with different interfaces is

impossible or ends with excessive template metaprogramming.

• Scalability– the wrapper code can easily outweigh the aspect code;– explicitly defining the aspect order for every affected class;– makes code hard to understand and debug.

“AOP with pure C++ is like OOP with pure C”

Conclusions

• C++ templates can be used for separation of concerns in C++ code without special tool support

• However, the lack of expressiveness and scalability restricts these techniques to projects with:– only small number of aspects;

– few or no aspect interactions;

– aspects with a non-generic nature;

– component code that is “aspect-aware”.

Aspect C++

• Basic features within Queue example– aspect, advice, execution, call– introduction– joinpoints, pointcut expressions

• Advanced concepts– the Joinpoint API– abstract aspects and aspect inheritance– aspects ordering– aspect instantiation

• Summary

ElementCounter aspect

aspect ElementCounter {

int counter;

ElementCounter() {

counter = 0;

}

advice execution(“% util::Queue::enqueue(…)”) : after() {

++counter;

printf(“ Aspect ElementCounter: # of elements = %d\n”, counter);

}

advice execution(“% util::Queue::dequeue(…)”) : after() {

if (counter > 0) --counter;printf(“ Aspect ElementCounter: # of elements = %d\n”, counter);

}};

Introducing new aspect.An aspect starts with word aspectand is syntactically much like a class.

Like a class, an aspect can define datamembers, constructors and so on.

ElementCounter aspect

aspect ElementCounter {

int counter;

ElementCounter() {

counter = 0;

}

advice execution(“% util::Queue::enqueue(…)”) : after() {

++counter;

printf(“ Aspect ElementCounter: # of elements = %d\n”, counter);

}

advice execution(“% util::Queue::dequeue(…)”) : after() {

if (counter > 0) --counter;printf(“ Aspect ElementCounter: # of elements = %d\n”, counter);

}};

Giving after advice This pointcut expression denoteswhere the advice shoud be given.

Introduction

• The aspect is not the ideal place to store the counter, because it is shared between all Queue instances

• Ideally, counter becomes a member of Queue• So, we will:

– move the counter into Queue by introduction;– expose context about the aspect invocation to access the

current new instance.

Introduction - continueaspect ElementCounter {private:

advice “util::Queue” : int counter;public:

advice “util::Queue” : int count {return counter;} const

ElementCounter() {counter = 0;

}

advice execution(“% util::Queue::enqueue(…)”) && that(queue) : after(util::Queue& queue) {++queue.counter;printf(“ Aspect ElementCounter: # of elements = %d\n”, queue.count());

}advice execution(“% util::Queue::dequeue(…)”) && that(queue) : after(util::Queue& queue) {

if (queue.count() > 0) --queue.counter;printf(“ Aspect ElementCounter: # of elements = %d\n”, queue.count());

}advice execution(“% util::Queue::Queue(…)”) && that(queue) : before(util::Queue& queue) {

queue.counter = 0;}

};

Introducing a public method to read the counter

Introduces a new data member counterinto all classes denoted by pointcut “util::Queue”

A context variable queue is bound to that.It has to be an util::Queue.

It is used to access the current instance

// within the constructor advice we ensure, that counter gets initialiazed

Joinpoints

• A joinpoint denotes a position to give advise:– Code joinpoint (control flow):

• execution of a function

• call to a function

– Name joinpoints• a named C++ program entity (identifier)

• class, function, method, type namespace

• Joinpoints are given by pointcut expressions– a pointcut expression describes a set of joinpoints

Pointcut Expressions

• Pointcut expressions are made from:– match expressions, e.g. “% util::Queue::enqueue(…)”

• are matched against C++ program entities (name joinpoints)• support wildcards

– pointcut functions, e.g. execution(…), call(…), that(…), within(…), cflow(…)

• execution: all points in the control flow, where a function is about to be executed (code joinpoints)

• call: all points in the control flow, where a function is about to be called (code joinpoints)

• Pointcut functions can be combined– using logical connectors: &&, ||, !– example:

• call(“% util::Queue::enqueue(…)”) && within(“% main(…)”)• !cflow(execution(“% util::~Queue()”))

Advice

• Advice to functions– before advice

• advice code is executed before the original code• advice may read/modify parameter values

– after advice• advice code is executed after the original code• advice may read/modify return value

– around advice• advice code is executed instead of the original code• original code may be executed explicitly: tjp->proceed()

• Introductions– additional methods, data members, etc. are added to the class– can be used to extend the interface of a class or namespace

Error Handling

• We want to check the following constraints:– enqueue() is never called with a NULL item– dequeue() is never called on an empty queue

• In a case of an error an exception should be thrown

• To implement this the advice needs an access to– the parameter passed to enqueue()– the return value returned by dequeue()

ErrorExceptionnamespace util {

class QueueInvalidItemError {};

class QueueEmptyError {};

}

aspect ErrorException {

advice execution(“% util::Queue::enqueue(…)”) && args(item) : before(util::Item* item) {

if (item == NULL) throw util::QueueInvalidItemError();

}

advice execution(“% util::Queue::dequeue(…)”) && result(item) : after(util::Item* item) {

if (item == NULL) throw util::QueueEmptyError();

}

};

Thread Safety

• Protect the queue by a mutex object• To implement this, we need to

– introduce a mutex variable into class Queue– lock it before execution of enqueue/dequeue– unlock it after execution of enqueue/dequeue

• The implementation should be exception safe– in the case of exception the “after” advice should called also– solution: around advice

LockingMutexaspect LockingMutex {

advice “util::Queue” : os::Mutex lock;

pointcut sync_methods() = “% util::Queue::%queue(…)”;

advice execution(sync_methods()) && that(queue) : around(util::Queue& queue) {queue.lock.enter();try { tjp->proceed();} catch(…) { queue.lock.leave(); throw;}queue.lock.leave();

}};

Queue Example Summary

• The Queue example has presented the most important features of the AspectC++ language– aspect, advice, joinpoint, pointcuts, introduction, etc.

• Additionally, AspectC++ provides some more advanced concepts and features– to increase the expressive power of aspectual code– to write broadly reusable aspects– to deal with aspect interdependence and ordering

Advanced Concepts

• The Joinpoint API– provides a uniform interface to the aspect invocation context

• Abstract Aspects and Aspect Inheritance

• Aspects Ordering– allows to specify the invocation order of multiple aspects– Important in the case of inter-aspect dependencies

• Aspect Instantiation– allows to implement user-defined aspect instantiation models

The Joinpoint API

• Inside the advice body, the current joinpoint context is available via the implicitly passed tjp variable:

advice … {

struct JoinPoint {

} *tjp; // implicitly available in advice code

• You have already seen how to use tjp: tjp->proceed() to execute the original code in around advice

• The joinpoint API provides a rich interface– to expose context independently of the aspect target– this is especially useful in writing reusable aspect code

The Joinpoint APIstruct JoinPoint {

// result type of a functiontypedef JP-specific Result;// object type (initiator)typedef JP-specific That;// object type (receiver)typedef JP-specific Target;// returns the encoded type of the joinpoint// (result conforms with C++ ABI V3 spec)static AC::Type type();// the same for resultstatic AC::Type resulttype();// the same for an argumentstatic AC::Type argtype(int n);// returns an unique id for this joinpointstatic unsigned int id();// returns joinpoint type of this joinpoint// (call, execution, …)static AC::JPType jptype(); };

// number of arguments of a function callstatic int args();// textual representation of this joinpointstatic const char* signature();// returns a pointer to the n’th argument// value of a function callvoid* arg(int n);// returns a pointer to the result value of a// function callResult* result();// returns a pointer to the object initiating a call That* that();// returns a pointer to the object that is target of a call

Target* target();// executes the original code in an around advicevoid proceed();// returns the runtime action objectAC::Action& action();

Abstract Aspects and Inheritance

• Aspects can inherit from other aspects– reusing aspect definitions– overriding methods and pointcuts

• Pointcuts can be pure virtual– postponing the concrete definition to derived aspects– an aspect with a pure virtual pointcut is called abstract aspect

• Common usage: resusable aspect implementation– Abstract aspect defines advice code, but pure virtual pointcuts– Aspect code uses the joinpoint API to expose context– Concrete aspect inherits the advice code and overrides

pointcuts

Abstract Aspects and Inheritance

#include “mutex.h”aspect LockingA {

pointcut virtual sync_classes() = 0;pointcut virtual sync_methods() = 0;

advice sync_classes() : os::Mutex lock;

advice execution(sync_methods()) : around() {tjp->that()->lock.enter();try { tjp->proceed();} catch(…) { tjp->that()->lock.leave(); throw;}tjp->that()->lock.leave();

}};

Abstract Aspects and Inheritance

#include “mutex.h”aspect LockingA {

pointcut virtual sync_classes() = 0;pointcut virtual sync_methods() = 0;

advice sync_classes() : os::Mutex lock;

advice execution(sync_methods()) : around() {tjp->that()->lock.enter();try { tjp->proceed();} catch(…) { tjp->that()->lock.leave(); throw;}tjp->that()->lock.leave();

}};

#include “LockingA.ah”

aspect LockingQueue : public LockingA {pointcut sync_classes() = “util::Queue”;pointcut sync_methods() = “% util::Queue::%queue(…)”;

};

Aspect Ordering

• Aspects should be independent of other aspects– however, sometimes inter-aspect dependencies are unavoidable– example: locking should be activated before any other aspect

• Order advice– the aspect order can be defined by order advice

advice pointcut-expr : order(high, …, low)

– different aspect orders can be defined for different pointcuts

• Example:advice “% util::Queue::%queue(…)” : order(“LockingQueue”, “%” && ! “LockingQueue”)

Aspect Instantiation

• Aspects are singeltons by default– aspectof() returns pointer to one-and-only aspect instance

• By overriding aspectof() this can be changed– e.g. one instance per client or one instance per thread

aspect MyAspect {

// …

static MyAspect* aspectof() {

static __declspec(thread) MyAspect* theAspect = NULL;

if (theAspect == NULL) theAspect = new MyAspect();

return theAspect;

}

};

Summary

• AspectC++ facilitates AOP with C++– AspectJ like syntax and semantics

• Full obliviousness and quantification– aspect code is given by advice– joinpoints are given declaratively by pointcuts– Implementation of crosscutting concerns is fully encapsulated in

aspects

• Good support for reusable and generic aspect code– aspect inheritance and virtual pointcuts– joinpoint API

Tool Support

• ac++ compiler– open source and the base of other tools

• AspectC++ add-in for MS Visual Studio– commercial product

• AspectC++ plugin for Eclipse (ACDT)– open source

About ac++

• www.aspect.org (Linux, Win32, Solaris, source, docs, etc.)

• Transforms AspectC++ to C++ code– machine code is created by the back-end (cross-)compiler– supports g++ and Visual C++ specific language extentions

• Current version 0.9 (<1.0)– no optimizations for compilation speed– no weaving in templates– but already more than a proof of concept

Aspect Transformations

• Aspects are transformed to ordinary C++ classes• One static aspect instance is created by default• Advice becomes a member function• “Generic advice” becomes a template member function

• A function call is replaced by a wrapper function• A local class with the wrapper function invokes the advice code for

the joinpoint

• The transformed code is built using C++ compiler

Pros and Cons

• AOP with pure C+++ no special tool required- requires in-depth understanding of C++ templates- the component code has to be aspect-aware- no pointcut concern, no match expressions

• AspectC+++ ac++ transforms AspectC++ into C++

+ various joinpoint types

+ support for advanced AOP concerns: cflow, joinpoint API

- longer compilation time

Roadmap

• Parser– full support of templates. Will accept g++ and VC++ STLs

– better performance

• Advice for object access, for object instantiation• Weaving in macro-generated code• cflow with context variables• Dependancy handling