AspectC++ Language Reference

Olaf Spinczyk and

pure-systems GmbH
Version 2.3, February 17, 2021

About

This document is intended to be used as a reference book for the AspectC++ language elements. It describes in-depth the use and meaning of each element providing examples. For experienced users the contents of this document are summarized in the AspectC++ Quick Reference. Detailed information about the AspectC++ compiler ac++ can be looked up in the AspectC++ Compiler Manual.
AspectC++ is an aspect-oriented extension to the C++ language
1
defined in the ISO/IEC 14882:1998(E) standard
. It is similar to AspectJ but, due to the nature of C++, in some points completely different. The first part of this document introduces the basic concepts of the AspectC++ language. The in-depth description of each language element is subject of the second part.

Basic Concepts

2.1 Pointcuts

2.1.1 Match Expressions

Example: match expressions (name pointcuts)

"int C::%(...)"
 
matches all member functions of the class C that return an int
"%List"
 
matches any namespace, class, struct, union, or enum whose name ends with List. In case of a matched namespace or class the match expression also matches entities inside the namespace resp. class. For more information see section
3.2.
"%
printf(const char *, ...)"  
matches the function printf (defined in the global scope) having at least one parameter of type const char * and returning any type
"const %& ...::%(...)"
 
matches all functions that return a reference to a constant object
Match expressions select program entities with respect to their definition scope, their type, and their name. A detailed description of the match expression semantics follows in section 3. The grammar which defines syntactically valid match expressions is shown in appendix B.

2.1.2 Pointcut Expressions

Name and code pointcuts can be combined in pointcut expressions by using the algebraic operators “&&”, “||”, and “!”.

Example: pointcut expressions

"%List" && !derived("Queue")
 
