$interface

The conventional approach to dynamic polymorphism in C++ is to use abstract base classes.

The Xc++ language provides an alternative way to achieve dynamic polymorphism.

An interface is analogous to an abstract base class with pure virtual functions. However it is more flexible, less type intrusive and typically provides better performance. There are numerous advantages of interfaces over abstract base classes

Defining an interface

The following defines an interface named IShape:


$interface IShape
{
    float64 GetArea() const;
    float64 GetPerim() const;
};

Note the clean syntax given that all methods are implicitly public pure virtual functions. These particular methods are declared const because they shouldn't mutate the object implementing the interface.

Interface pointers

For any interface T a ptr<T> is a simple, lightweight data type that supports inexpensive comparisons, copy and assignment.

A ptr<IShape> represents a pointer to an object that implements the IShape interface.

A ptr<const IShape> only allows the const methods on the interface to be called. Note that ptr<const IShape> associates constness with the object rather than the pointer to the object.

Calling methods on an interface

Given only the above definition of interface IShape it is possible to write code that calls the IShape methods such as GetArea on a ptr<IShape>, regardless of what kind of object implements the interface, i.e. with dynamic polymorphismn. For example:


void WriteArea(ptr<const IShape> s)
{
    std::cout << "Area = " << s->GetArea() << std::endl;
}

Defining objects that implement an interface

Consider the following class written in straight C++:


class Circle
{
public:
    Circle() : r(3.0) {}
    float64 GetArea() const {return 3.14*r*r;}
    float64 GetPerim() const {return 2*3.14*r;}
private:
    float64 r;
};

Notice that Circle doesn’t inherit from any base classes, it is written without any dependency on interface IShape.

An instance of a Circle class is probably only 8 bytes (the size of its float64 member r). There are no virtual methods so there is no vtable pointer.

Nevertheless we consider Circle to have implemented the IShape interface, because it has implemented all the IShape methods with the required signature and semantics.

The remarkable idea is that a dynamic polymorphic capability can be retrospectively applied to a class.

Getting an interface pointer on an object

The coercion into an interface pointer is very simple:


Circle c;
ptr<IShape> s = &c; // Coercion to IShape
WriteArea(s);

or just


Circle c;
WriteArea(&c);

If Circle hadn’t implemented the methods in the IShape interface then the attempted coercion would have resulted in a compile time error.

Under the hood: how it is implemented

Interface pointers are implemented in terms of a pair of pointers.

CD Player example

Xcpp interfaces provide the means to "wire up" software components more efficiently than with conventional C++ or with languages like C# and Java.

The CD player example shows how the play and stop buttons are "wired up" to call the Play() and Stop() methods without heap allocations.

Interface inheritance

Multiple inheritance of interfaces is supported. This allows one to speak of the subinterfaces and superinterfaces of a given interface.

The IObject interface

There is a special interface called IObject which supports

When a $interface is reflected an instance of a ReflectedInterface is registered in the ReflectedInterface registry.

A unique_interface_ptr is a smart pointer which represents exclusive ownership of an object that implements IObject and will call the Destroy method when it destructs.