6

Click here to load reader

AspectC++: Language Proposal and Prototype Implementation

Embed Size (px)

DESCRIPTION

Andreas Gal

Citation preview

Page 1: AspectC++: Language Proposal and Prototype Implementation

AspectC++: Language Proposal and PrototypeImplementation �

[Position Paper, Category A]

Andreas Gal, Wolfgang Schroder-Preikschat, and Olaf SpinczykUniversity of Magdeburg

Universitatsplatz 239106 Magdeburg, Germany

fgal,wosch,[email protected]

ABSTRACTThe success of aspect-oriented programming (AOP) raises and fallswith user-friendly tool support. With AspectJ1 the first completeand powerful language extension for AOP has been created. Withthis paper we intend to extend the AspectJ approach to C/C++. Wewill present and discuss a proposal for a set of language extensionswe call AspectC++ to facilitate aspect-oriented programming withC/C++ and we will illustrate our prototype implementation of acompiler for this new language.

1. MOTIVATIONWhile there is currently a heavy focus on aspect-oriented program-ming (AOP) with Java2, other languages like C and C++ experiencefar less attention in the effort of creating and establishing exten-sions for aspect-oriented programming. This is especially dissat-isfying as a large fraction of applications is still being developedin C and C++. These applications cannot simply be considered aslegacy, as for a number of domains specific features of the C/C++language like minimal requirements in terms of run-time supportare crucial. To be able to exploit the advantages of aspect-orientedprogramming (AOP) also with C/C++, it is inevitable to provide anAOP framework.

A key component of such a framework is a set of language ex-tensions to support the independent implementation of aspects.AspectJ [6] is an example for such a set of language extensionsfor the Java language. The popularity of AspectJ was supportedby the fact that a working AspectJ compiler is publicly availableand many programmers already gathered experience with aspect-oriented programming using AspectJ. As C++ and Java have gram-

�This work has been partly supported by the German ResearchCouncil (DFG), grant no. SCHR 603/1-1 and SCHR 603/2.1AspectJ is a trademark of Xerox Corporation.2Java is a registered trademark of Sun Microsystems, Inc. in theU.S. and other countries.

matically and semantically a lot in common, it seems natural touse AspectJ as foundation when creating a set of extensions for theC/C++ language to support aspect-oriented programming.

In this paper we will present such an extension to the C++ language,called AspectC++. While AspectC++ is conceptually very similarto AspectJ, it was also necessary to adopt and streamline what welearned from AspectJ accordingly to the specific peculiarities of theC/C++ syntax and semantics.

The outline of this paper is as follows: In section 2 we will describein detail the AspectC++ language and the differences to AspectJ.Following in section 3 the architecture of our prototype implemen-tation of an AspectC++ compiler is explained. Related work isdiscussed in section 4, followed by the conclusions and a road mapof our future work in section 5.

2. THE ASPECTC++ LANGUAGEAspectC++ is a general-purpose aspect-oriented extension to theC++ language. We will use a simple example (figure 1) we havetaken from the AspectJ introduction to illustrate the features of As-pectC++.

FigureElement

virtual void setXY(int,int) = 0;

Line Point

void setP1(Point&);void setP2(Point&);

void setX(int);void setY(int);

Point p1;Point p2;

int x;int y;

Figure 1: Class diagram of our example scenario

2.1 Language IntroductionIn AspectC++ join points are defined as points in the componentcode where aspects can interfere. Join points can refer to code,types (classes, structs, andunions), objects and control flows.

Pointcut expressionscan be used to identify a collection of suchjoin points. They are composed frompointcut designatorsand a

Page 2: AspectC++: Language Proposal and Prototype Implementation

set of algebraic operators. In figure 2 the pointcut designators ofAspectC++ are listed.

Additional to the pointcut designatorsnamed pointcutscan be de-clared to ease the composition of complex pointcut expressions:

