Idiom: Enhancing Access Control without Dynamic Polymorphism

Introduction

This document presents a technique in C++ to implement multiple interfaces in a single class without using virtual methods, thus reducing the memory footprint and execution time cost that come with the use of interfaces, while preserving the different forms of access control granted by each interface.

In this paper, we will present the most common use scenario for this technique, along with an example to help demonstrate this use. As well, we will discuss the tradeoffs incurred when using this technique.

Use Scenario

During the development process, it is often required that one class must implement multiple interfaces; in other words, a class must be developed that exposes different parts of its behavior to different types of user.

The typical solution for this problem is “dynamic polymorphism”: to create an interface (with its methods declared as virtual) for each user type, and to create a concrete class that inherits from all interfaces and implements their methods. Users of the class then access the base class object via pointers to the corresponding interface.

Within embedded environments, where memory is scarce (which means that it is preferable to avoid having vtables) and there may be strenuous time constraints (which makes indirections undesirable due to their time cost), this is not an optimal solution.

The idiom depicted below is an alternative that has no use of virtual methods, thus saving the memory and processing costs incurred by using such methods.

Example

Let there be two interfaces, Writer and Reader; Writer shows the write and close methods, and Reader shows read and close methods. MyImplementation implements both interfaces.

Using the typical solution (dynamic polymorphism) would yield something similar to this:

struct WriterInterface
{
   virtual void write() = 0;
   virtual void close() = 0;
};

struct ReaderInterface
{
   virtual void read() = 0;
   virtual void close() = 0;
};

class MyImplementation : public WriterInterface, public ReaderInterface
{
private:
   virtual void read()  { /* ... */ }
   virtual void write() { /* ... */ }
   virtual void close() { /* ... */ }
};

void writerUser(WriterInterface* wif);

In the design above, the interfaces are base classes and implementation is on a derived class. Although methods are reachable using dynamic_cast, this access alternative still incurs the memory and processing overhead caused by the usage of a virtual table.

The Idiom

The idiom proposes to reverse the order of inheritance and divide the responsibilities between three kinds of classes.

  1. Base implementation: this is the base class for the entire hierarchy. It contains all the interfaces’ methods implementations. Its constructor is protected, to avoid this class from being instantiated.
  2. Interfaces: there is one of these classes for every type of user. One of the interfaces inherits from base implementation, and then every new interface inherits from the most derived interface thus far. All inheritances are protected, and so are all constructors. Each interface exposes its methods within its public part with using declarations.
  3. Getter implementer: this is the last class in the hierarchy, and it inherits privately from the most  derived interface. It is the only class in the entire hierarchy that can be instantiated, and it provides a getter method for each one of the interfaces in the hierarchy.

The previous example, adapted to the idiom, is shown below; two C++11 features (=default for constructors [Crowl07] and final on the most derived class [ISO/IEC 14882-2011]) are used, but they both have equivalent expressions in C++03.

class MyImplementationBase
{
protected:
   void read()  { /* ... */ }
   void write() { /* ... */ }
   void close() { /* ... */ }
   MyImplementationBase() = default;
};

class WriterInterface : protected MyImplementationBase
{
public:
   using MyImplementationBase::write;
   using MyImplementationBase::close;
protected:
   WriterInterface() = default;
};

class ReaderInterface : protected WriterInterface
{
public:
   using MyImplementationBase::read;
   using MyImplementationBase::close;
protected:
   ReaderInterface() = default;
};

class MyImplementation final : private ReaderInterface
{
public:
   ReaderInterface& getReader() { return *this; }
   WriterInterface& getWriter() { return *this; }
};

void writerUser(WriterInterface* wif);

 

Conclusion

In conclusion, it becomes apparent that using the above idiom avoids the memory and processing overheads required by virtual methods’ usage while still ensuring adequate access control by users of each interface.

The impact of the idiom is that there is no automatic upcast from implementation to interfaces, thus forcing the use of getters for the various interfaces.

References

[Crowl07] Crowl, Lawrence. Defaulted and Deleted Functions. [N2346= 07-0206]. Programming Language C++. Evolution Working Group.

[ISO/IEC 14882-2011] ISO/IEC 14882-2011. Programming Languages — C++, Third Edition, 2011.


Daniel Gutson - Chief Engineering Officer & Director of Special Projects Division, Taller Technologies Argentina
Daniel Gutson is the Chief Engineering Officer and Director of the Special Projects Division at Taller Technologies Argentina. Daniel is also the Informatics Director at the non-profit FuDePAN (Foundation for the Development of Programming on Nucleic Acids) where he’s currently developing computer programs that seek to personalize treatments for patients with AIDS.

With 20 years' experience in the software industry, Daniel has a range of development experience, from constructing and developing the topology engine of a GIS (Geographic Information System), to designing and implementing the DSP engine for a DSL soft modem. He has contributed significantly to several free software projects, including the GNU toolchain: gcc, gas, ls, gdb, and porting the gdbserver to the SPARC architecture, as well as qemu. Daniel’s career includes over 7 years working at Motorola where he served as a Senior Staff Engineer. As well, Daniel has participated in the C++ (WG21) Committee where he is credited with proposing extensions which can now be found in the C++11 standard.

Pablo Oliva - Software Engineer of Special Projects Division, Taller Technologies Argentina.
Pablo Oliva is a Software Engineer and a member of the Special Projects Division at Taller Technologies Argentina. Along with Daniel, Pablo is a collaborator at FuDePAN and contributes his professional skills to develop computer programs that help enhance the lives of people living with AIDS.