Professional Documents
Culture Documents
Aspect-Oriented Programming
with ➢ Daniel Lohmann
C++ and AspectC++ dl@aspectc.org
Introduction
University of Erlangen-Nuremberg
Computer Science 4 © 2005 Daniel Lohmann and Olaf Spinczyk I/1 Introduction © 2005 Daniel Lohmann and Olaf Spinczyk I/2
Introduction © 2005 Daniel Lohmann and Olaf Spinczyk I/3 Introduction © 2005 Daniel Lohmann and Olaf Spinczyk I/4
Aspect-Oriented Programming Why AOP with C++?
➢ AOP is about modularizing crosscutting concerns ➢ Widely accepted benefits from using AOP
well modularized concern aspect
avoidance of code redundancy, better reusability,
maintainability, configurability, the code better reflects the
design, ...
➢ Enormous existing C++ code base
maintainance: extensions are often crosscutting
badly modularized
➢ Millions of programmers use C++
without AOP with AOP for many domains C++ is the adequate language
they want to benefit from AOP (as Java programmers do)
➢ Examples: tracing, synchronization, security, ➢ How can the AOP community help?
buffering, error handling, constraint checks, ... Part II: describe how to apply AOP with built-in
mechanisms
Part III-V: provide special language mechanisms for AOP
Introduction © 2005 Daniel Lohmann and Olaf Spinczyk I/5 Introduction © 2005 Daniel Lohmann and Olaf Spinczyk I/6
AOP with C++ © 2005 Daniel Lohmann and Olaf Spinczyk II/1 AOP with C++ © 2005 Daniel Lohmann and Olaf Spinczyk II/2
Templates!
#ifdef COUNTING_ASPECT int count() { return counter; }
++counter; #endif
#endif }; // class Queue
#ifdef LOCKING_ASPECT
} catch (...) {
lock.leave(); throw;
}
lock.leave();
#endif
AOP with C++
}
© 2005 Daniel Lohmann and Olaf Spinczyk II/3 AOP with C++ © 2005 Daniel Lohmann and Olaf Spinczyk II/4
Templates Using Templates
➢ Templates can be used to construct generic code Templates are typically used to implement
To actually use the code, it has to be instantiated generic abstract data types:
➢ Just as preprocessor directives
templates are evaluated at compile-time // Generic Array class
// Elements are stored in a resizable buffer
do not cause any direct runtime overhead (if applied properly)
template< class T >
class Array {
T* buf; // allocated memory
#define add1(T, a, b) \ public:
(((T)a)+((T)b)) T operator[]( int i ) const {
return buf[ i ];
template <class T> }
T add2(T a, T b) { return a + b; }
...
printf(“%d”, add1(int, 1, 2)); };
printf(“%d”, add2<int>(1, 2));
AOP with C++ © 2005 Daniel Lohmann and Olaf Spinczyk II/5 AOP with C++ © 2005 Daniel Lohmann and Olaf Spinczyk II/6
We can define a type alias (typedef) that combines ➢ Aspects can be implemented by template wrappers
both, component and aspect code (weaving): Aspect inherits from component class, overrides relevant methods
Introduction of new members (e.g. counter variable) is easy
Weaving takes place by defining (and using) type aliases
// component code
class Queue { ... }
// The aspect (wrapper class)
➢ The aspect code is generic
template <class Q> It can be applied to "any" component that
class Counting_Aspect : public Q { ... } exposes the same interface (enqueue, dequeue)
// template instantiation
typedef Counting_Aspect<Queue> CountingQueue; Each application of the aspect has to be specified explicitly
int main() { ➢ The aspect code is clearly separated
CountingQueue q;
q.enqueue(new Item);
All code related to counting is gathered in one template class
q.enqueue(new Item); Counting aspect and queue class can be evolved independently
printf(“number of items in q: %u\n”, q.count());
return 0;
(as long as the interface does not change)
}
AOP with C++ © 2005 Daniel Lohmann and Olaf Spinczyk II/9 AOP with C++ © 2005 Daniel Lohmann and Olaf Spinczyk II/10
AOP with C++ © 2005 Daniel Lohmann and Olaf Spinczyk II/11 AOP with C++ © 2005 Daniel Lohmann and Olaf Spinczyk II/12
Ordering Locking Aspect
➢ In what order should we apply our aspects? With what we learned so far, putting together the
Aspect code is executed outermost-first: locking aspect should be simple:
typedef Exceptions_Aspect< // first Exceptions, then Counting
Counting_Aspect< Queue > > ExceptionsCountingQueue;
template <class Q> Item* dequeue() {
typedef Counting_Aspect< // first Counting, then Exceptions class Locking_Aspect : public Q { Item* res;
Exceptions_Aspect< Queue > > ExceptionsCountingQueue; public: lock.enter();
Mutex lock; try {
➢ Aspects should be independent of ordering void enqueue(Item* item) { res = Q::dequeue(item);
lock.enter(); } catch (...) {
For dequeue(), both Exceptions_Aspect and Counting_Aspect give
try { lock.leave();
after advice. Shall we count first or check first? Q::enqueue(item); throw;
Fortunately, our implementation can deal with both cases: } catch (...) { }
lock.leave(); lock.leave();
Item* res = Q::dequeue(item); throw; return res;
// its ok if we run before Exceptions_Wrapper } }
lock.leave(); };
if (counter > 0) counter--;
}
return res;
AOP with C++ © 2005 Daniel Lohmann and Olaf Spinczyk II/13 AOP with C++ © 2005 Daniel Lohmann and Olaf Spinczyk II/14
template <class Q> Item* dequeue() { template <class Q> Item* dequeue() {
class Locking_Aspect : public Q { Item* res; class Locking_Aspect : public Q { Item* res;
public: lock.enter(); public: lock.enter();
Mutex lock; try { Mutex lock; try {
void enqueue(Item* item) { res = Q::dequeue(item); void enqueue(Item* item) { res = Q::dequeue(item);
lock.enter(); } catch (...) { lock.enter(); } catch (...) {
try { lock.leave(); try { lock.leave();
Q::enqueue(item); throw; Q::enqueue(item); throw;
} catch (...) { } } catch (...) { }
lock.leave(); lock.leave(); lock.leave(); lock.leave();
throw; return res; throw; return res;
} } } }
lock.leave(); }; lock.leave(); };
} }
AOP with C++ © 2005 Daniel Lohmann and Olaf Spinczyk II/15 AOP with C++ © 2005 Daniel Lohmann and Olaf Spinczyk II/16
Dealing with Joinpoint Sets Reusable Advice Code
To specify advice for a set of joinpoints, The advice code is expressed as template function,
the joinpoints must have a uniform interface: which is later instantiated with an action class:
template <class Q> template <class Q>
class Locking_Aspect2 : public Q { class Locking_Aspect : public Q {
public: ...
Mutex lock; template <class action> // template inside another template
void advice(action* a) {
// wrap joinpoint invocations into action classes lock.enter();
struct EnqueueAction { try {
Item* item; a->proceed(this);
void proceed(Q* q) { q->enqueue(item); } } catch (...) {
}; lock.leave();
throw;
struct DequeueAction { }
Item* res; lock.leave();
void proceed(Q* q) { res = q->dequeue(); } }
}; ...
... };
AOP with C++ © 2005 Daniel Lohmann and Olaf Spinczyk II/17 AOP with C++ © 2005 Daniel Lohmann and Olaf Spinczyk II/18
AOP with C++ © 2005 Daniel Lohmann and Olaf Spinczyk II/19 AOP with C++ © 2005 Daniel Lohmann and Olaf Spinczyk II/20
Putting Everyting Together “Obliviousness”
We can now instantiate the combined ... is an essential property of AOP: the component code
Queue class, which uses all aspects: should not have to be aware of aspects, but ...
(For just 3 aspects, the typedef
is already getting rather complex)
➢ the extended Queue cannot be named “Queue”
– our aspects are selected through a naming scheme
typedef Locking_Aspect2<Exceptions_Aspect<Counting_Aspect
<Queue> > > CountingQueueWithExceptionsAndLocking; (e.g. CountingQueueWithExceptionsAndLocking).
// maybe a little bit more readable ... ➢ using wrapper class names violates the idea of
typedef Counting_Aspect<Queue> CountingQueue; obliviousness
typedef Exceptions_Aspect<CountingQueue> CountingQueueWithExceptions;
typedef Locking_Aspect<CountingQueueWithExceptions>
CountingQueueWithExceptionsAndLocking; Preferably, we want to hide the aspects from client
code that uses affected components.
AOP with C++ © 2005 Daniel Lohmann and Olaf Spinczyk II/21 AOP with C++ © 2005 Daniel Lohmann and Olaf Spinczyk II/22
– namespace configuration: selection of desired aspects for // client code can import configuration namespace and use
// counting queue as “Queue”
class Queue using namespace configuration;
void client_code () {
➢ The complex naming schemes as seen on the Queue queue; // Queue with all configured aspects
previous slide is avoided }
queue.enqueue (new MyItem);
AOP with C++ © 2005 Daniel Lohmann and Olaf Spinczyk II/23 AOP with C++ © 2005 Daniel Lohmann and Olaf Spinczyk II/24
Obliviousness – Lessons Learned Limitations
➢ Aspect configuration, aspect code, and client code For simple aspects the presented techniques work
can be separated using C++ namespaces quite well, but a closer look reveals limitations:
➢ Joinpoint types
– name conflicts are avoided no destinction between function call and execution
no generic interface to joinpoint context
➢ Except for using the configuration namespace the no advice for private member functions
client code does not have to be changed ➢ Quantification
no flexible way to describe the target components
– obliviousness is (mostly) achieved on the client-side (like AspectJ/AspectC++ pointcuts)
applying the same aspect to classes with different interfaces is
What about obliviousness in the extended classes? impossible or ends with excessive template metaprogramming
AOP with C++ © 2005 Daniel Lohmann and Olaf Spinczyk II/25 AOP with C++ © 2005 Daniel Lohmann and Olaf Spinczyk II/26
AOP with C++ © 2005 Daniel Lohmann and Olaf Spinczyk II/27 AOP with C++ © 2005 Daniel Lohmann and Olaf Spinczyk II/28
References/Other Approaches
K. Czarnecki, U.W. Eisenecker et. al.: "Aspektorientierte Programmierung in
C++", iX – Magazin für professionelle Informationstechnik, 08/09/10, 2001
A comprehensive analysis of doing AOP with pure C++: what's possible and what not
http://www.heise.de/ix/artikel/2001/08/143/
AOP with C++ © 2005 Daniel Lohmann and Olaf Spinczyk II/29
Aspect-Oriented Programming The Simple Queue Class Revisited
with
C++ and AspectC++ namespace util {
class Item {
Item* dequeue() {
printf(" > Queue::dequeue()\n");
friend class Queue; Item* res = first;
Item* next; if( first == last )
AOSD 2005 Tutorial public: first = last = 0;
Item() : next(0){} else
}; first = first->next;
printf(" < Queue::dequeue()\n");
class Queue { return res;
Item* first; }
Item* last; }; // class Queue
public:
Queue() : first(0), last(0) {} } // namespace util
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/3 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/4
ElementCounter1 ElementCounter1 - Elements
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/5 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/6
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/7 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/8
ElementCounter1 - Elements ElementCounter1 - Elements
This pointcut expression denotes
aspect ElementCounter {
where the advice should be given. aspect ElementCounter {
(After execution of methods that match
int counter; the pattern) int counter; Aspect member elements can be
ElementCounter() { ElementCounter() { accessed from within the advice body
counter = 0; counter = 0;
} }
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/9 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/10
ElementCounter2.ah ElementCounter2.ah
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/13 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/14
ElementCounter2.ah ElementCounter2.ah
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/15 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/16
ElementCounter2 - Elements ElementCounter2 - Elements
aspect ElementCounter { aspect ElementCounter {
private: The context variable queue is private: By giving construction advice
advice "util::Queue" : int counter; used to access the calling advice "util::Queue" : int counter; we ensure that counter gets
public: public:
instance. initialized
advice "util::Queue" : int count { return counter; } const advice "util::Queue" : int count { return counter; } const
ElementCounter2.ah ElementCounter2.ah
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/17 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/18
– They are much like a class, may contain methods, advice execution("% util::Queue::enqueue(...)") : after()
data members, types, inner classes, etc. {
printf( " Aspect ElementCounter: after Queue::enqueue!\n" );
– Additionaly, aspects can give advice to be woven in }
at certain positions (joinpoints). Advice can be given to
Functions/Methods/Constructors: code to execute (code advice) ...
};
Classes or structs: new elements (introductions)
– Joinpoints are described by pointcut expressions
ElementCounter1.ah
advice body
➢ We will now take a closer look at some of them
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/21 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/22
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/23 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/24
Advice Before / After Advice
Advice to functions
– before advice with execution joinpoints:
class ClassA {
Advice code is executed before the original code public:
Advice may read/modify parameter values advice execution(“void ClassA::foo()”) : before() void foo(){
printf(“ClassA::foo()”\n);
– after advice advice execution(“void ClassA::foo()”) : after() }
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 with call joinpoints:
Original code may be called explicitly: tjp->proceed() int main(){
printf(“main()\n”);
Introductions advice call (“void ClassA::foo()”) : before() ClassA a;
a.foo();
Additional methods, data members, etc. are added to the class advice call (“void ClassA::foo()”) : after() }
Can be used to extend the interface of a class
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/25 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/26
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/27 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/28
Queue: Demanded Extensions Errorhandling: The Idea
➢ We want to check the following constraints:
I. Element counting – enqueue() is never called with a NULL item
– dequeue() is never called on an empty queue
I want Queue to
throw exceptions! ➢ In case of an error an exception should be thrown
II. Errorhandling
(signaling of errors by exceptions) ➢ To implement this, we need access to ...
– the parameter passed to enqueue()
– the return value returned by dequeue()
III. Thread safety ... from within the advice
(synchronization by mutex variables)
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/29 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/30
advice execution("% util::Queue::enqueue(...)") && args(item) advice execution("% util::Queue::enqueue(...)") && args(item)
: before(util::Item* item) { : before(util::Item* item) {
if( item == 0 ) if( item == 0 )
throw util::QueueInvalidItemError(); throw util::QueueInvalidItemError();
} }
advice execution("% util::Queue::dequeue(...)") && result(item) advice execution("% util::Queue::dequeue(...)") && result(item)
: after(util::Item* item) { : after(util::Item* item) {
if( item == 0 ) if( item == 0 )
throw util::QueueEmptyError(); throw util::QueueEmptyError();
} }
}; };
ErrorException.ah ErrorException.ah
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/31 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/32
ErrorException - Elements ErrorException - Elements
namespace util { namespace util {
struct QueueInvalidItemError {};
A context variable item is bound to struct QueueInvalidItemError
Here{};
the context variable item is
struct QueueEmptyError {}; struct QueueEmptyError {};
}
the first argument of type util::Item* }
bound to the result of type util::Item*
passed to the matching methods returned by the matching methods
aspect ErrorException { aspect ErrorException {
advice execution("% util::Queue::enqueue(...)") && args(item) advice execution("% util::Queue::enqueue(...)") && args(item)
: before(util::Item* item) { : before(util::Item* item) {
if( item == 0 ) if( item == 0 )
throw util::QueueInvalidItemError(); throw util::QueueInvalidItemError();
} }
advice execution("% util::Queue::dequeue(...)") && result(item) advice execution("% util::Queue::dequeue(...)") && result(item)
: after(util::Item* item) { : after(util::Item* item) {
if( item == 0 ) if( item == 0 )
throw util::QueueEmptyError(); throw util::QueueEmptyError();
} }
}; };
ErrorException.ah ErrorException.ah
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/33 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/34
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/35 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/36
Thread Safety: The Idea LockingMutex
aspect LockingMutex {
advice "util::Queue" : os::Mutex lock;
➢ Protect enqueue() and dequeue() by a mutex object
pointcut sync_methods() = "% util::Queue::%queue(...)";
LockingMutex.ah
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/37 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/38
LockingMutex.ah LockingMutex.ah
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/39 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/40
LockingMutex - Elements LockingMutex - Elements
aspect LockingMutex { aspect LockingMutex {
advice "util::Queue" : os::Mutex lock; advice "util::Queue" : os::Mutex lock;
LockingMutex.ah LockingMutex.ah
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/41 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/42
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/43 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/44
Interrupt Safety: The Idea LockingIRQ1
aspect LockingIRQ {
➢ Scenario pointcut sync_methods() = "% util::Queue::%queue(...)";
pointcut kernel_code() = "% kernel::%(...)";
– Queue is used to transport objects between
kernel code (interrupt handlers) and application code advice call(sync_methods()) && !within(kernel_code()) : around() {
os::disable_int();
– If application code accesses the queue, try {
interrupts must be disabled first tjp->proceed();
}
– If kernel code accesses the queue, catch(...) {
interrupts must not be disabled os::enable_int();
throw;
}
➢ To implement this, we need to distinguish os::enable_int();
}
– if the call is made from kernel code, or };
– if the call is made from application code
LockingIRQ1.ah
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/45 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/46
advice call(sync_methods()) && !within(kernel_code()) : around() { advice call(sync_methods()) && !within(kernel_code()) : around() {
os::disable_int(); os::disable_int();
try { try {
tjp->proceed(); tjp->proceed();
} }
catch(...) { We define two pointcuts. One for the catch(...) { This pointcut expression matches any
os::enable_int(); methods to be synchronized and os::enable_int();
call to a sync_method that is not done
throw; throw;
} one for all kernel functions } from kernel_code
os::enable_int(); os::enable_int();
} }
}; };
LockingIRQ1.ah LockingIRQ1.ah
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/47 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/48
LockingIRQ1 – Result LockingIRQ1 – Problem
util::Queue queue; util::Queue queue;
void do_something() { void do_something() {
The pointcut within(kernel_code)
printf("do_something()\n"); main() printf("do_something()\n"); does not match any indirect calls
main()
queue.enqueue( new util::Item ); os::disable_int() queue.enqueue( new util::Item ); os::disable_int()
to sync_methods
} > Queue::enqueue(00320FD0) } > Queue::enqueue(00320FD0)
namespace kernel { < Queue::enqueue() namespace kernel { < Queue::enqueue()
void irq_handler() { os::enable_int() void irq_handler() { os::enable_int()
kernel::irq_handler()
printf("kernel::irq_handler()\n"); kernel::irq_handler()
printf("kernel::irq_handler()\n");
queue.enqueue(new util::Item); > Queue::enqueue(00321030) queue.enqueue(new util::Item); > Queue::enqueue(00321030)
do_something(); < Queue::enqueue() do_something(); < Queue::enqueue()
} do_something() } do_something()
} os::disable_int() } os::disable_int()
int main() { > Queue::enqueue(00321060) int main() { > Queue::enqueue(00321060)
printf("main()\n"); < Queue::enqueue() printf("main()\n"); < Queue::enqueue()
queue.enqueue(new util::Item); os::enable_int() queue.enqueue(new util::Item); os::enable_int()
kernel::irq_handler(); // irq back in main() kernel::irq_handler(); // irq back in main()
printf("back in main()\n"); os::disable_int() printf("back in main()\n"); os::disable_int()
queue.dequeue(); > Queue::dequeue() queue.dequeue(); > Queue::dequeue()
} < Queue::dequeue() returning 00320FD0 } < Queue::dequeue() returning 00320FD0
os::enable_int() os::enable_int()
main.cc main.cc
<Output> <Output>
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/49 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/50
LockingIRQ2.ah LockingIRQ2.ah
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/51 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/52
LockingIRQ2 – Result LockingIRQ – Lessons Learned
util::Queue queue;
void do_something() { You have seen how to ...
printf("do_something()\n"); main()
queue.enqueue( new util::Item ); os::disable_int()
} > Queue::enqueue(00320FD0)
namespace kernel { < Queue::enqueue() ➢ restrict advice invocation to a specific calling context
void irq_handler() { os::enable_int()
kernel::irq_handler()
printf("kernel::irq_handler()\n");
queue.enqueue(new util::Item); > Queue::enqueue(00321030)
do_something(); < Queue::enqueue() ➢ use the within(...) and cflow(...) pointcut functions
} do_something()
} > Queue::enqueue(00321060) – within is evaluated at compile time and returns all code
int main() { < Queue::enqueue() joinpoints of a class' or namespaces lexical scope
printf("main()\n"); back in main()
queue.enqueue(new util::Item); os::disable_int()
kernel::irq_handler(); // irq > Queue::dequeue() – cflow is evaluated at runtime and returns all joinpoints
printf("back in main()\n"); < Queue::dequeue() returning 00320FD0
queue.dequeue(); os::enable_int()
where the control flow is below a specific code joinpoint
}
main.cc
<Output>
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/53 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/54
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/55 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/56
The Joinpoint API The Join Point API (Excerpt)
Types (compile-time) Values (runtime)
➢ Inside an advice body, the current joinpoint context
is available via the implicitly passed tjp variable: // object type (initiator)
That
// pointer to the object initiating a call
That* that()
advice ... { // object type (receiver) // pointer to the object that is target of a call
struct JoinPoint { Target Target* target()
... // result type of the affected function // pointer to the result value
} *tjp; // implicitly available in advice code Result Result* result()
... // type of the i'th argument of the affected // typed pointer the i'th argument value of a
} // function (with 0 <= i < ARGS) // function call (compile-time index)
Arg<i>::Type Arg<i>::ReferredType* arg()
➢ You have already seen how to use tjp, to ... Arg<i>::ReferredType
// pointer the i'th argument value of a
// function call (runtime index)
execute the original code in around advice with tjp->proceed() Consts (compile-time) void* arg( int i )
// textual representation of the joinpoint
➢ The joinpoint API provides a rich interface // number of arguments
ARGS
// (function/class name, parameter types...)
static const char* signature()
to expose context independently of the aspect target // unique numeric identifier for this join point // executes the original joinpoint code
JPID // in an around advice
this is especially useful in writing reusable aspect code // numeric identifier for the type of this join void proceed()
// point (AC::CALL, AC::EXECUTION, ...) // returns the runtime action object
JPTYPE AC::Action& action()
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/57 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/58
Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/65 Aspect C++ © 2005 Daniel Lohmann and Olaf Spinczyk III/66
Aspect-Oriented Programming Overview
with
C++ and AspectC++ ➢ ac++ compiler
– open source and base of the other presented tools
AOSD 2005 Tutorial
➢ ag++ wrapper
– easy to use wrapper around g++ for make-based projects
➢ AspectC++ Add-In for Microsoft® Visual Studio®
– commercial product by pure-systems GmbH
Transform.ah' Transform.ah'
Tool Support © 2005 Daniel Lohmann and Olaf Spinczyk IV/5 Tool Support © 2005 Daniel Lohmann and Olaf Spinczyk IV/6
Transform.ah' Transform.ah'
Tool Support © 2005 Daniel Lohmann and Olaf Spinczyk IV/7 Tool Support © 2005 Daniel Lohmann and Olaf Spinczyk IV/8
Joinpoint Transformation Joinpoint Transformation
int main() { int main() { the function call is replaced by
foo(); foo();
return 0; return 0;
a call to a wrapper function
} }
main.cc main.cc
int main() { int main() {
struct __call_main_0_0 { struct __call_main_0_0 {
static inline void invoke (){ static inline void invoke (){
AC::..._a0_before (); AC::..._a0_before ();
::foo(); ::foo();
} }
}; };
__call_main_0_0::invoke (); __call_main_0_0::invoke ();
return 0; return 0;
} }
main.cc' main.cc'
Tool Support © 2005 Daniel Lohmann and Olaf Spinczyk IV/9 Tool Support © 2005 Daniel Lohmann and Olaf Spinczyk IV/10
Tool Support © 2005 Daniel Lohmann and Olaf Spinczyk IV/11 Tool Support © 2005 Daniel Lohmann and Olaf Spinczyk IV/12
Tool Demos Summary
➢ Tool support for AspectC++ programming is based
on the ac++ command line compiler
– AspectC++ Add-In for Microsoft® Visual Studio®
– full “obliviousness and quantification”
– delegates the binary code generation to your
by pure-systems GmbH (www.pure-systems.com) favorite compiler
➢ Commercial and a non-commercial
®
– AspectC++ plugin for Eclipse IDE integration is available
– Microsoft® Visual Studio®
sophisticated environement for AspectC++ development – Eclipse®
Tool Support © 2005 Daniel Lohmann and Olaf Spinczyk IV/13 Tool Support © 2005 Daniel Lohmann and Olaf Spinczyk IV/14
Aspect-Oriented Programming AspectC++ in Practice - Examples
with
C++ and AspectC++ ➢ Applying the observer protocol
– Example: a typical scenario for the widely used observer pattern
AOSD 2004 Tutorial
– Problem: implementing observer requires several
design and code transformations
update on
change
Examples V/3 Examples V/4
Observer Pattern: Problem Solution: Generic Observer Aspect
aspect ObserverPattern {
The 'Observer Protocol' Concern... ...
public:
struct ISubject {};
IObserver observers ISubject struct IObserver {
update (in s : ISubject) updateAll() virtual void update (ISubject *) = 0;
};
pointcut virtual observers() = 0;
pointcut virtual subjects() = 0;
pointcut virtual subjectChange() = execution( "% ...::%(...)"
&& !"% ...::%(...) const" ) && within( subjects() );
advice observers () : baseclass( IObserver );
advice subjects() : baseclass( ISubject );
DigitalClock AnalogClock ClockTimer advice subjectChange() : after () {
Draw() Draw() GetHour() : int ISubject* subject = tjp->that();
update (in s) update (in s) SetTime (in h : int , in m : int, in s : int ) updateObservers( subject );
Tick() }
void updateObservers( ISubject* subject ) { ... }
void addObserver( ISubject* subject, IObserver* observer ) { ... }
...crosscuts the module structure void remObserver( ISubject* subject, IObserver* observer ) { ... }
};
Examples V/5 Examples V/6
– observer no longer “hard coded” into the desing and code WNDCLASS wc = {0, WndProc, 0, 0, ... , "Example_Class"};
RegisterClass( &wc );
HWND hwndMain = CreateWindowEx( 0, "Example_Class", "Example", ... );
– no more forgotten calls to update() in subject classes UpdateWindow( hwndMain );
MSG msg;
➢ Full source code on Tutorial CD while( GetMessage( &msg, NULL, 0, 0 ) ) {
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return 0;
Examples V/13 Examples
} V/14
MSG msg;
while( GetMessage( &msg, NULL, 0, 0 ) ) {
➢ Full source code on tutorial CD
TranslateMessage( &msg );
DispatchMessage( &msg );
}
return 0;
Examples
} V/19 Examples V/20
Aspect-Oriented Programming Pros and Cons
with
C++ and AspectC++ AOP with pure C++
+ no special tool required
AOSD 2005 Tutorial – requires in-depth understanding of C++ templates
– lack of “obliviousness”
the component code has to be aspect-aware
– lack of “quantification”
no pointcut concept, no match expressions
AspectC++
Part VI – Summary +
+
the ac++ compiler transforms AspectC++ into C++
various supported joinpoint types, e.g. execution and calls
+ built-in support for advanced AOP concepts:
cflow, joinpoint-API
– longer compilation times
Summary © 2005 Daniel Lohmann and Olaf Spinczyk VI-1 Summary © 2005 Daniel Lohmann and Olaf Spinczyk VI-2
Summary © 2005 Daniel Lohmann and Olaf Spinczyk VI-3 Summary © 2005 Daniel Lohmann and Olaf Spinczyk VI-4
Thank you for your attention!