pointcut moves() =calls("void FigureElement::setXY(int,int)") ||calls("void Point::set%(int)") ||calls("void Line::setP%(Point &)");

In this examplematch expressionsare used filter-out specific joinpoints. Match expressions are similar to C++ signatures, but cancontain additionally the wildcard character “%”. For example,“void %::set%(%)” could be used to match the same set of joinpoints (in this example scenario).

AdviceAn advice declaration can be used to specify code that should runat the join points specified by a pointcut expression:

advice moves() : void after() {cout << "A figure element was moved." << endl;

}

Context information from the join point can be exposed by usingpointcuts with arguments and match expressions that contain iden-tifiers instead of type names everywhere where context should beexposed:

advice calls("void FigureElement::setXY(x,y)") :void after(int x, int y) {cout << "A figure element was moved to ("

<< x << ", " << y << ")." << endl;}

Different kinds of advice can be declared, including anafteradvicethat runs after the join point, abeforeadvice that is executed beforethe join point, and anaroundadvice, which is executed in place ofthe join point. An around advice has to request the execution of theoriginal join point code usingproceed():

advice moves() : void around() {cout << "A figure element will move." << endl;proceed();cout << "A figure element was moved." << endl;

}

If the advice is not recognized as being of a predefined kind, it isregarded as a new method to be added to all join points containedin the pointcut expression. The pointcut expression must contain inthis case only join points of the type class.

pointcut all_classes() = classes("%");advice all_classes() : void print() {

cout << "Address: " << (void*)this << endl;}

AspectWhile named pointcut declarations can appear everywhere wheredeclarations are allowed, an advice can only be defined inside anaspect declaration. Aspects in AspectC++ implement in a modularway crosscutting concerns and are an extension to the class conceptin C++. Additionally to attributes and methods, aspects may alsocontain advice declarations. Aspects can be derived from classes

and aspects, but it is not possible to derive a class from an aspect.By default, for every aspect a single object is instantiated at run-time. Using theof-clause it is also possible to bind the instantiationof aspects to certain pointcuts. Pointcuts used with anof-clausemust contain join points of a uniform type (for example classes,objects, or cflows):

aspect classCounter of all_classes() {int count;

public:classCounter() {

count = 0;}advice all_classes() : void bump();

};void classCounter::bump() {count++;cout << "Called " << count << " times." << endl;

}

This example adds a methodbump()to all classes. Theof-clausecauses the aspectclassCounterand thus the private fieldcount tobe instantiated separately for every class. Thus, each invocationof bump()will only count the number of invocations of thebump()methodof this class. Omitting theof-clause would cause the defaultcase to be used where only a single instance of the aspect class iscreated. With this modification only a singlecountvariable wouldbe maintained and used by all instances ofbump().

2.2 Rationale of AspectC++So far we have presented the basic concepts of AspectC++, whichare mostly adopted from AspectJ. The reason for this similarity isto allow people who learned “thinking aspect-oriented” by usingthe popular AspectJ implementation to easily switch over to As-pectC++, if they are also already familiar with C++. NeverthelessAspectC++ is quite different in some points. The following para-graphs will discuss the reasons for the different design decisions.

GrammarThe parser of AspectJ is integrated into the Java parser and thegrammar of AspectJ shares rules with the standard Java part. Thisallows aspect code and component code to be mixed. For instance,it is allowed to define named pointcuts anywhere in the program,even as attributes of normal classes. Thus AspectJ is really morea Java language extension than a separate aspect language. Thismakes it not feasible to use AspectJ without modifications togetherwith other component languages than Java. While we would havepreferred to have a separate and portable aspect language with itsown grammar, where component code fragments are only used asquoted strings, we are aware that the “look and feel” would thendiffer significantly from AspectJ. To avoid this problem the gram-mar of AspectC++ has been integrated into the C++ grammar. Fig-ure 3 shows the grammar extensions. It should be understood as anextension to the C++ grammar listed by Stroustrup [9].