describes the set of classes with names that end with “List” and that are not derived from the class Queue
call("void
draw()") && within("Shape")  
describes the set of calls to the function draw that are within methods of the class Shape

2.1.3 Types of Join Points

According to the two types of pointcuts supported by AspectC++ there are also two coarse types of join points: name join points and code join points. As diagramed in figure 1 both of these have sub join point types. The types Any, Name, Code and Access are abstract types and exist just for categorizing the other join point types.

Figure 1 is extracted from the AspectC++ project repository hierarchy, that can be found in appendix C.
Based on a short code fragment the differences and relations between the types of join points shall be clarified.
class Shape { /*...*/ };
void draw(Shape& shape) { /*...*/ }

namespace Circle { 
  typedef int PRECISION;
  
  class S_Circle : public Shape {
    PRECISION m_radius;
    public:
      void radius(PRECISION r) {
        m_radius = r;
      }
      ~S_Circle() { /*...*/ }
  };
  
  void draw(PRECISION r) {
    S_Circle circle;
    circle.radius(r);
    draw(circle);
  }
}

int main() {
  Circle::draw(10);
  return 0;
}
Code join points are used to form code pointcuts and name join points (i.e. names) are used to form name pointcuts. Figure 2 shows join points of the code fragment above and how they correlate. Built-in constructors, destructors and uncalled operators are not shown. Additionally appendix D shows the contents of the project repository
3
The AspectC++ project repository is a file, that contains the internal AspectC++ model as xml-tree. The actual style and format of the content may change at any time. For more information see the AspectC++ Compiler Manual.
for the code fragment.

2.1.4 Pointcut declarations

A pointcut declaration is introduced by the keyword pointcut.

Example: pointcut declaration

pointcut lists() = derived("List");
 
lists can now be used everywhere in a program where a pointcut expression can be used to refer to derived("List")
Furthermore pointcut declarations can be used to define pure virtual pointcuts . This enables the possibility of having re-usable abstract aspects that are discussed in section 2.5. The syntax of pure virtual pointcut declarations is the same as for usual pointcut declarations except the keyword virtual following pointcut and that the pointcut expression is “0”.

Example: pure virtual pointcut declaration

pointcut virtual methods() = 0;
 
methods is a pure virtual pointcut that has to be redefined in a derived aspect to refer to the actual pointcut
expression

2.2 Attributes

2.3 Slices

A slice is a fragment of a C++ language element that defines a scope. It can be used by advice to extend the static structure of the program. For example, the elements of a class slice can be merged into one or more target classes by introduction advice. The following example shows a simple class slice declaration.

Example: class slice declaration

slice class Chain {
  Chain *_next;
public:
  Chain *next () const { return _next; }
};

2.4 Advice Code

Example: advice declaration

advice execution("void login(...)") : before() {
  cout << "Logging in." << endl;
}

Example: advice declaration with access to context information

pointcut new_user(const char *name) = 
  execution("void login(...)") && args(name); 
advice new_user(name) : before(const char *name) { 
  cout << "User " << name << " is logging in." << endl; 
}

2.4.1 Introductions

The second type of advice supported by AspectC++ are the introductions. Introductions are used to extend program code and data structures in particular. The following example extends two classes each by a member variable and a member function.

Example: introductions

pointcut shapes() = "Circle" || "Polygon";
advice shapes() : slice class {
  bool m_shaded;
  void shaded(bool state) {
    m_shaded = state;
  }
};

Example: base class introduction

advice "%Object" : slice class : public MemoryPool {
  virtual void release() = 0;
}

2.4.2 Advice Ordering

Example: advice ordering

advice call("% send(...)") : order("Encrypt", "Log");
If advice of both aspects (see 2.5) Encrypt and Log should be run when the function send(...) is called this order declaration defines that the advice of Encrypt has a higher precedence. More details on advice ordering and precedence can be found in section 9.

2.5 Aspects

Example: aspect declaration

aspect Counter { 
  static int m_count;
 
  pointcut counted() = "Circle" || "Polygon";
  advice counted() : slice struct {
    class Helper {
      Helper() { Counter::m_count++; }
    } m_counter;
  };
  advice execution("% main(...)") : after() {
    cout << "Final count: " << m_count << " objects"
         << endl;
  }
};
... and at an appropriate place
#include "Counter.ah"
int Counter::m_count = 0;
In this example the count of object instantiations for a set of classes is determined. Therefore, a member variable m_counter is introduced into the classes described by the pointcut incrementing a global counter on construction time. By applying advice code for the function main the final count of object instantiations is displayed when the program terminates.

Example: abstract aspect

aspect Counter { 
  static int m_count;
  Counter() : m_count(0) {}
  pointcut virtual counted() = 0;
  ...
};
It is now possible to inherit from Counter to reuse its functionality by reimplementing counted to refer to the actual pointcut expression.

Example: reused abstract aspect

aspect MyCounter : public Counter { 
  pointcut counted() = derived("Shape");
};

2.5.1 Aspect Instantiation

By default aspects in AspectC++ are automatically instantiated as global objects. The idea behind it is that aspects can also provide global program properties and therefore have to be always accessible. However in some special cases it may be desired to change this behavior, e.g. in the context of operating systems when an aspect shall be instantiated per process or per thread.

Example: aspect instantiation using aspectof

aspect ThreadCounter : public Counter {
  pointcut counted() = "Thread";
  advice counted() : ThreadCounter m_instance;
  static ThreadCounter *aspectof() {
    return tjp->target()->m_instance;
  }
};

2.6 Runtime Support

2.6.1 Support for Advice Code

types:
Result
result type
That
object type
Target
target type
AC::Type
encoded type of an object
AC::JPType
join point types
static methods:
int args()
number of arguments
AC::Type type()
typ of the function or attribute
AC::Type argtype(int)
types of the arguments
const char *signature()
signature of the function or variable
unsigned id()
identification of the join point
AC::Type resulttype()
result type
AC::JPType jptype()
type of join point
non-static methods:
void *arg(int)
actual argument
Result *result()
result value
That *that()
object referred to by this
Target *target()
target object of a call
void proceed()
execute join point code
AC::Action &action()
Action structure

Table 1: API of class JoinPoint available in advice code

Example: re-usable trace aspect

aspect Trace {
  pointcut virtual methods() = 0;
  advice execution(methods()) : around() {
    cout << "before " << JoinPoint::signature() << "(";
    for (unsigned i = 0; i < JoinPoint::args(); i++)
      printvalue(tjp->arg(i), JoinPoint::argtype(i));
    cout << ")" << endl;
    tjp->proceed();
    cout << "after" << endl;
  }
};

2.6.2 Actions

2.6.3 Support for Introductions

The JoinPoint API available in code introduced by an introduction is listed in the following table.
static methods:
const char *signature()
signature of the function or member variable
unsigned id()
identification of the join point
AC::JPType jptype()
type of join point
types:
Aspect
type of the aspect
Table 2: JoinPoint API for introductions
In difference to the API available for advice code the API for introduction only provides static information about a join point. A nice demonstration of this API is shown in the following example.

Example: static type identification using introductions

aspect TypeInfo {
  pointcut virtual typed() = 0;
  advice typed() : static unsigned type_id() {
    return JoinPoint::id();
  }
  advice typed() : virtual unsigned type() { 
    return type_id(); 
  }
};
The first introduction of this aspect introduces a static method type_id into a set of classes returning an unique integer value. By introducing a second non-static but virtual method type into these classes also returning the unique integer value a type identification can be realized like this:
if (obj->type() == AClass::type_id())
  ...
else if (obj->type() == AnotherClass::type_id())
  ...
This implements a nice alternative to the C++ RTTI mechanism especially when the RTTI support of a compiler is switched off.

Example: extended thread counting

aspect Counter { 
  int m_count;
  ...
  advice counted() : class Helper {
    Helper() { Aspect::aspectof()->m_count++; }
  } m_counter;
  ...
};

Match Expressions

Match expressions are a used to describe a set of statically known program entities in a C++ source code. These program entities correspond to name join points. Therefore a match expression always returns a name pointcut. There can be match expressions for namespaces, classes, functions or variables.

3.1 Commonly Used Matching Mechanisms

The grammar used for match expression parsing is shown in appendix B. The following subsections separately describe the name, scope, and type matching mechanisms. All of them are used in match expressions of functions and variables, while match expressions of namespaces and classes only uses name and scope matching.

3.1.1 Name Matching

Example: simple name patterns
Token
only matches Token
%
matches any name
parse_%
matches any name beginning with parse_ like parse_declarator or parse_
parse_%_id%
matches names like parse_type_id, parse_private_identifier, etc.
%_token
matches all names that end with _token like start_token, end_token, and _token

3.1.2 Scope Matching

A scope pattern matches the definition scope of a compared function or type if every part can successfully be matched with a corresponding part in the qualified name of the definition scope. The compared qualified name has to be relative to the global scope and should not start with ::, which is optional in a C++ nested-name-specifier. The special ... pattern matches any (even empty) sequence of scope names. If no scope pattern is given, a compared namespace, class, function or variable has to be defined in the global scope to be matched.
Example: scope patterns

3.1.3 Type Matching

C++ types can be represented as a tree. For example, the function type int(double) is a function type node with two children, one is an int node, the other a double node. Both children are leaves of the tree.
For comparing a type pattern with a specific type the tree representation is used and the any type node matches an arbitrary type (sub-)tree.
Example: type patterns with the wildcard character
%
matches any type
void (*)(%)
matches any pointer type that points to functions with a single argument and a void result type
%*
matches any pointer type
Matching of Named Types
Matching of “Pointer to Member” Types
Matching of Qualified Types (const/volatile)
Example: type patterns with const and volatile
%
matches any type, even types qualified with const or volatile
const %
matches only types qualified by const
% (*)() const volatile
matches the type of all pointers to functions that are qualified by const and volatile
Handling of Conversion Function Types
Ellipses in Function Type Patterns
Matching Virtual Functions
The decl-specifier-seq of a function type match expression may include the keyword virtual. In this case the function type match expression only matches virtual or pure virtual member functions. As const and volatile, the virtual keyword is regarded as a restriction. This means that a function type match expression without virtual matches virtual and non-virtual functions.
Example: type patterns with virtual
virtual % ...::%(...)
matches all virtual or pure virtual functions in any scope
% C::%(...)
matches all member functions of C, even if they are virtual
Matching Static Functions
Matching static functions works similar as matching virtual functions. The decl-specifier-seq of a function type match expression may include the keyword static. In this case the function type match expression only matches static functions in global or namespace scope and static member functions of classes. As const and volatile, the static keyword is regarded as a restriction. This means that a function type match expression without static matches static and non-static functions.
Example: type patterns with static
static % ...::%(...)
matches all static member and non-member functions in any scope
% C::%(...)
matches all member functions of C, even if they are static
Argument Type Adjustment

3.2 Namespace and Class Match Expressions

For namespaces and classes the matching process is special because it consists of two steps.
First, each namespace and class is compared with a given match expression. A match expression that matches a namespace or class begins with the optional scope part and ends with the required name part. In course of this step the matching name join points are collected in a temporary pointcut.

Example: scope and name parts of a namespace or class match expression

"Puma::...::Parser%"
This match expression describes the following requirements on a compared namespace or class:
scope:
the scope in which the namespace or class is defined has to match Puma::...::
name:
the name of the namespace or class has to match the name pattern Parser%
In the second step the temporary pointcut will be extended by contained name join points yielding the result pointcut. The extension rules are as follows:
  • If a namespace N is matched, the resulting pointcut additionally contains the following name join points:
    all functions, variables, (nested) classes, member functions, data members, constructors and destructors that are anywhere and arbitrary nested inside N.
  • If a class C is matched, the resulting pointcut additionally contains the following name join points:
    all member functions, data members and constructors of C as well as the destructor of C that are directly located inside C. So name join points that are nested inside a member function, a data member or a nested class are not added to the pointcut.
The following list contains example match expressions and the results after the first as well as after the second step.
after step one
result
Token
only matches namespaces or classes with the name Token that are directly inside the global namespace
...::Token
matches Token at arbitrary location
%
matches any namespace or class that is directly located in the global namespace but not the global namespace itself
matches any namespace except the global namespace, any class that is arbitrary nested in a non-global namespace, any class directly located in the global namespace and all functions, member functions, variables, data members, constructors and destructors that are contained in one of the just mentioned entities
::
matches the global namespace
matches any function, variable, (nested) class, member function, data member, constructor or destructor
after step one
result
OOStuBS::CGA%
matches any namespace or class inside OOStuBS beginning with CGA like OOStuBS::CGA, OOStuBS::CGA_Screen or OOStuBS::CGA_Stream. Note that this matches OOStuBS only inside the global namespace.
%::Smtp%Bldr%
matches namespaces and classes like SmtpBldr, SmtpClientBldr or SmtpServerBldrCreator, that are nested in exact one namespace or class.
%Node
matches any namespace or class ending with Node like ModelNode, GraphNode and Node
Please note that local classes inside functions or member functions are never matched.

3.3 Function Match Expressions

For function (or member function) matching a match expression is internally decomposed into the function type pattern , the scope pattern , and the name pattern .

Example: type, scope, and name parts of a function match expression

"const % Puma::...::parse_% (Token *)"
This match expression describes the following requirements on a compared function name:
name:
the function name has to match the name pattern parse_%
scope:
the scope in which the function is defined has to match Puma::...::
type:
the function type has to match const %(Token *)
If an entity matches all parts of the match expression, it becomes an element of the pointcut, which is defined and returned by the match expression.
Common descriptions of name, scope and type matching can be found in section 3.1. The following sections additionally describe the name matching of special functions.

3.3.1 Operator Function and Conversion Function Name Matching

The name matching mechanism is more complicated if the pattern is compared with the name of a conversion function or an operator function. Both are matched by the name pattern % . However, with a different name pattern than % they are only matched if the pattern begins with "operator ". The pattern "operator %" matches any operator function or conversion function name.
Example: operator name patterns
operator %
matches any operator function name (as well as any conversion function name)
operator +=
matches only the name of a += operator
operator %%
matches the name of an operator %
Example: conversion function name patterns
operator %
matches any conversion function name
operator int*
matches any name of a conversion that converts something into an int* object
operator %*
matches any conversion function name if that function converts something into a pointer

3.3.2 Constructors and Destructors

Name patterns cannot be used to match constructor or destructor names.

3.4 Variable Match Expressions

Example: type, scope, and name parts of a variable match expression

"const % Puma::...::parsed_%"
This match expression describes the following requirements on a compared variable name:
name:
the variable name has to match the name pattern parsed_%
scope:
the scope in which the variable is defined has to match Puma::...::
type:
the variable type has to match const %
If an entity matches all parts of the match expression, it becomes an element of the pointcut, which is defined and returned by the match expression.

Predefined Pointcut Functions

On the following pages a complete list of the pointcut functions supported by AspectC++ is presented. For every pointcut function it is indicated which type of pointcut is expected as argument(s) and of which type the result pointcut is. Thereby “N” stands for name pointcut and “C” for code pointcut. The optionally given index is an assurance about the type of join point(s) described by the result pointcut
7
C, C C , C E , C B , C S , C G : Code (any, only Call (without Builtin), only Execution, only Builtin, only Set, only Get); N, N N , N C , N F , N T N V : Names (any, only Namespace, only Class, only Function, only Type, only Variable)
. If a pointcut is used as argument of a pointcut function and the type of some join points in argument pointcut does not match one of the expected argument types of the pointcut function, these non-matching join points are silently ignored.

4.1 Types

base(pointcut)
N C,F,V N C,F,V
returns a pointcut p b of name join points created as follows
p b { all base classes of classes in pointcut but not the classes in pointcut } ,
p b p b ||{ all member functions and data members of classes in p b } ,
p b p b ||{ all previous definitions of member functions in pointcut but not the member functions in pointcut } ,
p b p b ||{ all previous definitions of data members in pointcut but not the data members in pointcut }
derived(pointcut)
N C,F,V N C,F,V
returns a pointcut p d of name join points created as follows
p d { all classes in pointcut and all classes derived from them } ,
p d p d ||{ all member functions and data members of classes in p d } ,
p d p d ||{ all member functions in pointcut and all redefinitions of these member functions in derived classes } ,
p d p d ||{ all data members in pointcut and all redefinitions of these data members in derived classes }

Example: derived function matching

struct A {};
struct B : public A { void f(); };
struct C : public B { void f(); };
aspect Z {
  advice execution(derived("A")) : before() {
    // before execution of B::f() or C::f()
  }
};

Example: type matching

A software may contain the following class hierarchy.
class Shape { ... };
class Scalable { ... };
class Point : public Shape { ... };
...
class Rectangle : public Line, public Rotatable { ... };
With the following aspect a special feature is added to a designated set of classes of this class hierarchy.
aspect Scale {
  pointcut scalable() = "Rectangle" ||
    (base("Rectangle") && derived("Point"));
  advice "Point" : slice class : public Scalable;
  advice scalable() : slice class { 
      void scale(int value) { ... }
  };
};
The pointcut describes the classes Point and Rectangle and all classes derived from Point that are direct or indirect base classes of Rectangle. With the first advice Point gets a new base class. The second advice adds a corresponding method to all classes in the pointcut.

4.2 Control Flow

cflow(pointcut)
C C
captures join points occurring in the dynamic execution context of join points in pointcut. Currently the language features being used in the argument pointcut are restricted. The argument is not allowed to contain any context variable bindings (see
4.8) or other pointcut functions which have to be evaluated at runtime like cflow(pointcut) itself.

Example: control flow dependant advice activation

The following example demonstrates the use of the cflow pointcut function.
class Bus {
  void out (unsigned char);
  unsigned char in ();
};
Consider the class Bus shown above. It might be part of an operating system kernel and is used there to access peripheral devices via a special I/O bus. The execution of the member functions in() and out() should not be interrupted, because this would break the timing of the bus communication. Therefore, we decide to implement an interrupt synchronization aspect that disables interrupts during the execution of in() and out():
aspect BusIntSync {
  pointcut critical() = execution("% Bus::%(...)");
  advice critical() && !cflow(execution("% os::int_handler()")) : around() {
    os::disable_ints();
    tjp->proceed();
    os::enable_ints();
  }
};

4.3 Scope

within(pointcut)
N C
returns all code join points that are located directly inside or at a name join point in pointcut
member(pointcut)
N N
maps the scopes given in pointcut to any contained named entities. Thus a class name for example is mapped to all contained member functions, variables and nested types.

Example: matching in scopes

aspect Logger {
  pointcut calls() =
    call("void transmit()") && within("Transmitter");
  advice calls() : around() {
    cout << "transmitting ... " << flush;
    tjp->proceed();
    cout << "finished." << endl;
  }
};
This aspect inserts code logging all calls to transmit that are within the methods of class Transmitter.

4.4 Functions

call(pointcut)
N F C C
returns all code join points where a user provided function or member function in pointcut is called. The resulting join points are located in the scope of the resp. caller meaning where the function or member functions is called. The pointcut does not include join points at calls to built-in operators.
execution(pointcut)
N F C E
returns all code join points where a function or member function in pointcut is executed. The resulting join points are located in the scope of the callee meaning where the function or member function is defined/implemented.

Example: function matching

The following aspect weaves debugging code into a program that checks whether a method is called on a null pointer and whether the argument of the call is null.
aspect Debug {
  pointcut fct() = "% MemPool::dealloc(void*)";
  pointcut exec() = execution(fct());
  pointcut calls() = call(fct());

  advice exec() && args(ptr) : before(void *ptr) {
    assert(ptr && "argument is NULL");
  }
  advice calls() : before() {
    assert(tjp->target() && "'this' is NULL");
  }
};
The first advice provides code to check the argument of the function dealloc before the function is executed. A check whether dealloc is called on a null object is provided by the second advice. This is realized by checking the target of the call.

4.5 Built-in Operators

builtin(pointcut)
N F C B
returns all code join points where a built-in operator in pointcut is called.
This pointcut function does not return join points at constructor or destructor calls. See section
Section 4.6 (4.6) to find out how to describe these join points.
The builtin pointcut function is a new feature that was introduced in version 2.0 and is therefore not enabled by default to avoid compatibility issues (e.g., if someone named a pointcut “builtin”). The command-line argument enables the described functionality.
The intersection of the results of call and builtin always yields the empty pointcut:
call(pointcut) && builtin(pointcut) = pointcut

Example: operator matching

The following aspect weaves code into a program that checks whether a null-pointer will be dereferenced. If this occurs, the advice will provide the code position on the error stream.
aspect ProblemReporter {
  advice builtin("% operator *(%)") : before() {
    if(*tjp->arg<0>() == 0) {    
      cerr << tjp->filename() << " (Line " << tjp->line() << "): dereferencing of null-pointer!" << endl;
    }
  }
};

4.5.1 Limitations

Some built-in operators could not be fully supported. For example, weaving advice code for built-in operators in constant expressions would destroy the constancy of the expressions and inhibit evaluation at compile time. Therefore, operators in constant expressions are not matched. The following code listing gives some examples for operators in constant expressions.A further limitation results from the fact, that the C++-standard forbids pointers and references to bit-fields. Thus all operators that refer to a bit-field (e.g. the assignment- or increment-/decrement-operator needs a reference as first argument) are not supported.

Moreover any operator that has an anonymous/unnamed or local type or a type with no linkage as argument or result is not supported (because these types shall not be used as a template argument which makes weaving impossible in most cases).

Additionally postfix increment/decrement operators have a second implicit argument of type int to distinguish between pre- and postfix operators. So e.g. “% operator ++(%, int)” matches the postfix increment operator and “% operator ++(%)” matches the prefix increment operator.

Also the address-of operator & is not supported, if the argument is a data member or member function, because these types do not exist as type of a variable.

Furthermore the C++-standard states that if the result of .* or ->* is a function, that result can be used only as the operand for the function call operator (). Therefore the pointer to member operators .* and ->*
that get a member function pointer as second argument are not supported, because a caching of the result is not possible.

At last there are some limitations with the short-circuiting
operators &&, || and ?:. If the second or third argument is not evaluated, tjp->args() will return a null-pointer for the corresponding argument. Additionally the result of the args pointcut function (see 4.8) is determined at runtime, if an short-circuit argument is bound with the args pointcut function . Thus the advice code in the following example is only executed, if the first argument evaluates to true so that the second argument is available. In case of || the first argument have to be false to make the second argument available and in case of ?: the first argument makes the decision about the availability of the second resp. third argument. A complete list with all limitations and not supported operators can be found in the next section 4.5.2.
class ExampleClass {
  static const int const_member = 5 * 2;
  unsigned int bitfield : 4 / 2;
}; 
const int const_two = 3 - 1;
static char char_array[const_two + 5];
enum ExampleEnum {
  ENUM_VALUE = const_two + 1
};
switch(const int const_temp = 1) {
  case const_temp + 1: {
    // ...
    break;
  }
}
advice builtin("% operator &&(bool, bool)") && args("%", b2) : before(bool b2) {
  // advice code
}

4.5.2 Supported And Not Supported Operators

This section contains information about the builtin pointcut function in terms of supported operators.
Table
3 shows all operators that are fully or partly supported and indicates the special characteristics of these operators, if available. For more information see section 4.5.1.
Table 4 shows not supported operators.
operator and example
special characteristics
ternary
?:
a?b:c
limitations due to short-circuit evaluation (see 4.5.1)
unary
++
a++
postfix operator (second argument of type )
unary
--
a--
postfix operator (second argument of type )
unary
++
++a
prefix operator; not supported if is a bit-field
unary
--
--a
prefix operator; not supported if is a bit-field
unary
&
&a
not supported if is a member
unary
*
*a
unary
+
+a
unary
-
-a
unary
a
unary
!
!a
binary
.*
a.*b
not supported if is a member function pointer
binary
->*
a->*b
not supported if is a member function pointer
binary
*
a*b
binary
/
a/b
binary
%
a%b
in match expressions: escape % with %%
binary
+
a+b
binary
-
a-b
binary
<<
a<<b
binary
>>
a>>b
binary
<
a<b
binary
>
a>b
binary
<=
a<=b
binary
>=
a>=b
binary
==
a==b
binary
!=
a!=b
binary
&
a&b
binary
a‸b
binary
|
a|b
binary
&&
a&&b
limitations due to short-circuit evaluation (see 4.5.1)
binary
||
a||b
limitations due to short-circuit evaluation (see 4.5.1)
binary
=
a=b
copy-assignment not supported;
not supported if is a bit-field
binary
*=
a*=b
not supported if is a bit-field
binary
/=
a/=b
not supported if is a bit-field
binary
%=
a%=b
in match expressions: escape %= with %%=;
not supported if is a bit-field
binary
+=
a+=b
not supported if is a bit-field
binary
-=
a-=b
not supported if is a bit-field
binary
<<=
a<<=b
not supported if is a bit-field
binary
>>=
a>>=b
not supported if is a bit-field
binary
&=
a&=b
not supported if is a bit-field
binary
|=
a|=b
not supported if is a bit-field
binary
‸=
a‸=b
not supported if is a bit-field
binary
[]
a[b]
operator and example
binary
,
a,b
binary
->
a->b
binary
.
a.b
new
new a
delete
delete a
new[]
new[] a
delete[]
delete[] a
implicit conversions
operators in constant expressions (see 4.5.1)
operators with an anonymous/unnamed or local type (see 4.5.1)
operators that have a type with no linkage (see 4.5.1)

4.6 Object Construction and Destruction

construction(pointcut)
N C C Cons
returns all code join points where an instance of a class in pointcut is constructed. The construction join point begins after all base class and member construction join points. It can be imagined as the execution of the constructor. However, advice for construction join points work, even if there is no constructor defined explicitly. A construction join point has arguments and argument types, which can be exposed or filtered, e.g. by using the args pointcut function.
destruction(pointcut)
N C C Des
returns all code join points where an instance of a class in pointcut is destructed. The destruction join point ends before the destruction join point of all members and base classes. It can be imagined as the execution of the destructor, although a destructor does not to be defined explicitly. A destruction join point has an empty argument list.

Example: instance counting

The following aspect counts how many instances of the class ClassOfInterest are created and destroyed.
aspect InstanceCounting {
  // the class for which instances should be counted
  pointcut observed() = "ClassOfInterest";
  // count constructions and destructions
  advice construction (observed ()) : before () {
                                        _created++; }
  advice destruction (observed ())  : after ()  {
                                        _destroyed++; }
  // counters
  int _created, _destroyed;
public:
  // Singleton aspects can have a default constructor
  InstanceCounting () { _created = _destroyed = 0; }
}; 
The implementation of this aspect is straightforward. Two counters are initialized by the aspect constructor and incremented by the construction/destruction advice. By defining observed() as a pure virtual pointcut the aspect can easily be transformed into a reusable abstract aspect.

4.7 Variables

get(pointcut)
N V C G
returns all code join points where a global variable or data member in pointcut is read. The get join points are located at implicit lvalue-to-rvalue conversions according to the C++ standard. In addition, the get join points are located within all built-in compound-assignment operators, and within the built-in increment and decrement operators.
set(pointcut)
N V C S
returns all code join points where a global variable or data member in pointcut is modified. The set join points are located within all built-in assignment operators, and within the built-in increment and decrement operators. The initialization of a global variable or data member provides no set join point.
ref(pointcut)
N V C R
provides all join points where a reference (reference type or pointer) to a global variable or data member in the pointcut is created. The ref join points are located within the built-in address-of operator &, if the operand is a global variable or data member. In addition, the ref join points are located before the initialization of a variable of reference type, including return values. Moreover, the binding of a reference parameter of a function, including default values, provides ref join points. The ref join points are also located within implicit array-to-pointer conversions according to the C++ standard.

Example: variable matching

The following aspect observes the modification of all variables (in any scope) of the type int. When such an integer variable is modified, the aspect reports the name of the variable and its new value, obtained by *tjp->entity().
aspect IntegerModification {
  advice set("int ...::%") : after() {
    cout << "Setting variable "
         << tjp->signature() << " to "
         << *tjp->entity() << endl;
  }
};

4.7.1 Limitations

The get and set pointcut functions cover variables of fundamental type, such as integer and floating-point types, and arrays thereof. Variables of any pointer type and arrays of pointers are also supported. The get and set pointcut functions do not support variables of class type, unions, enumerations, bitfields, and references.

The get, set, and ref pointcut functions match only if the variable is accessed directly by its name. Indirect variable access via pointer or reference does not match.

The get, set, and ref pointcut functions do not match for local variables.

The get, set, and ref joinpoints are not located within constant expressions, such as the built-in operator sizeof.

4.7.2 Compatibility

The get, set, and ref pointcut functions are new features that were introduced in version 2.0 and are therefore not enabled by default to avoid compatibility issues (e.g., if someone named a pointcut “get”). The command-line argument enables the described functionality.

4.8 Context

that(type pattern)
N T C
returns all code join points where the current C++ this pointer refers to an object which is an instance of a type that is compatible to the type described by type pattern
target(type pattern)
N T C
returns all code join points where the target object of a call/set/get is an instance of a type that is compatible to the type described by type pattern
result(type pattern)
N T C
returns all code join points where the type of the return value of a call/builtin/execution/get is matched by type pattern
args(type pattern, ...)
(N T ,...) C
returns all code join points where the types of the arguments of a call/builtin/execution/set are matched by the corresponding type patterns.
The argument list of args contains type patterns that are used to filter all join points, e.g. calls to functions or function executions, with a matching signature.
Instead of the type pattern it is also possible here to pass the name of a variable to which the context information is bound (a context variable). In this case the type of the variable is used for the type matching. Context variables must be declared in the argument list of before(), after(), or around() and can be used like a function parameter in the advice body.
The that() and target() pointcut functions are special, because they might cause a runtime type check. The args() and result() functions are evaluated at compile time. Exception: If a short-circuit argument is bound with the args pointcut function, then the result of args depends on the runtime availability of the bound argument.

Example: context matching

4.9 Algebraic Operators

pointcut && pointcut
(N,N) N, (C,C) C
returns the intersection of the join points in the pointcuts
pointcut || pointcut
(N,N) N, (C,C) C
returns the union of the join points in the pointcuts
! pointcut
N N, C C
returns all name resp. code join points that are not included in pointcut
exclusion of the join points in the pointcut

Example: combining pointcut expressions

Attributes

Attributes are a language element, which AspectC++ developers can use for user-defined annotations. An attribute provides additional information about a join point that aspects can exploit for collecting or filtering pointcuts. The attribute syntax is based on the attribute syntax of C++11 (and following standards). However, AspectC++ provides this mechanism even if the selected language standard is older than C++11. In this case no attributes from the namespaces gnu or clang or the global scope must be used.

5.1 Attribute declarations

Attributes must be declared before being used in a pointcut expression. An attribute that is used for annotating a join point must be declared, but it is not required that the declaration is seen by the parser before the annotation location. The following example shows such a declaration using the keyword attribute.

Example: attribute declaration

attribute myAttr();
To avoid naming conflicts, attributes can be declared inside of namespaces, classes, or aspects. User-defined attributes shall neither be declared in the global scope nor in the scope of backend compiler attributes, such as gnu or clang. The current version of AspectC++ supports only empty argument lists. To annotate an element of the program code, the attribute has to be referenced by its fully-qualified name. The following example illustrates this:

Example: using attributes to annotate program elements

namespace attrib {
  attribute myFuncAttr();
  attribute otherAttr();
}
[[attrib::myFuncAttr, attrib::otherAttr()]] void myFunc();
To be compatible with C++11 attributes, it is not necessary to specify parameters if the argument list of an attribute is empty. Furthermore, it is possible to use several attributes in a pair of brackets separated by commas or several pairs of brackets behind each other. For more information about attribute-syntax in C++11 consult the C++11 standard.
Attributes from the namespaces gnu and clang and the global scope are evaluated by AspectC++ and passed through to the backend compiler. All other attributes are only evaluated by AspectC++ and hidden from the backend compiler.

5.2 Supported code-elements

Table 6 shows the code elements, for which annotations with attributes are supported, and the possible attribute locations. Positions of attributes are marked by [[..]].
Code-Element
Positions
namespaces
namespace [[...]] myNamespace {}
classes
class [[...]] myClass {};
functions
[[...]] void myFunc [[...]] ();
variables
[[...]] int myVar [[...]];
statements
[[...]] i += 1; [[...]] { i--; }
If a namespace is opened more than once, all enclosed elements belong semantically to the same namespace. In this case, all attributes of that namespace must be present at its first definition. In subsequent definitions they can be present as well, but don't have to. It is forbidden to add an attribute in a subsequent definition, which was not present in the first. A similar rule is applied for classes, functions, and variables, which can have multiple forward declarations. In this case, all attributes must be present at the first declaration and can be omitted later on. Attributes on statements (and compound statements) can be used to filter join points that are located within the marked code region.

5.3 Attributes and pointcut expressions

Attributes can be used in pointcut expressions where they are interpreted similar to named pointcuts. They can be combined with logical operators like other pointcut expressions and can be used in pointcut declarations. Thereby, the usual C++ name lookup rules are also applicable for attributes. The following example shows how to use attributes in pointcut expressions.

Example: using attributes in pointcut expressions

struct [[output::myAttr]] myStruct {
  [[output::myAttr]] void myFunc() {};
};
aspect output {
  attribute myAttr();
  pointcut all() = myAttr();
};
If multiple name joinpoints, such as the namespace N and the class C, are annotated by an attribute A, the meaning of A() in a pointcut expression is equivalent to “N”||”C”. This means that also nested entities within N and C are matched.

Slices

This section defines the syntax and semantics of slice declarations. The next section will describe how slices can be used by advice in order to introduce code. Currently, only class slices are defined in AspectC++.

6.1 Class Slice Declarations

Class slices may be declared in any class or namespace scope. They may be defined only once, but there may be an arbitrary number of forward declarations. A qualified name may be used if a class slice that is already declared in a certain scope is redeclared or defined as shown in the following example:
slice class ASlice;
namespace N {
 slice class ASlice; // a different slice!
}
slice class ASlice { // definition of the ::ASlice
  int elem;
};
slice class N::ASlice { // definition of the N::ASlice
  long elem;
};
If a class slice only defines a base class, an abbreviated syntax may be used:
slice class Chained : public Chain;
Class slices may be anonymous. However, this only makes sense as part of an advice declaration. A class slice may also be declared with the aspect or struct keyword instead of class. While there is no difference between class and aspect slices, the default access rights to the elements of a struct slice in the target classes are public instead of private. It is forbidden to declare aspects, pointcuts, advice, or slices as members of a class slice.
Class slices may have members that are not defined within the body of a class slice declaration, e.g. static member variable or non-inline functions:
slice class SL {
  static int answer;
  void f();
};
//...
slice int SL::answer = 42;
slice void SL::f() { ... }
These external member declarations have to appear after the corresponding slice declaration in the source code.

Advice

This section describes the different types of advice offered by AspectC++. Advice are categorized in advice for join points in the dynamic control flow of the running program, e. g. function call or executions, and advice for static join points like introductions into classes.
In either case the compiler makes sure that the code of the aspect header file, which contains the advice definition (if this is the case), is compiled prior to the affected join point location.

7.1 Advice for Dynamic Join Points

before(...)
 
the advice code is executed before the join points in the pointcut
after(...)
 
the advice code is executed after the join points in the pointcut
around(...)
 
the advice code is executed in place of the join points in the pointcut

7.2 Advice for Static Join Points

Static join points in AspectC++ are classes or aspects. Advice for classes or aspects can introduce new members or add a base class. Whether the new member or base class becomes private, protected, or public in the target class depends on the protection in the advice declaration in the aspect.
baseclass(classname)
 
a new base class is introduced to the classes in the pointcut
introduction declaration
 
a new member variable, member function, or type is introduced
Introduction declarations are only semantically analyzed in the context of the target. Therefore, the declaration may refer, for instance, to types or constants, which are not known in the aspect definition, but only in the target class or classes. To introduce a constructor or destructor the name of the aspect, to which the introduction belongs, has to be taken as the constructor/destructor name.
Non-inline introductions can be used for introductions of static member variables or member function introduction with separate declaration an definition. The name of the introduced member has to be a qualified name in which the nested name specifier is the name of the aspect to which the introduction belongs.

JoinPoint API

The following sections provide a complete description of the JoinPoint API.

8.1 API for Dynamic Join Points

The JoinPoint-API for dynamic join points can be used within the body of advice code.

8.1.1 Types and Constants

Result
 
result type of a function
Res::Type,
Res::ReferredType  
result type of the affected function or entity access
Arg<i>::Type,
Arg<i>::ReferredType  
type of the argument of the affected join point (with )
ARGS
   
number of arguments
That
 
object type (object initiating a call)
Target
 
target object type (target object of a call)
Entity
 
type of the primary referenced entity (function or variable)
MemberPtr
 
type of the member pointer for entity or void * for nonmembers
Array
 
type of the accessed array
Dim<i>::Idx
 
type of the ith dimension of the accessed array (with )
Dim<i>::Size
 
size of the ith dimension of the accessed array (with )
DIMS
   
number of dimensions of an accessed array or 0 otherwise
Aspect
 
type of the aspect (only available in introductions)

Example: type usage

8.1.2 Functions

static AC::Type type()
 
returns the encoded type for the join point conforming with the C++ ABI V3 specification
static int args()
 
returns the number of arguments of a function for call and execution join points
static AC::Type argtype(int number)
 
returns the encoded type of an argument conforming with the C++ ABI V3 specification
static const char *signature()
 
gives a textual description of the join point (function name, class name, ...)
static unsigned int id()
 
returns a unique numeric identifier for this join point
static const char *filename()
 
returns the name of the file in which the join point (shadow) is located
static int line()
 
the number of the line in which the join point (shadow) is located
static AC::Type resulttype()
 
returns the encoded type of the result type conforming with the C++ ABI V3 specification
static AC::JPType jptype()
 
returns a unique identifier describing the type of the join point

Example: static function usage

void *arg(int number)
 
returns a pointer to the memory position holding the argument value with index number
Result *result()
 
returns a pointer to the memory location designated for the result value or 0 if the function has no result value
That *that()
 
returns a pointer to the object initiating a call or 0 if it is a static method or a global function
Target *target()
 
returns a pointer to the object that is the target of a call or 0 if it is a static method or a global function
Entity *entity()
 
returns a pointer to the accessed entity (function or variable) or 0 for member functions or builtin operators
MemberPtr *memberptr()
 
returns a member pointer to entity or 0 for nonmembers
Array *array()
 
returns a typed pointer to the accessed array
Dim<i>::Idx idx<i>()
 
returns the value of the ith index used for the array access
void proceed()
 
executes the original join point code in an around advice by calling action().trigger()
AC::Action &action()
 
returns the runtime action object containing the execution environment to execute the original functionality encapsulated by an around advice

Example: non-static function usage

8.2 API for Static Join Points

The JoinPoint-API for static join points can be used within the definition of a slice and describes the state of target class before the introduction took place. It is accessed through the built-in type JoinPoint (e.g. JoinPoint::signature()) and provides the following functions, types, and constants:
static const char *signature()
 
returns the target class name as a string
That
 
The (incomplete) target type of the introduction
HASHCODE
 
integer hash value of the target type
BASECLASSES
 
number of base classes of the target class
BaseClass<I>::Type
 
type of the base class
BaseClass<I>::prot, BaseClass<I>::spec
 
Protection level (AC::PROT_NONE /PRIVATE /PROTECTED /PUBLIC) and additional specifiers (AC::SPEC_NONE /VIRTUAL) of the base class
MEMBERS
 
number of data members of the target class
Member<I>::Type, Member<I>::ReferredType
 
type of the member variable of the target class
Member<I>::prot, Member<I>::spec
 
Protection level (see BaseClass<I>::prot) and additional member variable specifiers (AC::SPEC_NONE /STATIC /MUTABLE)
static ReferredType *Member<I>::pointer(T *obj=0)
 
returns a typed pointer to the member variable (obj is needed for non-static member variables)
static const char *Member<I>::name()
 
returns the name of the member variable
FUNCTIONS
 
number of member functions of the target class
Function<I>::prot, Function<I>::spec
 
Protection level (see BaseClass<I>::prot) and additional member variable specifiers (AC::SPEC_NONE /STATIC /VIRTUAL)
CONSTRUCTORS
 
number of user-defined constructors of the target class
Constructor<I>::prot, Constructor<I>::spec
 
Protection level (see BaseClass<I>::prot) and additional member variable specifiers (AC::SPEC_NONE)
DESTRUCTORS
 
number (zero or one) of user-defined destructors of the target class
Destructor<I>::prot, Destructor<I>::spec
 
Protection level (see BaseClass<I>::prot) and additional member variable specifiers (AC::SPEC_NONE /VIRTUAL)

Advice Ordering

9.1 Aspect Precedence

AspectC++ provides a very flexible mechanism to define aspect precedence. The precedence is used to determine the execution order of advice code if more than one aspect affects the same join point. The precedence in AspectC++ is a member of a join point. This means that the precedence relationship between two aspects might vary in different parts of the system. The compiler checks the following conditions to determine the precedence of aspects:
order declaration:
if the programmer provides an order declaration, which defines the precedence relationship between two aspects for a join point, the compiler will obey this definition or abort with a compile-time error if there is a cycle in the precedence graph. Order declarations have the following syntax:
advice
pointcut-expr : order ( high, ...low )
The argument list of order has to contain at least two elements. Each element is a pointcut expression, which describes a set of aspects. Each aspect in a certain set has a higher precedence than all aspects, which are part of a set following later in the list (on the right hand side). For example '("A1"||"A2","A3"||"A4")' means that A1 has precedence over A3 and A4 and that A2 has precedence over A3 and A4. This order directive does not define the relation between A1 and A2 or A3 and A4. Of course, the pointcut expressions in the argument list of order may contain named pointcuts and even pure virtual pointcuts.
inheritance relation:
if there is no order declaration given and one aspect has a base aspect the derived aspect has a higher precedence than the base aspect.

9.2 Advice Precedence

The precedence of advice is determined with a very simple scheme:

9.3 Effects of Advice Precedence

Only advice precedence has an effect on the generated code. The effect depends on the kind of join point, which is affected by two advice declarations.

Class Join Points

Advice on class join points can extend the member variable list or base class list. If advice has a higher precedence than another it will be handled first. For example, an introduced new base class of advice with a high precedence will appear in the base class list on the left side of a base class, which was inserted by advice with lower precedence. This means that the execution order of the constructors of introduced base classes can be influenced, for instance, by order declarations.
The order of introduced member variables also has an impact on the constructor/destructor execution order as well as the object layout.

Code Join Points

Advice on code join points can be before, after, or around advice. For before and around advice a higher precedence means that the corresponding advice code will be run first. For after advice a higher precedence means that the advice code will be run later.
If around advice code does not call tjp->proceed() or trigger() on the action object no advice code with lower precedence will be run. The execution of advice with higher precedence is not affected by around advice with lower precedence.
For example, consider an aspect that defines advice
9
BE is before advice, AF after advice, and AR around advice
in the following order: BE1, AF1, AF2, AR1, BE2, AR2, AF3. As described in section
9.2 the declaration order also defines the precedence: BE1 has the highest and AF3 the lowest. The result is the following advice code execution sequence:
  1. BE1 (highest precedence)
  2. AR1 (the indented advice will only be executed if proceed() is called!)
    1. BE2 (before AR2, buts depends on AR1)
    2. AR2 (the indented code will only be executed if proceed() is called!)
      1. original code under the join point
      2. AF3
  3. AF2 (does not depend on AR1 and AR2, because of higher precedence)
  4. AF1 (run after AF2, because it has a higher precedence)

List of Examples

pointcut expressions, elsewhere
pointcut declaration, elsewhere
pure virtual pointcut declaration, elsewhere
class slice declaration, elsewhere
advice declaration, elsewhere
advice declaration with access to context information, elsewhere
introductions, elsewhere
base class introduction, elsewhere
advice ordering, elsewhere
aspect declaration, elsewhere
abstract aspect, elsewhere
reused abstract aspect, elsewhere
aspect instantiation using aspectof, elsewhere
re-usable trace aspect, elsewhere
static type identification using introductions, elsewhere
extended thread counting, elsewhere
type, scope, and name parts of a function match expression, elsewhere
simple name patterns, elsewhere
operator name patterns, elsewhere
conversion function name patterns, elsewhere
scope patterns, elsewhere
type patterns with the wildcard character, elsewhere
type patterns with const and volatile, elsewhere
type patterns with virtual, elsewhere
type matching, elsewhere
control flow dependant advice activation, elsewhere
matching in scopes, elsewhere
function matching, elsewhere
instance counting
context matching, elsewhere
combining pointcut expressions, elsewhere
attribute declaration, elsewhere
using attributes to annotate program elements, elsewhere
using attributes in pointcut expressions, elsewhere
type usage, elsewhere
static function usage, elsewhere
non-static function usage, elsewhere

Grammar

The AspectC++ syntax is an extension to the C++ syntax. It adds five new keywords to the C++ language: aspect, advice, slice, pointcut, and attribute. Additionally it extends the C++ language by advice and pointcut declarations. In contrast to pointcut declarations, advice declarations may only occur in aspect declarations.
class-key:
 
aspect
declaration:
 
pointcut-declaration
slice-declaration
advice-declaration
attribute-declaration
member-declaration:
 
pointcut-declaration
slice-declaration
advice-declaration
attribute-declaration
pointcut-declaration:
 
pointcut  declaration
pointcut-expression:
 
constant-expression
advice-declaration:
 
advice  pointcut-expression  :  order-declaration
advice  pointcut-expression  :  slice-reference
advice  pointcut-expression  :  declaration
order-declaration:
 
order (  pointcur-expression-seq )
slice-reference:
 
slice :: opt  nested-name-specifier opt unqualified-id ;
slice-declaration:  
slice
declaration
attribute-declaration:
 
attribute  unqualified-id ( ) ;

Match Expression Grammar

Match expression in AspectC++ are used to define a type pattern and an optional object name pattern to select a subset of the known program entities like functions, member variables, or argument/result types. The grammar is very similar to the grammar of C++ declarations. Any rules, which are referenced here but not defined, should be looked up in the ISO C++ standard.
match-expression:
 
match-declaration
match-id:
 
%
nondigit
match-id %
match-id nondigit
match-id digit
match-declaration:
 
match-decl-specifier-seq opt match-declarator
match-decl-specifier-seq:
 
match-decl-specifier-seq opt match-decl-specifier
match-decl-specifier:
 
nested-match-name-specifier opt match-id
cv-qualifier
match-function-specifier
char
wchar_t
bool
short
int
long
signed
unsigned
float
double
void
match-function-specifier:
 
virtual
static
nested-match-name-specifier:
 
match-id :: nested-match-name-specifier opt
... :: nested-match-name-specifier opt
match-declarator:
 
direct-match-declarator
match-ptr-declarator match-declarator
abstract-match-declarator:
 
direct-abstract-match-declarator
match-ptr-declarator abstract-match-declarator
direct-match-declarator:
 
match-declarator-id
direct-match-declarator
( match-parameter-declaration-clause ) cv-qualifier-seq opt
direct-match-declarator
[ match-array-size ]
direct-abstract-match-declarator:
 
direct-abstract-match-declarator
( match-parameter-declaration-clause ) cv-qualifier-seq opt
direct-abstract-match-declarator
[ match-array-size ]
match-array-size:
 
%
decimal-literal
match-ptr-operator:
 
* cv-qualifier-seq opt
&
nested-match-name-specifier * cv-qualifier-seq opt
match-parameter-declaration-clause:
 
...
match-parameter-declaration-list opt
match-parameter-declaration-list
, ...
match-parameter-declaration-list:
 
match-parameter-declaration
match-parameter-declaration-list
, match-parameter-declaration
match-parameter-declaration:
 
matct-decl-specifier-seq match-abstract-declarator opt
match-declarator-id:
 
nested-match-name-specifier opt match-id
nested-match-name-specifier opt match-operator-function-id
nested-match-name-specifier opt match-conversion-function-id
match-operator-function-id:
 
operator %
operator match-operator
match-operator:
one of
new
delete
new[]
delete[]
+
-
*
/
%%
^
&
|
~
!
=
<
>
+=
-=
*=
/=
%%=
^=
&=
|=
<<
>>
>>=
<<=
==
!=
<=
>=
&&
||
++
--
,
.*
->*
->
()
[]
?:
match-conversion-function-id:
 
operator match-conversion-type-id
match-conversion-type-id:
 
match-type-specifier-seq match-conversion-declarator opt
match-conversion-declarator:
 
match-ptr-operator match-conversion-declarator opt

Structure Of The Project Repository

Figure 3 shows the internal structure of the AspectC++ model and the AspectC++ project repository. The distinction between name and code join points and also the inheritance hierarchy is visible.

Project Repository File For Example elsewhere

<?xml version="1.0"?>
<ac-model version="1.2" ids="7">
  <files>
    <TUnit filename="shape.cpp" len="42" time="1442951698" id="0"/>
  </files>
  <root>
    <Namespace name="::">
      <children>
        <Class name="Shape" id="1">
          <children>
            <Function kind="8" cv_qualifiers="0" name="~Shape" builtin="true">
              <children>
                <Destruction/>
              </children>
            </Function>
            <Function kind="7" cv_qualifiers="0" name="Shape" builtin="true">
              <children>
                <Construction/>
              </children>
            </Function>
            <Function kind="7" cv_qualifiers="0" name="Shape" builtin="true">
              <arg_types>
                <Type signature="const Shape &amp;"/>
              </arg_types>
              <children>
                <Construction/>
              </children>
            </Function>
          </children>
          <source>
            <Source kind="1" file="0" line="1" len="1"/>
            <Source kind="2" file="0" line="1" len="1"/>
          </source>
        </Class>
        <Namespace name="Circle">
          <children>
            <Class bases="1" name="S_Circle" id="4">
              <children>
                <Function kind="7" cv_qualifiers="0" name="S_Circle" builtin="true">
                  <children>
                    <Construction/>
                  </children>
                </Function>
                <Function kind="7" cv_qualifiers="0" name="S_Circle" builtin="true">
                  <arg_types>
                    <Type signature="const Circle::S_Circle &amp;"/>
                  </arg_types>
                  <children>
                    <Construction/>
                  </children>
                </Function>
                <Variable kind="3" name="m_radius">
                  <type>
                    <Type signature="int"/>
                  </type>
                  <source>
                    <Source kind="1" file="0" line="8" len="1"/>
                  </source>
                </Variable>
                <Function kind="3" cv_qualifiers="0" name="radius" id="3">
                  <result_type>
                    <Type signature="void"/>
                  </result_type>
                  <arg_types>
                    <Type signature="int"/>
                  </arg_types>
                  <children>
                    <Execution/>
                    <Builtin target="2" lid="0">
                      <source>
                        <Source kind="0" file="0" line="11" len="1"/>
                      </source>
                    </Builtin>
                  </children>
                  <source>
                    <Source kind="1" file="0" line="10" len="3"/>
                  </source>
                </Function>
                <Function kind="8" cv_qualifiers="0" name="~S_Circle">
                  <children>
                    <Destruction/>
                  </children>
                  <source>
                    <Source kind="1" file="0" line="13" len="1"/>
                  </source>
                </Function>
              </children>
              <source>
                <Source kind="1" file="0" line="7" len="8"/>
                <Source kind="2" file="0" line="7" len="1"/>
              </source>
            </Class>
            <Function kind="1" cv_qualifiers="0" name="draw" id="6">
              <result_type>
                <Type signature="void"/>
              </result_type>
              <arg_types>
                <Type signature="int"/>
              </arg_types>
              <children>
                <Execution/>
                <Call target="3" lid="0" target_class="4">
                  <source>
                    <Source kind="0" file="0" line="18" len="1"/>
                  </source>
                </Call>
                <Call target="5" lid="1">
                  <source>
                    <Source kind="0" file="0" line="19" len="1"/>
                  </source>
                </Call>
              </children>
              <source>
                <Source kind="1" file="0" line="16" len="5"/>
              </source>
            </Function>
          </children>
          <source>
            <Source kind="0" file="0" line="4" len="18"/>
          </source>
        </Namespace>
        <Function kind="1" cv_qualifiers="0" name="draw" id="5">
          <result_type>
            <Type signature="void"/>
          </result_type>
          <arg_types>
            <Type signature="Shape &amp;"/>
          </arg_types>
          <children>
            <Execution/>
          </children>
          <source>
            <Source kind="1" file="0" line="2" len="1"/>
          </source>
        </Function>
        <Function kind="1" cv_qualifiers="0" name="operator =" builtin="true" tunits="0" id="2">
          <result_type>
            <Type signature="int &amp;"/>
          </result_type>
          <arg_types>
            <Type signature="int &amp;"/>
            <Type signature="int"/>
          </arg_types>
        </Function>
        <Function kind="1" cv_qualifiers="0" name="main">
          <result_type>
            <Type signature="int"/>
          </result_type>
          <children>
            <Execution/>
            <Call target="6" lid="0">
              <source>
                <Source kind="0" file="0" line="24" len="1"/>
              </source>
            </Call>
          </children>
          <source>
            <Source kind="1" file="0" line="23" len="4"/>
          </source>
        </Function>
      </children>
    </Namespace>
  </root>
</ac-model>