Using Interfaces in C++

Abstract

The distinction between classes and interfaces is a powerful language feature present both in Java and C# (along with many other languages) but not in C++. For years, our team has been using a "methodological" implementation of the interface concept for C++, which I present here. Starting in VS7, there's also an MS extension pointing the same way, allowing the compiler to enforce most of the defining characteristics of an interface, and of course, managed extensions for C++ supports the definition and implementation of .NET interfaces too. However, there are some subtle and not so subtle differences between each of these mechanisms that you should take into account.

Background

An interface describes the behavior or capabilities of a class without committing to a particular implementation. It represents a contract between a provider and its users, defining what's required from each implementer, but only in terms of the services they must provide, no matter how they manage to do it.

In case you are unfamiliar with the concept of interfaces, and when and how to use it to improve your design, the following MSDN article might be of great help. It's addressed to Visual Basic users switching to VB.NET, but it does a very good job of explaining the basic ideas that apply to any object-oriented language anyway.

Introduction

Many years ago, around November 1999, I defined a way to declare interfaces for C++, and to make classes implement them, just by using a few macros and making sure some basic rules were followed. I should make it clear that I don't claim to be the "inventor" of this technique or anything like that. Although it was independently developed, mostly with the invaluable advice of Roberto Lublinerman, I later found many articles on the Internet that described more or less the same ideas, and at least some of them were dated way before we had even come to think about it.

These days, the solution can be improved by making use of Microsoft extensions that were introduced in later versions of the C++ compiler, but let me go one step at a time.

The First Version

First, some macros are defined in a header file that you'll probably want to include in your precompiled headers:

//
// CppInterfaces.h
//

#define Interface class

#define DeclareInterface(name) Interface name { \
          public: \
          virtual ~name() {}