A main design goal was to keep the grammar extension simple.Compiler writers, who want to implement the AspectC++ lan-guage, should not be forced to use a hand-coded parser. Insteadthey should be able to continue using compiler generators and thelexical analysis should not require context information to work.Parsing C++ is already much harder than parsing Java due to am-biguities in the grammar, which require either the syntactical and

Page 3: AspectC++: Language Proposal and Prototype Implementation

calls(“match expression”)receptions(“match expression”)executions(“match expression”)

calls finds all calls to methods with a signature matching the givenmatch expression,receptionsrefers to the code called through an abstract interface with the given signa-ture, andexecutionsmatches the body of a function. Constructors and destructors aretreated like regular methods.

gets(“match expression”)gets(“match expression”) [old ]sets(“match expression”)sets(“match expression”) [old ]sets(“match expression”) [old ] [new]

getsand setslocate any direct access to fields with a signature matching thematchexpression. Field access through C++ references or pointers are not recognized.oldandnewcan be used in the aspect code to refer to the old and the new value of the field.

classes(“match expression”)derived(pointcut)bases(pointcut)objects(“match expression”)instanceof(pointcut)

classesfilters out all classes, structures, and unions with matching names. Withderivedit is possible to refer to all types in the pointcut expression and all classes derived fromthem. basescan be used to find all base types of classes in a pointcut.objectsfindsobjects with matching names, whileinstanceofcan be used to locate objects of a certaintype.

handles(pointcut)within(pointcut)

With handlesfor every class in thepointcutexception handlers are returned that handleexceptions of that type.within matches all join points declared in methods of types inthepointcut.

cflow( pointcut)each(cflow(pointcut))

cflowcaptures join points occurring in the dynamic execution context of join points inthe pointcut. each(cflow(. . . )) can be used only in conjunction with theof-clause torefer to execution contexts and not the join points occurring in them.

callsto(pointcut) callstocan be used to locate all calls to join points in the givenpointcut.

Figure 2: Pointcut designators in AspectC++

semantical analysis phases to be mixed or an extremely weak gram-mar. The AspectC++ extension should not shake such a fragilebuilding more than necessary.

Property-Based Pointcut DesignatorsOne of the main problems with the AspectJ grammar comes fromthe property-based pointcut designators. For example in AspectJthe pointcut

calls(void FigureElement.set*(int, int))

refers to all calls to methods of classFigureElement with two in-teger argument, void result type, and a name beginning with “set”.The symbol “*” is used as a wildcard in identifiers. While thissyntax might be familiar for Java programmers it can not be usedunmodified in the C++ context. For instance, “*” is a valid partin C++ type declarators, which are used to declare pointers. Al-lowing “*” to be a wildcard character in pointcut designator there-fore leads to ambiguities. For this reason we replaced “*” with“%” in AspectC++. Furthermore the “.” between the class nameFigureElement and the match expression “set*” is misleadingfor C++ programmers, because in C++ “::” is used to specify qual-ified names. To avoid this problem we have replaced “.” with “::”in pointcut designators. For reasons we will discuss below, it isfurther required to specify pointcut designators as quoted strings.The following example shows the AspectC++ version of the As-pectJ pointcut shown above as it results from the described modifi-cations:

calls("void FigureElement::set%(int, int)")

The quoting of pointcut designators frees the lexical analyzer fromthe burden to accept wildcards in identifiers in specific contexts andsimplifies the grammar significantly: pointcuts can now be parsedwith the normal C++ expression syntax.

It is necessary to detect identifiers, which are used to expose contextinformation, in the quoted pointcut designator strings. Furthermoresimple regular expression matching is not sufficient to safely iden-tify groups of function signatures. Therefore the pointcut designa-tors must be syntactically analysed. With our solution this parsingstep can be postponed and is the job of a separated parser. Thus noadditional rules in the C++ grammar are needed.

