An AOP Extension for C++
Total Page:16
File Type:pdf, Size:1020Kb
Workshop Aspects AspectC++: Olaf Spinczyk Daniel Lohmann Matthias Urban an AOP Extension for C++ ore and more software developers are The aspect Tracing modularises the implementa- On the CD: getting in touch with aspect-oriented tion of output operations, which are used to trace programming (AOP). By providing the the control flow of the program. Whenever the exe- The evaluation version M means to modularise the implementation of cross- cution of a function starts, the following aspect prints of the AspectC++ Add- in for VisualStudio.NET cutting concerns, it stands for more reusability, its name: and the open source less coupling between modules, and better separa- AspectC++ Develop- tion of concerns in general. Today, solid tool sup- #include <cstdio> ment Tools (ACDT) port for AOP is available, for instance, by JBoss for Eclipse, as well as // Control flow tracing example example listings are (JBoss AOP), BEA (AspectWerkz), and IBM (As- aspect Tracing { available on the ac- pectJ) and the AJDT for Eclipse. However, all // print the function name before execution starts companying CD. these products are based on the Java language. advice execution ("% ...::%(...)") : before () { For C and C++ developers, none of the key players std::printf ("in %s\n", JoinPoint::signature ()); offer AOP support – yet. } This article introduces AspectC++, which is an as- }; pect-oriented language extension for C++. A compil- er for AspectC++, which transforms the source code Even without fully understanding all the syntactical into ordinary C++ code, has been developed as elements shown here, some big advantages of AOP an open source project. The AspectC++ project be- should already be clear from the example. Without gan with a research prototype in 2001 that has gained using this simple aspect, which is only a few lines maturity over the years. Today, the AspectC++ lan- long, one would have to augment all functions of guage and weaver has been successfully applied the program with an additional printf statement to a number of commercial projects in industry and to get the same result. In a large project, a style academia and IDE integration into Eclipse and Micro- guide would have to document this as a require- soft VisualStudio.NET make the first steps child's play. ment and all programmers would have to read and Our AspectC++ introduction will start with an ex- obey this global policy. As a result of this, the AOP ample that can be considered the Hello World of solution saves a lot of time, organisational effort, AOP. It will illustrate the basic language elements like and guarantees that no function will be forgotten. aspects, pointcuts, and advice, which some read- At the same time the code, which is affected by ers might already know from the AspectJ language. the aspect, is completely decoupled from the trac- We will then quickly step beyond these AspectJ-like ing code, i.e. the printf. Not even <cstdio> has language elements by looking into an AspectC++ to be included, because all this is done separately version of the well-known observer pattern and into by the aspect. Generic Advice. This unique AspectC++ feature com- bines the power of aspects with generic and genera- Aspects, advice and pointcuts tive programming in C++. The Tracing example shows most of the language elements that are responsible for these advantag- Tracing – the Hello World of AOP es. We will start with the aspect, which is intended As an introductory example for AOP with AspectC++, as a module for the implementation of a crosscutting we will take a closer look at a very simple aspect. concern. From the syntactical perspective an aspect in AspectC++ is very much like a class in C++. How- Dr. Olaf Spinczyk is the founder and leader of the As- ever, besides member functions and data elements, pectC++ project. Daniel Lohmann uses AspectC++ in his an aspect can additionally define advice. After the research on operating system development and works advice keyword a pointcut expression defines where on the design and evolution of the AspectC++ language the advice should affect the program (the join points), concepts. Both are employed at Friedrich-Alexander while the part that follows the colon defines how the University Erlangen-Nuremberg. Matthias Urban is the program should be affected at these points. This is main developer of the AspectC++ parser. He works a general rule for all kinds of advice in AspectC++. for pure-systems GmbH, where he is responsible for AspectC++ support and the AspectC++ Add-In for Pointcut expressions VisualStudio.NET. Contact with the authors: [email protected], The pointcut expression in the example (where) is [email protected], [email protected] execution("% ...::%(...)"). It means that the advice should affect the execution of all functions that match 68 www.sdjournal.org Software Developer’s Journal 5/2005 Aspect Programming the expression "% ...::%(...)". In "match expressions" the % and �������� ��������� �������� ... characters are used as wildcards. A percent (%) matches any ����������������������� ����������� type (for example "% *" matches any pointer type) and also any sequence of characters in identifiers (for example "xdr _ %" matches all classes, which have a name that starts with xdr _ ). An ellipsis (...) matches any sequence of types or namespaces (for example "int foo(...)" matches any global function which returns an int and is named foo). Eventually, the match expres- ������������ ����������� ���������� sion "% ...::%(...)" matches any function in any class or name- ������ ������ ��������������� space. ������������� ������������� �������������������������������������������� Match expressions represent sets of named program en- ������ tities like functions or classes. Thus, match expressions are already primitive pointcut expressions which describe a set of Figure 1. Crosscutting in the Observer Pattern join points in the static program structure (’static join points‘). However, in our example we want advice for an event in the implement an additional piece of program behaviour. In our dynamic control flow of the program, namely the execution of aspect Trace this behaviour is implemented by the printf() functions. Therefore, the pointcut function execution() is used. statement, which follows before(). Syntactically this looks It yields all function execution join points for the functions like a function body and, indeed, we can understand the ad- given as its argument. vice body as an anonymous member function of the aspect. Instead of before() we could also use after() advice (or both) Advice for dynamic join points in the example. In this case, the advice body would be run af- For dynamic join points AspectC++ supports three types of ter the execution of a function has finished. Anaround() advice code advice called before(), after() and around(). They all body is executed instead of the control flow, which would normally follow the dynamic join point. Listing 1. The Abstract Aspect ObserverPattern Combined pointcut expressions aspect ObserverPattern { Pointcut expressions can be combined by using the set opera- // Data structures to manage subjects and observers tors && (intersection), || (union) and ! (inversion). For ... instance, the expression "% foo(int, ...)" || "int bar(...)" public: matches any global function named foo which takes // Interfaces for each role an int as the first parameter and any global function named struct ISubject {}; bar which returns an int. In conjunction with pointcut func- struct IObserver { tions, we thereby get quite powerful expressions to describe virtual void update(ISubject *) = 0; where advice should affect the program. For instance, we }; might change the pointcut expression for our Tracing aspect as follows: // To be defined by the concrete derived aspect // subjectChange() matches all non-const methods advice call ("% ...::%(...)") pointcut virtual observers() = 0; && within ("Client") : before () { pointcut virtual subjects() = 0; std::printf ("calling %s\n", JoinPoint::signature ()); pointcut virtual subjectChange() = } execution("% ...::%(...)" && !"% ...::%(...) const") The call() function yields all function call join points for && within(subjects()); the given functions. In contrast to execution join points, call join points take effect on the caller side, that is before, // Add new baseclass to each subject/observer class after, or around an actual function call. The within() point- // and insert code to inform observers cut function simply returns all join points in the given class- advice observers() : baseclass(IObserver); es or functions. By giving advice to the intersection of call advice subjects() : baseclass(ISubject); ("% ...::%(...)") (any function call) and within ("Client") (any join point in the class Client), the aspect will now trace advice subjectChange() : after() { only those function calls that are made from a method in the ISubject* subject = tjp->that(); class Client. updateObservers(subject); } Join point API // Operations to add, remove and notify observers In the advice body the expression JoinPoint::signature() void updateObservers(ISubject* sub) { ... } still waits for an explanation. As we know from the example, void addObserver(ISubject* sub, IObserver* ob) { ... } it yields the function name that we print before the function void remObserver(ISubject* sub, IObserver* ob) { ... } execution starts. The static member function signature() }; is defined by the join point API. This is an API defined by AspectC++ that allows aspect code to retrieve