So, you’re a C++ programmer, and you’ve heard of Go. You really like the idea of Go’s automatic interface satisfaction, and you are green with Go-envy.
What you don’t know is you can write those in C++ too. Yeah, really. C++’s type system is nutso. Let’s see how you use them.
// Person is an interface type with these methods... // const char* name () // void talk () // This function takes one as an argument void talkabout (Person t) { printf("%s says:\n", t.name()); t.talk(); } // No "implements" declaration, no virtual methods struct Foo { int x; const char* name () { return "Foo"; } void talk () { printf("I'm a Foo with a %d!\n", x); } }; // Watch the magic int main () { Foo x = {24}; talkabout(&x); return 0; }
What output does this produce?
Foo says: I'm a Foo with a 24!
Nice, huh? What happens when the type doesn’t satisfy the interface?
struct Nameless { void talk () { printf("I have no name."); } }; int main () { Nameless x; talkabout(&x); return 0; }
With this, we get an error message.
if3.cpp: In instantiation of ‘const Person::implemented_by
Well, C++’s error messages are always a little weird when you’re doing metaprogramming. If you’ve done a lot of it, you’ll know that this is a surprisingly sane message, given all the magic that’s happening. It can’t instantiate Person as implemented by Nameless, because name is not a member of Nameless.
So what does the interface definition look like? Yeah, um...
struct Unknown { };
class Person {
// Vtable
template <class T>
struct implemented_by {
const char* (T::* name ) ();
void (T::* talk ) ();
static const implemented_by vtable;
};
// Interface struct is two pointers long
const implemented_by<Unknown>* const vt;
Unknown* const p;
public:
// Methods
inline const char* name () { return (p->*(vt->name))(); }
inline void talk () { return (p->*(vt->talk))(); }
// Conversion
template <class T>
Person (T* x) :
vt(reinterpret_cast<const implemented_by
You knew it couldn’t be that perfect, right? C++ allows you to do all sorts of crazy things with its type system as long as you’re okay with writing the ugliest code you’ve written in your life. This can’t easily be reduced to macros either.
So it isn’t pretty, but maybe it could be useful in some specific scenarios that don’t happen to be part of any project you’ll ever work on. Disclaimer: Please don’t actually use this in any real production code you ever write! When you need interfaces, just inherit from pure virtual classes like everyone else.
How far can this idiom be extended? Actually, you can make it work with overloaded method names as well; even Go doesn’t support that. My original reference implementation tested overloaded names, but I decided it was too much information to include in this post. In addition, although I have not tried it, I believe that with even more hideous code you can get it to detect static attributes of a class and consider those when satisfying the interface, because they share enough syntax with methods. Unfortunately I cannot think of any way to get the interface to use non-static attributes. As it happens, you can only cheat so far before you’re caught.