Generalized Type Names“Generalized Type Names” (GTNs) are used in AspectJ to selecta group of types (i.e. classes and interfaces) with a single pattern.For example

subtypes(Object) && !java.io.*

refers to all classes that are not part of thejava.io package. Asit is shown here, GTNs may contain algebraic operators like “||”,“&&”, and “!” and the wildcard character “*” for pattern matching.Together with further functions like “subtypes()” this can be usedto form complex expressions that select arbitrary sets of classes.GTNs can be used in AspectJ to introduce new attributes, meth-ods, base classes or implemented interfaces into the referred set ofclasses. For example

aspect A {private Registry otherPackage.*.r;

}

introduces a new private attribute “r” of typeRegistry into eachclass that matches the GTN “otherPackage.*”.

While AspectJ explicitly distinguishes betweenpointcuts and ad-vice on the one hand andGTNs and introductionson the other,AspectC++ handles both in a uniform way. For example in As-pectC++

Page 4: AspectC++: Language Proposal and Prototype Implementation

aspect-name:identifier

declaration:pointcut-declaration

class-head:aspectidentifieropt aspect-clausesopt base-clauseopt

aspect-clauses:aspect-clauses aspect-clauseopt

aspect-clause:of pointcutdominatesaspect-name

pointcut:constant-expression

member-declaration:pointcut-declarationadvice-declaration

pointcut-declaration:pointcutdeclaration

advice-declaration:advicepointcut : declaration

Figure 3: The AspectC++ extensions to the C++ grammar

aspect A {private:

advice classes("otherNamespace::%") : Registry r;};

can be used to introduce a member “Registry r” into each classin namespace “otherNamespace”. This design decisions has thefollowing advantages:

� With the concept of named pointcuts in combination withthe uniform handling, it is possible to have named GTNs.While this is already a useful feature it is even possible tohave virtual or pure virtual GTNs in AspectC++.

� The grammar does not require special rules for GTNs andintroductions.

� The users benefit from the coherent language design. Ev-erything that is declared inside of an aspect body and beginswith the keywordpointcut or advice will have an impacton other classes while everything else will become part ofthe instantiated “aspect object”.

Regarding the implementation, this extended join point model re-quires using typed join points. For example, the AspectC++ com-piler has to ensure that only code join points are used in conjunctionwith anbefore, after, or aroundadvice.

3. PROTOTYPE IMPLEMENTATIONOur prototype implementation of a compiler for the AspectC++language is a C++ preprocessor-like compiler based on PUMA [8].PUMA is a source code transformation system for C++.

The architecture of our AspectC++ compiler is shown in figure 4.First the AspectC++ source code is scanned, parsed and a seman-tical analysis is performed. Then the planing stage is entered. In

the planing stage the pointcut expressions are evaluated and thejoin point sets are calculated. A plan for the weaver is createdcontaining join points and the operations to be performed at thejoin points (i.e. adding advice code). While the planing stage ismainly independent from the component language C++, the weaveris now responsible to transform the plan into concrete manipulationcommands based on the C++ syntax tree generated by the PUMAparser. The actual code manipulation is then performed by thePUMA manipulation engine. The output of the prototype compileris C++ component source code with the aspect codewoven in. Theproduced output source code does not contain AspectC++ languageconstructs anymore and thus can be translated to executable codeusing conventional C++ compilers.

PUMA

AspectC++

plansyntax treeC++

token stream aspects semantics

manipulated source code

AspectC++ source code

manipulator commands

manipulator

scanner parser sem. analyser

C++ compiler

weaver

planner

Figure 4: Architecture of the AspectC++ compiler

As the proposed AspectC++ language was designed with the dif-ficulties of the C++ grammar in mind, adding the language exten-sions to the C++ grammar is quite straight forward. We would havepreferred an implementation where the additional grammar rulesof AspectC++ can be dynamically added to the C++ grammar, butPUMA did not give us this choice. The PUMA scanner has alsobeen modified to recognize the new AspectC++ keywords. We dis-covered that it is possible to restrain the number of new keywordsto aspect, pointcutand adviceby slightly changing the grammarrule for an aspect clause (figure 5).