#define DeclareBasedInterface(name, base) class name :
        public base { \
           public: \
           virtual ~name() {}

#define EndInterface };

#define implements public

By using these macros, you can declare an interface in the following way:

//
// IBar.h
//

DeclareInterface(IBar)
   virtual int GetBarData() const = 0;
   virtual void SetBarData(int nData) = 0;
EndInterface

Then, you can declare a class that implements this interface with something like the following:

//
// Foo.h
//

#include "BasicFoo.h"
#include "IBar.h"

class Foo : public BasicFoo, implements IBar
{
// Construction & Destruction
public:
   Foo(int x) : BasicFoo(x)
   {
   }

   ~Foo();

// IBar implementation
public:
   virtual int GetBarData() const
   {
      // stuff...
   }

   virtual void SetBarData(int nData)
   {
      // stuff...
   }
};

Easy, isn't it? Without much effort, you now are able to use interfaces in C++. However, because they aren't directly supported in the language, you are supposed to follow some rules that can't be automatically enforced at compilation time. After all, all that the compiler can see is the use of plain old multiple inheritance and abstract base classes. Here are the rules you need to follow, along with some recommendations:

  • When declaring a class, use the first base class for "structural" inheritance (the "is a" relationship) if there is one, as you normally do. (For example: CFrameWnd derives from CWnd, CBitmapButton derives from CButton, YourDialog derives from CDialog, and so on.) This is especially important if you are deriving from MFC classes; declaring them as the first base class avoids breaking MFC's RuntimeClass mechanism.
  • Use additional base classes for interface implementation, as many as you want or need. (For example: class Foo : public BasicFoo, implements IBar, implements IOther, implements IWhatever, ...)
  • Do not declare any member variables inside the interfaces. Interfaces are intended to express behavior, not data. Besides, this helps to avoid some problems if you were to use multiple "structural" inheritance and happened to be deriving more than once from the same interface.
  • Declare all the member functions in the interfaces as virtual pure (in other words, with "= 0"). This ensures every instantiable class that declares to implement an interface does it for all its functions. It's okay to partially implement an interface in an abstract class (in fact, it will be abstract anyway if you do so) as long as you implement the remaining functions in the derived classes you actually intend to instantiate. Because the interfaces offer no "basic" implementation, you need to be sure everyone that receives a pointer to some interface will be able to call any of its members; declaring all your interface members as virtual pure will enforce this at compile time.
  • Do not derive your interfaces from anything but other interfaces. You can use the DeclareBasedInterface() macro for that. Normal classes can choose to implement the basic interface or the derived (extended) one; the latter of course means implementing both.
  • Assigning a pointer to a class that implements some interface to a pointer to that interface requires no casting, because you will be actually casting to a base class. Doing it the other way though (from an interface pointer to a pointer to the class implementing it), will require an explicit cast, as you will be casting to a derived class. Because you will in fact be using multiple inheritance (even if for virtually every other practical need, you can think of it as single inheritance plus interface implementations), these casts can't be done the "old" way because they may need different actual memory values. However, enabling Run-Time Type Information (/GR compiler option) and using dynamic casts works fine and it's of course safer anyway.
  • Further, the use of dynamic_cast will give you a way to ask any object or interface whether it implements a given interface or not.
  • You need to be careful to avoid name conflicts between functions in different interfaces because it isn't easy to detect and resolve those conflicts should you have a class implementing both.

Evaluation

When I posted about the above technique in my recently created blog, one of the readers expressed some concerns in the following terms:

  • Why bother with the macros? They don't enforce anything, and they don't improve readability any more than these old standbys:
  • #define begin {
    #define end }
    
  • Maybe that's useful as a crutch for Java/C# developers, but if a developer needs a crutch like this, they have other problems.

If you look closely at the DeclareInterface and DeclareBasedInterface macros, you'll note that there is at least something that is being enforced there: Every class implementing an interface will have a virtual destructor. You may or may not think this is important, but there are cases when the lack of a virtual destructor will cause problems. Consider the following code, for instance:

DeclareInterface(IBar)
   virtual LPCTSTR GetName() const = 0;
   virtual void SetName(LPCTSTR name) = 0;
EndInterface

class Foo : implements IBar
{
// Internal data
private:
   char* m_pName;

// Construction & Destruction
public:
   Foo()
   {
      m_pName = NULL;
   }

   ~Foo()
   {
      ReleaseName();
   }

// Helpers
protected:
   void ReleaseName()
   {

      if (m_pName != NULL)
         free(m_pName);
   }

// IBar implementation
public:
   virtual const char* GetName() const
   {
      return m_pName
   }

   virtual void SetName(const char* name)
   {
      ReleaseName();
      m_pName = _strdup(name);
   }
};

class BarFactory
{
public:
   enum BarType {Faa, Fee, Fii, Foo, Fuu};

   static IBar CreateNewBar(BarType barType)
   {
      switch (barType)
      {
         default:
         case Faa:
            return new Faa;
         case Fee:
            return new Fee;
         case Fii:
            return new Fii;
         case Foo:
            return new Foo;
         case Fuu:
            return new Fuu;
      }
   }
};

As you can see, there's a factory to which you can ask to create an IBar implementation, depending on a BarType parameter. After using it, you are expected to delete the object; so far, so good. Now, consider how this is used in the main function of some application:

int main()
{
   IBar* pBar = BarFactory::CreateBar(Foo);

   pBar->SetName("MyFooBar");
   // Use pBar as much as you want,
   // ...

   // and then just delete it when it's no longer needed
   delete pBar;    // Oops!
}

What happens at the delete pBar line depends on whether the actual class of that object has a virtual destructor or not. If Foo doesn't have a virtual destructor, the compiler will only generate a call to IBar's implicit empty destructor; the destructor for Foo won't be called, and thus you'll have a memory leak. The virtual destructors in the interface declaration macros are there to avoid this situation; they ensure every class implementing an interface will also have a virtual destructor.

Now, if you are going to use DeclareInterface, it kind of makes sense to also use EndInterface to match with it, rather than a closing brace with no apparent opening one to match. The need for the Interface and implements macros, which resolve to class and public, respectively, could be objected as superfluous, but I find them to better express the actual intent of the code. If I write Foo : public IBar, you can only read some inheritance there and nothing else. If on the other hand, I write Foo implements IBar, you can see it for its actual value and intent: the implementation of the interface concept, and not just any kind of class inheritance. I do find value in that.

To be fair, I'm sure that the reader was actually concerned with the other things these macros do not and cannot enforce: interfaces are expected to have only pure virtual functions and no instance data. But, at least, if you write your interfaces using DeclareInterface and EndInterface, the inclusion of any instance data or non virtual pure function should be easy to spot. In Joel Spolsky's words, these macros also help by "making wrong code look wrong".

C++ Interfaces Support in VS7

The folks at Microsoft must have felt the same need to enforce those restrictions on classes used as interfaces, judging from the introduction in VS7 of the __interface keyword as a new Microsoft extension to the C++ compiler. In the documentation, they define a Visual C++ interface as follows:

  • Can inherit from zero or more base interfaces.
  • Cannot inherit from a base class.
  • Can only contain public and pure virtual methods.
  • Cannot contain constructors, destructors, or operators.
  • Cannot contain static methods.
  • Cannot contain data members; properties are allowed.

And they note: "A C++ class or struct could be implemented with these rules, but __interface enforces them." So, if you are not worried by portability, you could use this extension and have the compiler enforce what needs to be enforced, right? Wrong.

Remember that bit about the need for virtual destructors? __interface does not add a virtual destructor for the implementing class, and I can understand why MS didn't see the need for virtual destructors in interfaces. If you look at the samples in the documentation, it's pretty clear from where they are COMing. They obviously created this extension with COM interfaces in mind and you never use delete with them because they are count-referenced objects that call delete on themselves when the count reaches zero.

Couldn't you just use __interface in the DeclareInterface macro definition, to have the best of both worlds? Hmm..., read the definition again: "...cannot contain constructors, destructors, or operators." This led me to initially think it wasn't possible, and even to say that while __interface might be useful for COM interfaces, it was neither intended nor suitable for the general purpose interfaces you are learning about here. Fortunately, that's not the end of the story.

The Best of Both Worlds

A while later, I found a rather simple solution: the name for the desired interface is actually used to declare a class that contains a virtual destructor and inherits from an __interface that contains the needed methods. The macros are now defined as follows:

//
// CppInterfaces2.h
//

#define Interface class

#define implements public

#define DeclareInterface(name) __interface actual_##name {

#define DeclareBasedInterface(name, base)
        __interface actual_##name \
     : public actual_##base {

#define EndInterface(name) };                \
     Interface name : public actual_##name { \
     public:                                 \
        virtual ~name() {}                   \
     };

