C/C++

Mixin-Based Programming in C++

Source Code Accompanies This Article. Download It Now.

A mixin is a fragment of a class that is intended to be composed with other classes or mixins. Our authors present a solution to the constructor problem in parameterized inheritance-based mixin programming in C++.

A mixin is a fragment of a class in the sense that it is intended to be composed with other classes or mixins. The term "mixin" (or "mixin class") was originally introduced in Flavors (see "Object-Oriented Programming with Flavors," by D.A. Moon, Proceedings of the 1st ACM Conference on Object-Oriented Programming Languages and Applications, 1986), the predecessor of CLOS (Object-Oriented Programming in Common Lisp: A Programmer's Guide to CLOS, by S. Keene, Addison-Wesley, 1989). The difference between a regular, stand-alone class (such as Person) and a mixin is that a mixin models some small functionality slice (for example, printing or displaying) and is not intended for standalone use. Rather, it is supposed to be composed with some other class needing this functionality (Person, for instance). One use of mixins in object-oriented languages involves classes and multiple inheritance. In this model, a mixin is represented as a class, which is then referred to as a "mixin class," and we derive a composed class from a number of mixin classes using multiple inheritance. Another model is to use parameterized inheritance. In this case, we can represent a mixin as a class template derived from its parameter, for example:

template<class Base>
class Printing : public Base
{...}

Indeed, some programmers define mixins as "abstract subclasses"  that is, subclasses without a concrete superclass (see "Mixin-Based Inheritance," G. Bracha and W. Cook, Proceedings of the 8th Conference on Object-Oriented Programming, Systems, Languages, and Applications/European Conference on Object-Oriented Programming, 1990). Mixins based on parameterized inheritance in C++ have been used to implement highly configurable collaborative and layered designs (for instance, "Using Role Components to Implement Collaboration-Based Designs," by M. VanHilst and D. Notkin, Proceedings of the 1996 ACM Conference on Object-Oriented Programming Systems, Languages and Applications, 1996 and "Implementing Layered Designs with Mixin Layers," Y. Smaragdakis and D. Batory, Proceeding of the 12th European Conference on Object-Oriented Programming, 1998).

In this article, we'll present a solution to the constructor problem with parameterized inheritance-based mixin programming in C++. Listing One illustrates this problem, which was also described in "Mixin-Based Programming in C++," by Y. Smaragdakis and D. Batory (Proceedings of the Second International Symposium on Generative and Component-Based Software Engineering, 2000).

In Listing One, the mixin classes PhoneContact and EmailContact can easily be composed with Customer; that is, PhoneContact<Customer> and EmailContact<Customer>. In both cases, the constructor interface of the base class (Customer) is known, and the arguments for initializing the base class are passed to the base constructor in the initializer lists of the derived mixin classes (PhoneContact and EmailContact). Although semantically plausible, compositions including both mixins (PhoneContact<EmailContact<Customer> > or EmailContact<PhoneContact<Customer> >, for instance) do not work. This is due to the fact that the necessary constructors accepting four arguments and calling the appropriate base class constructor with three arguments are missing.

There are several partial solutions to the constructor problem. The worst idea is to restrict or change the order in which mixin classes can be composed. Because the previously described example cannot be fixed using this strategy anyway, we will not further explore it. Here, we will describe four partial solutions that are somewhat better, but still suffer from problems such as incurring unnecessary overhead, leading to clumsy client code and poor scalability when adding new mixin classes. Nevertheless, they provide the basic insights for understanding the more advanced, complete solution, which we will also propose.

Partial Solutions

One aspect of the aforementioned problem is that a mixin class with an appropriate constructor simply does not exist. A straightforward solution  at least at first glance  would be to simply implement an extra mixin class with the needed constructor using multiple inheritance; see Listing Two. Unfortunately, this approach requires major changes to the existing code. First, we have to prepare the inheritance hierarchy for using multiple inheritance. To avoid potential duplication of subobjects of the class Customer in PhoneAndEmailContact, Customer has to be changed into a virtual base of PhoneContact and EmailContact. Additionally, PhoneAndEmailContact  as the most-derived class  must take care of initializing the virtual base class.

An annoying change is that the print() methods of PhoneContact and EmailContact must be split into basicprint() and print(). Otherwise, it would be impossible to produce an appropriate output with PhoneAndEmailContact::print(). (The method splitting technique in the context of virtual base classes is described by Bjarne Stroustrup in The C++ Programming Language, Third Edition, Addison-Wesley, 1997.) Imagine what would happen if we introduced another mixin class  PostalAddress, for example. This would require adding special mixin classes that combine PhoneContact with PostalAddress, EmailContact with PostalAddress, as well as PhoneContact, EmailContact, and PostalAddress. Thus, the number of such combination classes grows exponentially (without considering the composition order).

Providing a Special Argument Class

When you create a special argument class, the basic idea is to provide a standardized interface for the constructors of all mixin classes by introducing a special class that wraps the union of all arguments of all mixin class constructors (see Listing Three). Stroustrup described the technique of bundling arguments in a special argument class in The Design and Evolution of C++ (Addison-Wesley, 1994).

This solution also has several drawbacks. Every time a new mixin class with additional constructor parameters is added, the argument class has to be updated accordingly. Fortunately, the source code of the already defined mixin classes does not break, but only needs to be recompiled. Furthermore, an additional parameter object has to be created when defining the desired object, which is awkward for the client programmer. We should also note that this solution is not very efficient because arguments will be created even if they are not required. By providing default values for optional arguments, we only (partially) hide this fact from the client programmer. This illusion breaks if one of the arguments preceding the last argument is optional. In such a case, the optional argument must be specified, as the declaration of the EmailContact<Customer> object shows.

This solution allows symmetric composition; for instance, PhoneContact<EmailContact<Customer> > and EmailContact<PhoneContact<Customer> >. But it should be recognized that the resulting print() method is composed differently in both cases.

Dr. Dobb's encourages readers to engage in spirited, healthy debate, including taking us to task.
However, Dr. Dobb's moderates all comments posted to our site, and reserves the right to modify or remove any content that it determines to be derogatory, offensive, inflammatory, vulgar, irrelevant/off-topic, racist or obvious marketing or spam. Dr. Dobb's further reserves the right to disable the profile of any commenter participating in said activities.

Video

This month's Dr. Dobb's Journal

This month,
Dr. Dobb's Journal is devoted to mobile programming. We introduce you to Apple's new Swift programming language, discuss the perils of being the third-most-popular mobile platform, revisit SQLite on Android
, and much more!