Sign in to follow this  
Waterlimon

Using nonvirtual inheritance for informal 'interfaces'?

Recommended Posts

Waterlimon    4398

Is it a good idea to make interfaces like

class Foo
{
    void bar()
    {
        assert(false); //Not implemented in derieved class, go fix nao.
    }
}

class Derieved : public bar
{
    void bar()
    {
        print("yay");
    }
}

to have some idea what methods a class should have, when i need some common functionality for the classes to be used as template args?

 

One problem i see is that it wont fail at compile time if something is not implemented. Is there a way to do something like this, so that a compile time failure would result if something is missing and the base class version is called instead (which will fail at runtime due to the assert)?

 

An obvious solution is to simply comment out the ": public bar" section.

 

Or do you recommend just creating some documentation in a place well hidden somewhere with outdated information as to what methods/typedefs a class should implement to be a 'valid' value for certain templates?

Share this post


Link to post
Share on other sites
frob    44908

Make it an abstract function in the base.

class Foo
{
   virtual bar() = 0;
}

Also note that in your example, the bar() function will hide the base function; it must be marked virtual in order for it to work the way you expected.

Share this post


Link to post
Share on other sites
Waterlimon    4398

I want it to hide the bases methods. This has no actual need, i just want some structure in my program.

I want a class that says that all derieving classes must implement this and this, but i dont want to use virtuals since im not intending to use the classes polymorphically.

Like i said, a solution would be to comment out the ": public Foo" part.

Share this post


Link to post
Share on other sites
Ravyne    14300

Right, first be aware of the differences between inheritance of virtual and non-virtual member functions.

 

If you want a compile time error, use virtual inheritance as Frob shows. Otherwise, if you wanted a run-time assert, or some other default action, its something of a little known fact that you actually *can* give a virtual function an implementation. Derived classes must still implement a version of that function, but they can call specifically to the base class' "default" implementation. This is probably more useful when there is a reasonable default for the method though, rather than just asserting, etc.

Share this post


Link to post
Share on other sites
Waterlimon    4398

The reason i dont want to use virtuals is because i dont need polymorphism and it would add extra overhead.

 

But, if i have a pure abstract base class, will there actually be any overhead (will there be a vtable?)

 

Can i use a pure abstract base class as a compile time enforcement to force the derieving classes to implement certain methods (although i cannot force the classes to derieve from the base class in my template functions)?

Share this post


Link to post
Share on other sites
SiCrane    11839

I don't really see the point. If you try calling a member function on a class that doesn't support it you'll get a compiler error even without any tricky base class usage.

Share this post


Link to post
Share on other sites
Waterlimon    4398

I don't really see the point. If you try calling a member function on a class that doesn't support it you'll get a compiler error even without any tricky base class usage.

 

I know.

 

But i would like a way to document the code in a better way than a random comment somewhere saying what the classes need. With a base class, i have something bundling the classes into the same 'family'.

 

What is standard when there are requirements of classes to be used as template args? How are the requirements documented? In code, in comments somewhere, or in some external documentation? Or do you just copypaste the declaration of a working class and implement them? :P

Share this post


Link to post
Share on other sites
SiCrane    11839

Generally, the documentation for the template says that the template arguments are required to satisfy certain criteria and somewhere that criteria is defined. For example, for the C++ standard library in C++03, the value types of containers are documented to be Assignable and CopyConstructible and references to where those requirements are documented are placed in the container documentation (sections 23.1 and 20.1.3 if anyone cares).

Share this post


Link to post
Share on other sites
Waterlimon    4398
As i dont have external documentation, i shall make an "example class" with method declarations and whatever else is required (no definitions), which i dont actually use anywhere.

Itll have the added benefit of letting me copypaste it when i need a new class that works with my templates.

Until something line concepts gets added, and 5 years from that so they are actually implemented ;P

Share this post


Link to post
Share on other sites
L. Spiro    25621

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

Share this post


Link to post
Share on other sites
swiftcoder    18426
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.

Share this post


Link to post
Share on other sites
swiftcoder    18426


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.

Share this post


Link to post
Share on other sites
alvaro    21246


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. :)

Share this post


Link to post
Share on other sites
Juliean    7068

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?

Edited by Juliean

Share this post


Link to post
Share on other sites
Tessellator    1394

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

Share this post


Link to post
Share on other sites
swiftcoder    18426

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.

Share this post


Link to post
Share on other sites
Matt-D    1574

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this