Here is how you declare an interface using the above-defined macros:

//
// IBar2.h
//

DeclareInterface(IBar)
   int GetBarData() const;
   void SetBarData(int nData);
EndInterface(IBar)

As you may have noticed, these new macro definitions require using the name of the wanted interface (in other words, IBar) twice, first for DeclareInterface(), and then for EndInterface(). This introduces an always undesired redundancy, which made me struggle for a while trying to eliminate, but failed. If anyone finds a way to define the macros to avoid requiring the same name twice, please let me know.

On the other hand, provided you don't mind sacrificing portability to anything but MS compilers starting from VS7, the new macros have many advantages over their predecessors, simply because every requirement for an interface (only pure virtual methods, no data members, and virtual destructor for implementing classes) is now automatically enforced. You don't even need to explicitly declare interface methods as virtual or pure virtual ("= 0"), although the compiler won't complain if you do so.

Additional Reading

Before closing this article, I'd like to include some links to related resources that I found while doing research on this topic.

The techniques described here allow defining and implementing interfaces in C++ through the use of abstract base classes. Some people don't like this as a solution because it forces every implementing class to have (and use) a virtual table and they find this unacceptable due to the space (the virtual table for each class) and performance (the indirection in each method call) penalties it imposes. As an alternative, Brian McNamara and Yannis Smaragdakis from the Georgia Institute of Technology wrote a paper Static interfaces in C++, which was published in the First Workshop on C++ Template Programming, Erfurt, Germany, October 10 2000.

Christopher Diggins wrote a proposal for a modification of the C++ language, to make it support interfaces without virtual functions. I have no idea whether this proposal was actually presented or considered by the standards committee.

Dr. Dobb's Journal, August 1998, includes an article by Fred Wild, "Keeping interfaces and implementations separate," which discusses some ways to do so in C++ code.

Nemanja Trifunovic pointed me to another alternative for static interfaces, the Boost Interface Library.

There's a very interesting interview with Scott Meyers, by Bill Venners on December 2002, in which they discuss the topic of interfaces in C++.

Last, but not least, the father of C++, Bjarne Stroustrup himself, made interesting comments about interfaces and C++ in an interview by Bill Venners on November 2003.



About the Author

Jose Lamas Rios

Jose Lamas Rios is a member of Genexus' R&D team since 1993. He publishes a blog at http://jlamas.blogspot.com/ (English) and http://blogjlr.blogspot.com/ (Spanish)

Downloads

