Using nonvirtual inheritance for informal 'interfaces'?

Started by
17 comments, last by Matt-D 10 years, 9 months ago

Waterlimon, since you plan never to actually instantiate classes of type Foo you are correct that you do not need virtual functions, and your method for run-time checking that methods have been implemented is fine, at least in debug mode.

But a better option for ensuring things work how you want them to work is to make the constructor of Foo protected rather than inheriting from it in a protected or private way—making the constructor protected ensures no future mistakes can occur when one day you accidentally forget and inherit from it in a public manner.

L. Spiro

I restore Nintendo 64 video-game OST’s into HD! https://www.youtube.com/channel/UCCtX_wedtZ5BoyQBXEhnVZw/playlists?view=1&sort=lad&flow=grid

Advertisement
There is a clever template trick to check the presence of members at runtime. This would allow you to at least check if interfaces are actually implemented.

Off the top of my head:

#include <iostream>

/*
interface Foo {
	foo();
	bar();
}
*/

template <typename T>
struct ensure_implements_foo {
	decltype(&T::foo) foo;
	decltype(&T::bar) bar;
};

class A {
public:
	int foo() {return 0;}
	void bar() {}
};

class B {
public:
};

int main() {
	ensure_implements_foo<A> checkA; // compiles fine
	ensure_implements_foo<B> checkB; // fails compilation, because B does not implement Foo
}
If you get cute with the template logic, you should be able to also confirm return types, and potentially even full function signatures.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]


There is a clever template trick to check the presence of members at runtime.

You know, it now strikes me that this is a completely pointless exercise.

All the template magic in the world, and it still doesn't tell you anything more than you would know if you tried to call the function in the first place.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]


All the template magic in the world, and it still doesn't tell you anything more than you would know if you tried to call the function in the first place.

Oh, but the obscure error message you would get with the method you posted is much more fun to track down. :)

When they say "code should be self-documenting", thats probably not what they mean biggrin.png Sorry for offtopic, here is some helpful advice:

Why don't you just use the pure virtual interface method, but put an ifdef around the virtual methods, eliminating them for release mode?


class Foo
{
#ifdef _DEBUG

virtual void test(void) = 0;
virtual void test2(void) = 0;
#endif
};

class Bar : public Foo
{
void test(void); // compile in both modes
// doesn't compile due to test2 missing in debug mode
};

This will at least remove the overhead of the virtual methods in release mode. I don't know if this is such a great idea code-style wise, and I personally don't think its necessary or adds anything, but you asked... what do the experienced guy say?

I've had these thoughts before and ultimately realized it was not necessary. Since you're not using virtual inheritance, you're not going to be storing all your objects in some big list of IThings (which is good), unless you implement your own runtime type identification and branch on the type (at which point you might as well use virtual).

Instead, embrace that each of your Things is different and store them in different lists so you don't need to know the type. You can always document naming/interface schemes instead. As the others have said, just start writing code and let the compiler do the work for you. smile.png

T

Oh, but the obscure error message you would get with the method you posted is much more fun to track down. smile.png

You should rejoin the modern world, my friend, where template errors are no longer scary:


$ clang++ -std=c++11 -o foo foo.c++

foo.c++:12:18: error: no member named 'foo' in 'B'
    decltype(&T::foo) foo;
              ~~~^
foo.c++:28:30: note: in instantiation of template class 'ensure_implements_foo<B>' requested here
    ensure_implements_foo<B> checkB; // fails compilation, because B does not implement Foo
                             ^
foo.c++:13:18: error: no member named 'bar' in 'B'
    decltype(&T::bar) bar;
              ~~~^
2 errors generated.

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Oooohhh! Aaaahhhh!

Now I just have to convince my company to ditch g++. :)

Sounds like you may be interested in the Curiously Recurring Template Pattern (CRTP):

http://en.wikipedia.org/wiki/Curiously_recurring_template_pattern

http://eli.thegreenplace.net/2011/05/17/the-curiously-recurring-template-pattern-in-c/

http://stackoverflow.com/questions/262254/crtp-to-avoid-dynamic-polymorphism

http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Curiously_Recurring_Template_Pattern

http://accu.org/index.php/journals/296

Alternatively, check out the non-virtual interface (NVI) pattern:

http://en.wikipedia.org/wiki/Non-virtual_interface_pattern

http://en.wikibooks.org/wiki/More_C%2B%2B_Idioms/Non-Virtual_Interface

http://stackoverflow.com/questions/6481260/non-virtual-interface-design-pattern-question-in-c-c

http://herbsutter.com/2013/05/22/gotw-5-solution-overriding-virtual-functions/

This topic is closed to new replies.

Advertisement