aspect-clause:identifier pointcutidentifier aspect-name

Figure 5: Modified aspect clause grammar

The modified grammar will accept a superset of valid AspectC++aspect clauses, but it is possible to filter out invalid aspect clausesduring the semantic analysis. The reason for this quirk is to preventincompatibilities with plain C++ where “‘of” and “dominates” arevalid identifiers. Such collisions can still occur with the three new

Page 5: AspectC++: Language Proposal and Prototype Implementation

keywords, but eliminating those would require a major restructur-ing of the AspectC++ grammar.

The AspectC++ specific parts of the compiler are implemented asa plugin for the PUMA system. As described above, we have splitthis plugin into two major parts: the planner and the weaver.

The planner is using internally sets of join points to represent point-cuts. Thus, the algebraic operators are implemented as operationson join point sets. As in AspectC++ join points can represent loca-tions in the control flow as well as other kinds of join points (i.e.classes), special attention has to be paid to the propagation of thejoin point type when join point sets are merged. The planner per-forms an extensive dynamic type checking on the join point setsto ensure the syntactical and semantical correctness of the manipu-lated source code. The C++ language dependent weaver is respon-sible to materialize the plan generated by the planner. By doingso the weaver heavily depends on code transformation operationsoffered by the PUMA library.

For the end-user the AspectC++ prototype compiler behaves like acompiler front-end (ac++). In contrast to traditional compilers ourfront-end transforms a whole program at once. While aspects canbe implemented in the same files as the component code, from ourexperience we suggest to putting aspect declarations in dedicatedaspect files.

4. RELATED WORKUsing aspect-oriented implementation techniques in conjunctionwith C++ does not seem to be very popular in the AOP community.Only very few contributions related to C++ can be found in the pro-ceedings of relevant conferences and workshops of the last years.We explain this with the overall “Java hype” and, more than that,with the lack of tool support. For instance, L. Dominick describes“life-cycle control aspects when applying the Command Processorpattern” and complains “because no weaver technology was avail-able, C preprocessor macros were used” [3].

A very interesting approach is followed by FOG [11]. FOG is ameta-compiler for C++ supporting a superset of the language. Sim-ilar to the AspectC++ implementation it is a source to source trans-lator, but the language concept differs. In FOG the C++ “One Def-inition Rule” is replaced by a “Composite Definition Rule”. Thisallows, for instance, to define multiple classes with the same name,which FOG will then merge into a single class. Functions and at-tributes can be easily added this way to classes. Function codecan be extended with a similar mechanism. While FOG seems tobe ideally suited for subject-oriented programming [4][7] the joinpoint model is much less powerful in comparison to AspectJ/C++.Especially the algebraic operations on “pointcuts” and the notionof control flow are useful in many aspect implementations.

More powerful than the FOG approach is OpenC++ [1]. It allowsa compiled C++ meta-program to manipulate the base-level C++code. Classes, types, and the syntax tree of the base-level are vis-ible on the meta-level and arbitrary transformations are supported.This implementation has a lot in common with the PUMA codetransformation system. OpenC++ allows aspect-oriented program-ming, but language extensions that especially support AOP are notprovided.

AspectC [2] is an aspect-oriented extension to plain C, which is cur-rently under development to study crosscutting concerns in operat-

ing system code. It is not planned to support C++ component codewith this implementation [5]. AspectC also adopts the key con-cepts from AspectJ, but the non-object-oriented nature of C forcesAspectC to leave out many useful features like using inheritance tocompose aspects. As C is basically a subset of C++ our AspectC++tools can be used with C as well.

