A future release of Boost.Interfaces will include support for Aspect-Oriented Programming (AOP), as described in a August 2004 Dr. Dobb's Journal article by Christopher Diggins.[1]
Aspect-Oriented Programming with interfaces involves three concepts: advice, pointcuts and crosscutting.
A model of the concept Advice is a class types implementing functionality which is otherwise hard to localize using traditional constructs. For example, such a class might implement thread synchronization, logging or contract checking. A model of Advice has five or six member functions which can be inserted into the body of a given function at well defined point, such as prior to execution of the body, after execution of the body or upon an exception being thrown.
A poincut represents the subset of the member functions of a class to which a model of Advice is to be applied. For our purposes, a pointcut is just an interface. An aspect is a pair consisting of a pointcut and a model of Advice.
Crosscutting is the process of inserting calls to the member functions of models of Advice into the member functions of a given class. It is accomplished using the class template crosscut. An application of crosscutting looks as follows:
typedef crosscut< MyClass, aspect<Pointcut1, Advice1>, aspect<Pointcut2, Advice2>, ... , aspect<PointcutN, AdviceN> > MyCrosscutClass;
The class MyCrosscutClass can be used as a drop-in replacement for MyClass, with added functionality provided by the classes Advice1 through AdviceN.[2]
A future release of Boost.Interfaces will allow an interface's functions to be enumerated and looked up by name at runtime. The precise syntax had not been determined yet, but a possible application might look as follows:
template<typename Interface> void f(Interface& i) { boost::function<void(int)> f; if (f = find_function<void(int)>(i, "foo")) f(3); }
A future release of Boost.Interfaces will allow an interface definition to contain overloaded operators. For example:
// Pseudocode template<typename T> interface IArray { std::size_t size() const; bool empty() const; T& operator[] (std::size_t); const T& operator[] (std::size_t) const; ... };
Once operator overloading is introduced, it may be necessary to replace some of the existing interface operators, such as the safe bool conversion and the assignment operator, with non-member functions.
With the introduction of operator overloading comes the need for interfaces which appear in the parameter types or return types of their own member functions:
// Pseudocode template<typename T> interface IForwardIterator { T& operator*() const; T* operator->() const; IForwardIterator& operator++(); IForwardIterator operator++(int); bool operator==(const IForwardIterator&) const; bool operator!=(const IForwardIterator&) const; };
Interface member functions which involve the interface itself as part of a parameter type can be implemented using an extension of the technique used to implement extract. Interface member functions which involve the interface as part of the return type present some challenging implementation issues; it is likely that they cannot be implemented in full generality.
It is possible to implement a template-based IDL in which use of the preprocessor is confined to defining special class types called Names which represent C++ identifiers. With such an IDL, an interface definition would consist of a sequence of Name delclarations followed by a typedef or class definition constructed from Names, functions types and library templates interface, function and extends. For example, the following definition
BOOST_IDL_NAME(name) BOOST_IDL_NAME(rank) BOOST_IDL_NAME(serial_number) typedef interface< extends<IPerson>, function<name, const char*()> const, function<rank, const char*()> const, function<serial_number, long()> const > IPrisonerOfWar;
would be equivalent to the following pseudocode:
interface IPrisonerOfWar : IPerson { const char* name() const; const char* rank() const; long serial_number() const; };
This IDL has not yet been implemented, so it is not known how well it will work in practice. It might, for example, lead to internal errors on one or more of the currently supported compilers, or increase compile times unacceptably. On the other hand, it might perform better than the current IDL.
Once we have introduced Names, we can use them to generate much more than just interfaces. We might develop an entire library which allows more or less arbitrary classes and functions to be assembled using templates.[3] For example, one might be able to write a scope guard as follows:
BOOST_IDL_NAME(close) void f() { vector<Window> vec = ... ; scope_guard< for_each< _2, _1, call_member<_2, close> > > g(v); ... // All the Windows in vec are closed at end of block }
One of the problems with the delegation facility that was briefly a part of C++ was that the delegate had no way to access the delegating object.[4] This can be addressed by introducing COM-style aggregation, in which the delegate receives, at the beginning of each function argument list, a pointer or reference to the delegating object. An example might look as follows:
// Pseudocode: interface IConnection { void open(); void close(); }; template<typename T> struct Connection { void open(T& t) { /* uses t's user name and password */ } void close(T& t) { /* ... */ } }; struct AggregatingConnection : aggregating<IConnection, AggregatingConnection> { AggregatingConnection() : c(new Connection<AggregatingConnection>) { aggregating<IConnection, AggregatingConnection>::set(c); } std::string user_name() const; std::string password() const; // Inherits implementation of IConnection Connection<AggregatingConnection>* c; };
There is no reason, in principle, that the names and signatures of the member function of an interface and the member function of a class implementing the interface should match. In general, all that is needed is a set of rules indicating which functions of the bound object should be associated with which interface functions, and how the argument list of an interface function should be transformed before it is forwarded. The IDL therefore might be expanded to include declarations such as the following:
// Pseudocode interface IIncrementable { void inc() = operator++(); void advance(int n) = operator+=(n); };
Once we relax the requirement that the names and signatures of the member function of an interface and the member function of a class implementing the interface should match, there is nothing to prevent binding interface member functions to non-member functions involving the bound object:[5]
// Pseudocode interface IPrintable { free void print(std::ostream&, IPrintable); }; void print(std::ostream& out, int i) { out << i; } int main() { int i = 5; IPrintable p = i; p.print(std::cout); // executes std::cout << i }
Several people have requested integration with component architectures such as COM and CORBA. This seems like a reasonable request, but it is not yet clear what type of integration would be useful or what implementation issues might arise.
A more uniform treatment of interface qualifiers such as const and fixed is needed. It must be possible to mix qualifiers freely to produce, for instance, a const fixed view of an interface. In addition, several new qualifiers may be added, such as strict, to force an exact match in function signatures between an interface an the type of the bound object, and checked, to cause an exception to be thrown if a user attempts to invoke a function through an empty interface instance. Finally, a facility analagous to const_cast should be added.
[1]See [Diggins1]. Previous versions of Boost.Interfaces included AOP support. It is not included in this release becuase the author has not had time to update the AOP machinery to conform to the most recent ABI changes.
[2]The Forwarding Problem causes some compliciations. See [Dimov].
[3]There are some serious challenges, such as name hiding and access control, which have not yet been fully explored.
[4]See [Stroustrup], pp 272-73.
[5]This was suggested by Pavel Voženílek, in a private communication, and by Dave Harris. The example is adapted from an example of Harris. See [Harris]
Revised 13 Jan, 2005
© Copyright Jonathan Turkanis, 2005
Distributed under the Boost Software License, Version 1.0. (See accompanying file LICENSE_1_0.txt or copy at http://www.boost.org/LICENSE_1_0.txt)
Sha'arei Tefila, an Orthodox Shul (Synagogue) in Salt Lake City, Utah Chabad Lubavitch of Utah