Channels ▼
RSS

C++ with Interfaces


September, 2004: C++ with Interfaces

Christopher Diggins is a freelance computer programmer and developer of Heron, a modern general-purpose open-source language inspired by C++, Pascal, and Java. He can be contacted at http://www.heron-language.com/.


While interface constructs are a commonplace feature in many newer languages (Java, Delphi, C#, Heron, and D, for instance), they're not in C++. The most well-known and advocated approach to implement interface-like functionality in C++ is through the use of abstract base classes (ABCs). In this article, I examine how interfaces differ from ABCs, and how you can implement efficient interfaces in C++ without virtual functions. In the process, I present HeronFront, a code generator for C++ that generates interface-style types from a file containing interface declarations.

What Are Interfaces?

Interfaces are noninstantiable types that contain only function declarations. A variable with an interface type acts as a reference to any object that implements that particular interface. To implement an interface, an object must supply definitions for all of the functions that make up the interface. In many languages, a class declares its intent to implement an interface explicitly, but this is not necessarily the case.

Interfaces resemble ABCs in many ways and as a result are commonly confused. There is, however, one significant difference—a type that implements an interface is not necessarily an implicit statement of intention for the interface functions to be virtual. That is a side effect of using ABCs to emulate interfaces and is surprising from a straightforward interpretation of what an interface is. Therefore, using an ABC to emulate an interface is incorrect, at least from a theoretical standpoint. But does this make a difference from a practical standpoint?

Comparing ABCs to Interfaces

When using ABCs to emulate interfaces, we unintentionally make functions virtual, which leads to an unintentional design change and to unnecessary performance penalties. The design change occurs when you consider code such as this:

interface IFuBar {
  contract:
    void FuBar();
};

struct BaseFuBar {
  void FuBar() { std::cout << 
    "BaseFuBar"; };
};

struct DerivedFuBar : public BaseFuBar {
  void FuBar() { std::cout << 
    "DerivedFuBar"; };
};

main() {
  DerivedFuBar d;
  BaseFuBar& b = d;
  IFuBar i = b;
  i.FuBar(); // outputs BaseFuBar
};

The common way to approximate this design in standard C++ is:

struct AbcFuBar {
  virtual void FuBar() = 0;
};

struct BaseFuBar : public AbcFuBar {
  void FuBar() { std::cout << 
    "BaseFuBar"; };
};

struct DerivedFuBar : public BaseFuBar {
  void FuBar() { std::cout << 
    "DerivedFuBar"; };
};

main() {
  DerivedFuBar d;
  BaseFuBar& b = d;
  AbcFuBar& a = b;
  a.FuBar(); // output DerivedFuBar
};

This is not by any means a bad or incorrect design, but it is not what you would expect from an interface. However, it is the only way that you can easily have runtime polymorphic variables in C++. Notice that I say "easily," since there exist alternatives with excellent performance, but are rather verbose.

Some programmers view the difference as somewhat pedagogical, but there are potentially significant performance penalties in terms of execution speed and memory usage for having superfluous virtual functions in a class.

Emulating Interfaces: Disadvantages

One of the major problems of using multiple inheritance of ABCs is vtable pointer bloat, which occurs in most implementations of C++. The problem is this: Each time a class has in its inheritance hierarchy a different branch with a virtual function, another vtable pointer is added to the class. Typically, this is not viewed as a debilitating problem because inheritance is meant to model is-a type object relationships, for which single inheritance is often appropriate. Interfaces, though, are often intended to model behaves-like or looks-like relationships, which can lead to much broader inheritance trees (assuming you use ABCs), which makes the problem much more significant.

Another disadvantage of virtual functions is that they execute slower. This stems mostly from the inability of compilers to inline virtual functions in many situations, which is one of the most common and effective forms of optimizations a compiler can perform.

Hand-Coding Interface Types

Interfaces can still be implemented in C++ without virtual functions through an interface class that contains a pointer to a function pointer table and an object.

Listing 1 was posted on comp.std.c++ by David Abrahams of Boost Consulting (http://www.boost-consulting.com/) and is an excellent example of how to code an interface type. That code is presented here with only a small change to the visibility specifiers in order to make it compatible with my Borland compiler. This code is very efficient, but as you can see, it is quite verbose and perhaps a bit tricky to write. The technique, though, is not as complicated as it might seem at first. The baz class contains two member variables—_m_a, which is a pointer to an object and _m_t, which is a pointer to the function dispatch table. The function dispatch table is constructed using a parameterized constructor. The interface then routes any call to a member function through the static function table, which in turn routes the call to the appropriate object in a type-safe manner.

One limitation of this technique is that it doesn't work well as a parameterized type, but this can be easily overcome. Another method of implementing interfaces was also proposed on comp.std.c++ by Dave Harris, which managed to isolate some of the complexity into an interface class.

HeronFront: A Code Generator for Interfaces

For some people (myself included), writing interface-style classes is too verbose and error prone. This leads naturally to adding an extra code-generation step to the compilation process. Consequently, I wrote HeronFront (http://www.heron-language.com/heronfront.html and http://www.cuj.com/code/) as a free, open-source program to do just that. HeronFront simply generates reference classes from interface declarations.

HeronFront interface declarations take this form:

[template <template_parameter_list>]
interface interface_name [: interface_inheritance_list]
{
  function_declaration_list
};

Interface classes generated by HeronFront support this functionality:

template<typename T> interface_name& operator=(T& x);
template<> interface_name& operator=(IFuBar& x);
template<typename T> interface_name(const T& x);
template<> interface_name(const interface_name& x);

These interface classes behave much like references to the object they are constructed with, but are reassignable.

Interfaces Versus ABCs: An Example

To demonstrate the memory-bloat problem and performance penalties of using ABCs to emulate interfaces, I present an example that compares using HeronFront interfaces to a design written using ABCs. Consider the example of a circle class from an imaginary drawing program. Listing 2 is the file shapes.hfront, which translates with HeronFront to shapes.hpp in a prebuild step in the makefile. Listing 3, on the other hand, is the file heron-test.cpp, which compares the interface method with the abc method. The program outputs Example 1 on my machine (an Intel Celeron 1.8-GHz with 256-MB RAM running Windows XP) when compiled with Borland C++ 5.5.1 Compiler for Windows and the -O2 (speed optimization) command-line switch is activated.

From this output, you can see that the Circle type is less than half the size of the NaiveCircle type and it is also able to execute MoveBy calls in half the time. Timing results obviously vary from compiler to compiler, and conditions where ABCs perform more quickly than interfaces still exist. In general and for most compilers, however, the nonvirtual versions of the functions out-perform the virtual versions.

Conclusion

Interface types can provide an alternative method of nonintrusive polymorphism, which, for certain scenarios, are more efficient and appropriate than traditional abstract base-class inheritance design patterns. Interfaces are a powerful concept that have applicability in a wide range of problem domains and exploration is being done as to their usefulness in domains, ranging from test-driven design and extreme programming to aspect-oriented programming and design by contract, among others.

Acknowledgments

Thanks to David Abrahams of Boost Consulting for his excellent interface code.


Related Reading


More Insights






Currently we allow the following HTML tags in comments:

Single tags

These tags can be used alone and don't need an ending tag.

<br> Defines a single line break

<hr> Defines a horizontal line

Matching tags

These require an ending tag - e.g. <i>italic text</i>

<a> Defines an anchor

<b> Defines bold text

<big> Defines big text

<blockquote> Defines a long quotation

<caption> Defines a table caption

<cite> Defines a citation

<code> Defines computer code text

<em> Defines emphasized text

<fieldset> Defines a border around elements in a form

<h1> This is heading 1

<h2> This is heading 2

<h3> This is heading 3

<h4> This is heading 4

<h5> This is heading 5

<h6> This is heading 6

<i> Defines italic text

<p> Defines a paragraph

<pre> Defines preformatted text

<q> Defines a short quotation

<samp> Defines sample computer code text

<small> Defines small text

<span> Defines a section in a document

<s> Defines strikethrough text

<strike> Defines strikethrough text

<strong> Defines strong text

<sub> Defines subscripted text

<sup> Defines superscripted text

<u> Defines underlined text

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.

 
Disqus Tips To upload an avatar photo, first complete your Disqus profile. | View the list of supported HTML tags you can use to style comments. | Please read our commenting policy.
 

Video