1 / 37

AOP and Aspect C++

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.

evelia
Télécharger la présentation

AOP and Aspect C++

An Image/Link below is provided (as is) to download presentation Download Policy: Content on the Website is provided to you AS IS for your information and personal use and may not be sold / licensed / shared on other websites without getting consent from its author. Content is provided to you AS IS for your information and personal use only. Download presentation by click this link. While downloading, if for some reason you are not able to download a presentation, the publisher may have deleted the file from their server. During download, if you can't get a presentation, the file might be deleted by the publisher.

E N D

Presentation Transcript


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

  2. Contents • AOP with pure C++ • AspectC++ • AspectC++ Tool Support • Summary

  3. 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.

  4. 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;} };

  5. 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(…); … }

  6. Combining Aspects Weaving with multiple aspects: typedef Exception_Aspect<Counting_Aspect<Queue> > ExceptionsCountingQueue; ortypedef 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.

  7. 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”

  8. 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”.

  9. 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

  10. Introducing new aspect. An aspect starts with word aspect and is syntactically much like a class. Like a class, an aspect can define data members, 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); } };

  11. Giving after advice This pointcut expression denotes where the advice shoud be given. 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); } };

  12. 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.

  13. Introduces a new data member counter into all classes denoted by pointcut “util::Queue” Introducing a public method to read the counter A context variable queue is bound to that. It has to be an util::Queue. It is used to access the current instance Introduction - continue aspect 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; } }; // within the constructor advice we ensure, that counter gets initialiazed

  14. 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

  15. 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()”))

  16. 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

  17. 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()

  18. ErrorException namespace 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(); } };

  19. 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

  20. LockingMutex aspect 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(); } };

  21. 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

  22. 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

  23. The Joinpoint API • Inside the advice body, the current joinpoint context is available via the implicitly passedtjp 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

  24. struct JoinPoint { // result type of a function typedefJP-specific Result; // object type (initiator) typedefJP-specific That; // object type (receiver) typedefJP-specific Target; // returns the encoded type of the joinpoint // (result conforms with C++ ABI V3 spec) static AC::Type type(); // the same for result static AC::Type resulttype(); // the same for an argument static AC::Type argtype(int n); // returns an unique id for this joinpoint static unsigned int id(); // returns joinpoint type of this joinpoint // (call, execution, …) static AC::JPType jptype(); }; // number of arguments of a function call static int args(); // textual representation of this joinpoint static const char* signature(); // returns a pointer to the n’th argument // value of a function call void* arg(int n); // returns a pointer to the result value of a // function call Result* 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 advice void proceed(); // returns the runtime action object AC::Action& action(); The Joinpoint API

  25. 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

  26. 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(); } };

  27. 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(…)”; };

  28. 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 advicepointcut-expr : order(high, …, low) • different aspect orders can be defined for different pointcuts • Example: advice “% util::Queue::%queue(…)” : order(“LockingQueue”, “%” && ! “LockingQueue”)

  29. 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; } };

  30. 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

  31. 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

  32. 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

  33. 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

  34. 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

  35. 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

More Related