5. CONCLUSIONS AND FUTURE WORKMuch of the work presented in this paper is based on AspectJ. Thisholds especially for the language introduction in section 2.1. How-ever, we have attempted to streamline the language extensions forthe use in conjunction with C++.

A major change compared to the AspectJ language is the modifiedjoin point model, which allows to have class, object and controlflow join points. The result is a more coherent language designand we would suggest to consider changing the AspectJ join pointmodel accordingly. While it was necessary to make some visiblechanges to the syntax and grammar of AspectJ, we have preservedmost language concepts. This should enable users experienced withAspectJ and C++ to get familiar with AspectC++ without mucheffort.

The prototype implementation of an AspectC++ compiler is stillin an early stage. The compiler cannot yet deal correctly with allAspectC++ language constructs in all possible contexts. However,the clear architecture of the compiler and the powerful tool supportin form of PUMA ease the further development of the AspectC++compiler. As the prototype implementation of the compiler we seealso the AspectC++ language as still under development and weare open for suggestions how to further optimize the syntax andsemantics.

Concerning future extensions we see room for improvement in thearea of the match expressions. We have implemented a PUMAmechanism for creating syntax tree fragments from a textual rep-resentation [10]. Currently we are analyzing the possibility to usethis syntax-oriented textual representation to create more powerfulmatch expressions.

We also intend to add mechanisms to AspectC++ to manipulatethe C++ class hierarchy (for example adding a new base class tocertain classes). AspectJ has also limited support for this feature,but we are still investigating a more general approach. We believethat our modified join point approach was already the right step inthis direction.

6. REFERENCES[1] S. Chiba. Metaobject Protocol for C++. InProceedings of the

ACM Conference on Object-Oriented Programming Systems,Languages, and Applications (OOPSLA), pages 285–299,Oct. 1995.

[2] Y. Coady, G. Kiczales, M. Feeley, N. Hutchinson, J. S. Ong,and S. Gudmundson. Exploring and Aspect-OrientedApproach to OS Code. InProceeding of the 4th ECOOPWorkshop on Object-Orientation and Operating Systems(ECOOP-OOSSWS’2001), pages 55 – 59. Universidad deOviedo, June 2001. ISBN 84-699-5329-X.

[3] L. Dominick. Aspect of Life-Cycle Control in a C++Framework. InProceedings of the Aspect-OrientedProgramming Workshop at ECOOP’99, Lisbon, Portugal,June 1999.

Page 6: AspectC++: Language Proposal and Prototype Implementation

[4] W. Harison and H. Ossher. Subject-Oriented Programming (aCritique on Pure Objects). InProceedings of the ACMConference on Object-Oriented Programming: Systems,Languages, and Applications (OOPSLA), pages 411–428,Woshington, D.C., Sept. 1993. ACM.

[5] G. Kiczales, July 2001. Personal communications.

[6] G. Kiczales, E. Hilsdale, J. Hugonin, M. Kersten, J. Palm,and W. G. Griswold. An Overview of AspectJ. In J. L.Knudsen, editor,ECOOP 2001 – Object-OrientedProgramming, volume 2072 ofLNCS. Springer-Verlag, June2001.

[7] H. Ossher and P. Tarr. Operation-Level Composition: A Casein (Join) Point. InProceedings of the Aspect-OrientedProgramming Workshop at ECOOP’98, Brussels, Belgium,July 1998.

[8] O. Spinczyk and M. Urban. The PUMA Project Homepage,2001.http://ivs.cs.uni-magdeburg.de/˜puma/.

[9] B. Stroustrup.The C++ Programming Language.Addison-Wesley, 1997.

[10] M. Urban. The PUMA User’s Manual, 2000.http://ivs.cs.uni-magdeburg.de/˜puma/.

[11] E. D. Willink and V. B. Muchnick. Weaving a Way Past theC++ One Definition Rule. InProceedings of theAspect-Oriented Programming Workshop at ECOOP’99,Lisbon, Portugal, June 1999.