Comments

  • Short posting unwraps the unignorable info about chanel and the way that it can have an effect everyone.

    Posted by emeseesip on 05/06/2013 08:36pm

    Almost Certainly The Most Thorough nike Instructions You Ever Seen Otherwise Your Money Back [url=http://www.guccija.biz/]グッチ バッグ[/url] I can't believe this, incredible solution. Your business have to have a look at adidas right now while it's still in store ! ! [url=http://www.guccija.biz/]グッチ 長財布[/url] adidas will help all of us by simply including a handful of exclusive capabilities and capabilities. It's a unvaluable item for all fan of nike. [url=http://www.guccija.biz/]グッチ 財布 新作[/url] Independent site divulges Couple of fresh, new things regarding nike that no-one is talking about. [url=http://www.chanelja.biz/]シャネル バッグ[/url] Howcome no one is chatting about nike and as a consequence what one ought to take care of right away. [url=http://www.chanelja.biz/]シャネル チェーンウォレット[/url] Different queries about gucci answered and in addition the reasons you really should look at every single phrase of this specific story. [url=http://www.chanelja.biz/]chanel 財布[/url] The essential principles behind nike it is possible to benefit of starting off today.[url=http://www.nikeja.biz/]ナイキスニーカー[/url] The best way to understand all the stuff there is to understand concerning adidas in Few easy ways.

    Reply
  • http://www.nikeairmaxwr.com/ xnkygv

    Posted by http://www.nikeairmaxwr.com/ Suttonqgb on 03/29/2013 05:18pm

    com) begged them not only going to help, the only requirement is not to drag their hind legs, it all will depend on the mastermind exact task to decide. Hi ...... life really is full of frustration, ah! These equipment are prepared ray ban wayfarer sunglasses, the appropriate choose their own. These agents these days with the principle of direct tougher stealing monster energy ray ban caravan upgrade and should make oakley sunglasses outlet level up. Time is limited, so I can only make so much, oakley sunglasses cheap per person per bottle If you believe me, then drink it. Well, sweet little bitter, so do not have to worry about the taste of ray ban sunglasses However, I want to explain in advance, will be a little side-effects after drinking. Well, that is, the upgrade process will be a bit of a pain. Xiao Feng readily lost a lot of the equipment out of thin air in front of all the new eyes light up, making new people feel good captain.He new when they are not treated like it! Breeding in his heart, so he turned out that the smiling, round face rare hard look. And then Xiao Feng took out bottles of reddish Pharmacy out after the fat man's eyes followed redden larger, in stark contrast to the look of joy and those newcomers. 1,2,3,4 ...... 15 a bottle, the number of new number, it is Xiao Feng did not prepare his share Limei, strength and Xiao Feng out obviously become strong, so the fat can not jealous of what ? Fat, this is for you. Ah, your situation and ray ban sunglasses sale is not the same kind of pharmacy you and not have any effect, so when ray ban sunglasses sale you special with. Fat have an idea in mind Xiao Feng course a clear picture, so to see his eyes red like love bull after leisurely took out a bottle of purple Pharmacy shook in front of him, strong power fluctuations from that came in a small bottle, also Akira took the eye of the fat.

    Reply
  • http://www.oakleysunglassesoutc.com/ hsvnzd

    Posted by http://www.oakleysunglassesoutc.com/ Mandydbw on 03/28/2013 09:02am

    The great generals of the more than thirty years in charge of the German General Staff bid farewell to two of the German emperor, still controls the general operation of the direction of the world-famous German General Staff in the final stages of his life. Next to the bronze statue of Moltke spoke some anecdotes about Moltke legitimate Tan Yan and Prince Henry, an officer wearing a navy clothing hurriedly approached him and whispered in his ear, said a few words, Tan urgent need to delay hear eyebrows a challenge said: His Royal Highness Prince Henry of Wales is very sorry ghd straightener in such an important day should not have to deal with something else, but the sea suddenly gave birth to an outrageous event ghd sale to make a decision, and HRH Prince can understand ... Prince Henry felt a little strange, but he knew the severity of. He magnanimously said Tan Yan, do not apologize.Led by the Northern battleship, Jinan Fengtian No. three armored cruiser out from Port Arthur. Meet in the morning in the Sea of ​​Japan with two Japanese armored cruiser. The Beiyang battleship led ground fleet to Japan in accordance with international practice airing of the 15th-gun salute. Japan has responded to thirteen ring - this incident is a Chinese intelligence organization interfering. The release of the Chinese navy the Salute to operate step by ghd hair straightener After a careful study of single-handedly formulated ground.cheap ghd, Regardless to the number or release from the voice sounded salute Jumping to smoke are thirteen rang.ghd australia, And the fact 15 ring. Met to release Salute expressed respect for etiquette Navy ships at sea between the two countries. The general is a number other places. And to judge each other ceremony cannons number is the most reliable to release Salute Jumping to smoke. The Tan Yan proposed to manufacture Japanese Navy friction.

    Reply
  • cheap ugg boots zNkt yBoq

    Posted by Mandyoor on 03/08/2013 11:25pm

    longchamp bags onkxpgdq longchamp outlet ddvhsjre longchamp sale brrsuzwu longchamp tote wecheryb longchamp bzajnbmr

    Reply
  • ghd australia bzknci

    Posted by Suttonyqe on 03/08/2013 04:37pm

    cheap ugg boots sale wmrfmpip cheap ugg boots uk hqbadgos cheap ugg boots rtsuqudi cheap uggs sibbvesn ugg boots sale uk mvsmjuek ugg boots sale junkresd ugg boots uk osbqfgmp ugg boots qpdcdmgq ugg sale pqynzcua

    Reply
  • ugg boots gqcbgj

    Posted by Suttonnda on 02/20/2013 07:15am

    cheap ugg boots sale woxibauo cheap ugg boots uk mocipium cheap ugg boots rsznepyq cheap uggs hhyekpcy ugg boots sale uk isqoxgml ugg boots sale ivkkcaub ugg boots uk jtpamvhb ugg boots wirgamei ugg sale eadoapso

    Reply
  • ghd australia swjxnx

    Posted by Mandydjo on 02/12/2013 04:15am

    3dKkd christian louboutin pJmy longchamp outlet pVnw michael kors outlet 9oKci 2qAjd chi 0bFlw michael kors outlet 3vEgh cheap nfl jerseys 3eRvr nike uk 8pTfj ghd 2gXqo ugg 4cSmf toms outlet 0lTyk New Tory Burch Flip Flops Miller Yellow Cheap 9qNrk hollister paris 8tRnc ghd 3mVjx ugg boots

    Reply
  • ghd australia thdfbg

    Posted by Mandyazx on 02/09/2013 06:47am

    2cSrw ugg bGka ¥È¥ê©`¥Ð©`¥ÁÐÂ×÷ lLfy nike sko norge 0yFyl cheap toms 9tRcp hollister 0zHam 8hCrv sacoche longchamp 9kFgl louis vuitton shoes 3uJjf michael kors handbags 6sDvr christian louboutin 4cIgv colin kaepernick jerseys 8qMhh 3xZck cheap ghd 1xXmc ghd 8kSsa ugg boots

    Reply
  • ghd australia oszscr

    Posted by Suttonwop on 02/07/2013 03:02am

    0zEfv ugg kRgw dVdu nike 6jPhf toms outlet 2hOht hollister outlet uk 5pBxw ugg 6xIex longchamps 5gFok louis vuitton outlet 1hGec michael kors outlet 7gWmw christian louboutin 8iKtk 49ers T-Shirts 6tGpe 8tRjd 1yYtl ghd 5fHzw ugg sale

    Reply
  • ugg boots ycgejd http://www.cheapfashionshoesas.com/

    Posted by Mandylaq on 01/27/2013 02:22am

    6nCap zDev Michael Kors outlet zTwm ugg boots 6dQzz 8oGye Cheap nfl jerseys 0qAud coach,coach outlet,coach usa,coach factory outlet,coach factory 5eNah burberry sale 9wHak casque docteur dre 7aIgr monster beats 8jKlo 4dOrm 9bDxu 0bTre 9dVxw 3xOfs

    Reply
  • Loading, Please Wait ...

Leave a Comment
  • Your email address will not be published. All fields are required.

Top White Papers and Webcasts

  • Live Event Date: October 29, 2014 @ 11:00 a.m. ET / 8:00 a.m. PT Are you interested in building a cognitive application using the power of IBM Watson? Need a platform that provides speed and ease for rapidly deploying this application? Join Chris Madison, Watson Solution Architect, as he walks through the process of building a Watson powered application on IBM Bluemix. Chris will talk about the new Watson Services just released on IBM bluemix, but more importantly he will do a step by step cognitive …

  • Live Event Date: October 29, 2014 @ 1:00 p.m. ET / 10:00 a.m. PT It's well understood how critical version control is for code. However, its importance to DevOps isn't always recognized. The 2014 DevOps Survey of Practice shows that one of the key predictors of DevOps success is putting all production environment artifacts into version control. In this eSeminar, Gene Kim will discuss these survey findings and will share woeful tales of artifact management gone wrong! Gene will also share examples of how …

Most Popular Programming Stories

More for Developers

Latest Developer Headlines

RSS Feeds