Thursday, May 12, 2005
Using Interfaces in C++ (I)
The distinction between classes and interfaces is a powerful language feature present both in Java and C# (and probably many other languages) but not in C++. For years, our team has been using a "methodological" implementation of the interface concept for C++. Starting in VS7, there's also an MS extension pointing in 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 be aware of.
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 clear 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 internet articles, which described more or less the same ideas, and at least some of them were dated way before we had even came to think about it.
More recently, I wrote about this technique in a rather lengthy usenet post, but let me summarize it here.
First, some macros are defined in a header file, which you'll probably want to include in your precompiled headers:
Using these macros, you can declare an interface in the following way:
Then you can declare a class that implements this interface with something like:
Easy, isn't it? Without much effort you are now able to use interfaces in C++. However, as they aren't directly supported in the language, you are supposed to follow some rules, which can't be automatically enforced at compilation time. After all, all the compiler can see is the use of plain old multiple inheritance. So, here are the rules:
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 clear 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 internet articles, which described more or less the same ideas, and at least some of them were dated way before we had even came to think about it.
More recently, I wrote about this technique in a rather lengthy usenet post, but let me summarize it here.
First, some macros are defined in a header file, which you'll probably want to include in your precompiled headers:
#define Interface class
#define DeclareInterface(name) class name \
{ \
public: \
virtual ~name() {}
#define DeclareBasedInterface(name, base) class name \
: public base \
{ \
public: \
virtual ~name() {}
#define EndInterface };
#define implements public
Using these macros, you can declare an interface in the following way:
DeclareInterface(IBar)
public:
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:
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 are now able to use interfaces in C++. However, as they aren't directly supported in the language, you are supposed to follow some rules, which can't be automatically enforced at compilation time. After all, all the compiler can see is the use of plain old multiple inheritance. So, here are the rules:
- When declaring a class, use the first base class for "structural" inheritance (the "is a" relationship) if there is one, as you normally do (eg: CFrameWnd derives from CWnd, CBitmapButton derives from CButton, YourDialog derives from CDialog, and so on). This is specially 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 (eg: class Foo : public BasicFoo, implements IBar, implements IOther, implements IWhatever, ...)
- Do not declare any member variables inside interfaces. Interfaces are intended to express behaviour, not data. Besides, this helps 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 member functions in interfaces as virtual pure (ie: with "= 0"). This ensures every instantiable class that declares to implement an interface does it for all its functions. It's OK 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. Since 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, as 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. Since you will be in fact using multiple inheritance (even if for virtually every other practical need you can think of it as single inheritance plus interace 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.
- In fact 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, as it isn't easy to detect and resolve those conflicts if you have a class implementing both.



10 Comments:
Nice post :)
I will use it in my next projects.
Cheers
By
Javier Luna, at 5:01 PM, May 12, 2005
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.
Other than that, it's a good technique.
By
mschaef, at 5:16 PM, May 12, 2005
Welcome to the blogsphere!!
By
Enrique Almeida, at 6:35 PM, May 12, 2005
Thank you all for your encouraging comments.
I think mschaef raises a good point. There are some comments about it in the second part.
By
Jose Lamas Rios, at 1:53 AM, May 27, 2005
I can help.
The answer to getting C++ to work with Interfaces is a combination of my CCom class, and adding to ANSI C++, the ability to overload the "." (DOT) operator. I have already contacted Bjarnbe Stroustrup about this, and he says it would be difficult, but not impossible. With the ability to overload the "." operator in such context as this:
SomeBaseInterfaceReturnClass operator .(...);
One can finally use interfaces in C++ to the fullest. For example, in VB, Java, one can use ADO as such:
Set Connection_Temp = Server.CreateObject("ADODB.Connection")
Connection_Temp.ConnectionTimeout = 45
Connection_Temp.Open("DSN=MyDb;UID=MyUid;PWD=MyPwd")
Now in C++, that simple set of interface calls is UGLY without using the Type Library, which wraps the whole ADO set of interfaces. In order to get interfaces TRULY accessible in C++, the DOT operator would have to be overloadable so that a base Interface class could then wrap interface objects like that VB sample above did, where C++ would see "Connection_Temp.ConnectionTimeout = 45" and know that "Connection_Temp" is based off the base interface, and then see that yes, the base interface class does not have a member function or data member called "ConnectionTimeout", but it does have the "DOT" operator overloaded, so it will call that function as this:
CInterfaceClass::operator .("ConnectionTimeout");
Then the overloaded DOT operator function would grab the string "ConnectionTimeout" and try to find at run time (without causing any errors at compile/link time) a "property" or "method" that the current object Connection_Temp has. Once found (run timae again), it would then return a "reference" to it, which then has its overloaded "=" function called to set it to be 45.
I've accomplished 90%++ percent of this with my own CCom class, but without the "." operator overloaded, I could only overload the () and [], which I used () for method calls and [] for properties, and there are still limitations, especially in that I have to put quotes (") around the names of the property or method I want to access. Otherwise, C++ lets my define a class that returns a reference to the property or method that I can then work upon.
Please email me if you have any question.
John Brumbelow
JBrumbelow@OptiDoc.com
By
John Brumbelow, at 9:30 AM, June 17, 2005
Interesting concept, but nothing I'd allow into my code base, as I believe if obfustaces more than it clarifies (as macros usually do).
By
Per, at 11:47 AM, June 29, 2005
The standard in COM is to use the "I" prefix, e.g. IUnknown, to signify that a class is an abstract base class or interface, i.e. contains only pure virtual methods. The lack of the keyword "Interface" is not a problem that needs to be solved in my view since C++ is quite able to use multiple inheritance you are free to "extend" one class (or more) while you "implement" several "interface" or abstract classes, i.e. classes containing only pure virtual methods.
C++ is C++. Java is Java. PHP is PHP. C# is C#. Why try to morph one into another? If you strive to make C++ look like Java you'll only end up confusing a future maintainer of your code who happens to be familiar with C++ but not Java. If you're going to write C++ then write C++. Obfuscating your C++ to look like Java is a waste of effort on your part.
By
Ben Dash, at 1:44 AM, July 23, 2005
Hi, Ben: thank you very much for your comments (in this and in the other post).
You wrote:
The lack of the keyword "Interface" is not a problem that needs to be solved in my view since [...]
I know, since I can already do that, right? The same way I don't need a while construction to write a loop when I can do it with an if and a goto. :)
There's a big difference between writing some things (be it a while loop or the concept of an interface class) out of primitive constructions (if, goto, class), over and over again, always in the same fashion, always observing the same rules (e.g.: only pure virtual functions, no data members, virtual destructor), always being prone to the same mistakes (e.g.: forgetting a virtual destructor, forgetting a pure virtual specifier), and encapsulating that precise kind of use into a distinct notation that supports, implements, and enforces the pattern. I don't think I can explain it better than this article from Charles Simonyi, about Notations and Programming Languages.
It's not that I'm trying to morph C++ into Java, nor that I strive to make C++ look like Java. I do object oriented design, and that design is not tied to a particular language. While designing, I find the interfaces pattern very useful. This pattern is first of all a design thing, and it certainly doesn't belong or is confined to Java, COM or C#; you can even find Bjarne Stroustrup talking about these interfaces (he describes them as pure interfaces, and explaining why and when he thinks they are useful for object oriented design in the context of C++ programming.
Now, if a design that uses the interfaces pattern needs to be implemented in Java or C#, the interfaces in the design can be mapped directly to the interfaces in the implementation language. But if you use C++ for the implementation, you need to resort to lower-level language constructions, and make use of abstract base classes, make sure you only include pure virtual functions, etc. You aren't doing anything conceptually different, you are just doing it in a different way, which in Simonyi's words, has more degrees of freedom.
That means there is a mismatch between the level of detail in my design (even in those cases where it's only a mental, not formally written design), in which I simply express something to be an interface, and the level of detail in my implementation, in which I must say something is an abstract base class with only pure virtual functions and a virtual destructor, and even after that, let's not forget, make sure the class remains that way. Precisely because of this mismatch, part of the design is lost in the implementation. For example, once I write an abstract base class with only pure virtual functions to represent the interface in my design, there's nothing there indicating (much less enforcing) that the lack of anything but a virtual destructor and pure virtual functions is a design requirement and not a mere coincidence. A while later, any of the future maintainers of my code you mentioned, and that includes myself, might be breaking one of the design requirements (e.g.: adding a data member to the class.) How would he know that he isn't supposed to do that when he has so much freedom? In order to avoid doing such mistakes he needs to reverse-engineer the implementation so as to recognize the class as an interface in the design. It's either that or resorting to the usually out-of-date comments or documentation. I find that's time-consuming and error-prone, and thus, highly undesirable.
That's what I'm attacking here, and the attack consists of extending the language notation to directly support, implement, and enforce the way I like to design, that is, using the interfaces pattern. You may say this "obfuscates" the code, but what exactly does that mean? Granted, I'm hiding the details that the interface concept is implemented through an abstract base class with only a virtual destructor and pure virtual functions, but that's the whole point: hiding those implementation details which aren't part of the design domain. I don't want to see "this class is an abstract base class with blah, blah, blah", I want to see "this is an interface class". I don't want to see "this class derives from that abstract base class with blah, blah, blah", I want to see "this class implements that interface". I don't call it "obfuscation", I call it abstraction and encapsulation. Would you say having a while construction obfuscates the presence of an if and a goto? If I think and design in terms of while I don't want to see the underlying if and goto. If I think and design in terms of interface, I don't want to see the underlying abstract base class with a virtual destructor and only pure virtual functions.
I hope that now that I made explicit the reasons why I'm doing this, you realize it has nothing to do with making C++ look like Java; that was neither the motive, nor it's the important point. What I do, does make programming with interfaces in C++ similar to programming with interfaces in Java or C#, but it's not just for the sake of emulation. That being said, if I'm going to give my code support for the interfaces pattern, and remember that's for the reasons I explained above, I think that making it look similar to the way it's done in Java or C# is the wise thing to do, anyway. Many C++ programmers, just like myself, also program in Java or C#, or at least have some basic knowledge about them, so using something they would find familiar can only be of help.
By
Jose Lamas Rios, at 9:44 PM, July 23, 2005
Apart from the fact that I dislike the whole idea of using macros to somehow make abstract base clases into interfaces - an abstract base class IS ALREADY an interface in C++. You seem to be under the mistaken belief that their destructors must always be virtual. They shouldn't. They should only be virtual if you intend to delete an object that implements the interface via a pointer to that interface. Mostly, I find that this isn't the case, in which case the destructor should be non virtual and protected so that the client of the interface is restricted from deleting the object via the interface...
See http://www.gotw.ca/publications/mill18.htm
(guideline #4) for more details.
The language doesn't need extending to incorporate the concept you just need to learn to understand the local idioms :)
By
Len Holgate, at 4:08 PM, July 27, 2005
I started writing this as a reply to the previous comment by Len Holgate. However, it became such a long message I decided to promote it to a full post. Here is then, my response:
Why a Public Virtual Destructor
By
Jose Lamas Rios, at 6:48 PM, August 21, 2005
Post a Comment