Re: Sharing interface implementation among impl. of derived interfaces
* alex:
I have a problem sharing common implementation of an interface among
implementations of derived interfaces.
I have three interfaces (abstract classes) in my program: Node, File
and Folder. File and Folder are Node-s themselves, so they are
publicly derived from Node. This cannot be changed and I have to live
with it.
Next, Node interface have methods common to File and Folder. File and
Folder interfaces add methods specific for files and folders.
I only need to have one implementation of File abstract class, say
FileImpl. And one implementation of Folder-- FolderImpl. If I
implement Node interface in a class, say NodeImpl, then how should I
implement FileImpl and FolderImpl?
Any way you like. ;-)
It is clear that simply deriving FileImpl from NodeImpl does not brings
in File's methods, but deriving additionally from File leaves
unimplemented Node sub-object methods in that File. The same is for
FolderImpl.
I have some options now:
1. Include NodeImpl as a private data member into File and Folder
classes, then delegate Node's method calls to this data member. (which
is ugly)
2. Inherit File and Folder from Node using virtual keyword-- no
delegation will be need, but, argh!.. environment restrictions do not
let me do that (I must conform to COM and virtual inheritance of
interfaces is not allowed).
Sad, but true. Virtual inheritance is the language-supported solution
for this problem. But when it's ruled out, it's ruled out.
3. Make NodeImpl a class template taking the class name as a parameter
and make it inherit from that interface like this:
template< typename T >
class NodeImpl: public T
{
//...
};
Then derive FileImpl and FolderImpl like this:
class FileImpl: public NodeImpl< File >
{
//...
};
class FolderImpl: public NodeImpl< Folder >
{
//...
};
This latter one is quite tempting since it works and does not need
delegation of calls to Node methods-- I only need to add implementation
of file- and folder- specific methods :)
This is a very nice summary of the implementation techniques actually
used for this problem.
But the question arises: is there a guarantee that the code for
NodeImpl< File > and NodeImpl< Folder > specializations will be shared
or does this solely depend on the quality of compiler?
Compiler quality.
However, /if/ turns out to be an actual problem, then you can minimize
the templated code by combining forwarding and templatizing, making only
the short forwarding stubs template code:
#include <iostream>
#include <ostream>
#include <string>
struct Node
{
virtual std::string nodeName() const = 0;
};
struct File: Node
{
virtual std::size_t fileSize() const = 0;
};
struct Folder: Node
{
virtual bool folderIsVirtual() const = 0;
};
struct NodeImpl: Node
{
virtual std::string nodeName() const { return "Baluba!"; }
};
template< class T >
struct NodeImplForwarderFor: T, NodeImpl
{
virtual std::string nodeName() const
{ return NodeImpl::nodeName(); }
};
struct FileImpl: NodeImplForwarderFor<File>
{
virtual std::size_t fileSize() const { return 1234; }
};
struct FolderImpl: NodeImplForwarderFor<Folder>
{
virtual bool folderIsVirtual() const { return true; }
};
int main()
{
File const& file = FileImpl();
std::cout
<< file.nodeName() << std::endl
<< file.fileSize() << std::endl;
}
But unless a code size problem has been demonstrated, this would
probably be a case of evil premature optimization.
Please note,
that in my case NodeImpl knows nothing about File or Folder and never
uses it's template parameter T directly. I assume that specific
template parameter affects specialized template class' virtual table
layout, but not the layout of data members. Thus the same code can be
generated (and used) for both NodeImpl< File > and NodeImpl< Folder >.
Am I right?
Probably, as an in-practice matter (AFAIK the standard offers no
guarantee). That's because your NodeImpl inherits only from pure
interface classes with no data members. So whatever data members are
introduced in NodeImpl, they will most probably end up at the same
offsets no matter what those inherited interface classes are.
--
A: Because it messes up the order in which people normally read text.
Q: Why is it such a bad thing?
A: Top-posting.
Q: What is the most annoying thing on usenet and in e-mail?
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ]