Topic: Intermediate implementation classes
Author: Francis Glassborow <francis.glassborow@ntlworld.com>
Date: Sat, 15 Sep 2001 23:24:02 GMT Raw View
In article <af78075c.0109140711.34b124b2@posting.google.com>, Manuel
<kessler@iag.uni-stuttgart.de> writes
>class Base {
>public:
> Base() {}
> void foo() {}
>protected:
> class Intermediate;
>};
>
>class Base::Intermediate : public Base {
>public:
> Intermediate() {}
> void bar() {}
>};
This seems infinitely recursive to me. You are saying that
Base::Intermediate is derived from Base, but then it must include
itself. I am confused, so I am not surprised that compilers are :)
Francis Glassborow
I offer my sympathy and prayers to all those who are suffering
as a result of the events of September 11 2001.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: "Carl Daniel" <carl@pixami.com>
Date: Sat, 15 Sep 2001 23:24:10 GMT Raw View
"Manuel" <kessler@iag.uni-stuttgart.de> wrote in message
news:af78075c.0109140711.34b124b2@posting.google.com...
[snip]
>
> For some kind of database library I would like to have a Base class
> representing a generic node (entry) of the database, while most nodes
> are much more specific. However, some of them share many
> implementation details. Therefore my design derives some intermediate
> classes from the base class, and the specific Node classes from the
> intermediate classes (no multiple inheritance). Just like this
> Base <- Intermediate <- Specific.
[snip]
>
> PS: If there is a much more simpler solution to the original problem
> (disallow implicit casts to an intermediate class, but allow cast to
> the base class), I'd appreciate a little hint how to do it.
>
[snip]
There is a simpler way - don't use inheritance to facilitate implementation
reuse. Use delegation & aggregation instead.
class Base
{
// ...
};
class Impl1;
class Derived11 : public Base
{
private: Impl1 impl;
// ...
};
class Derived12 : public Base
{
private: Impl1 impl;
// ...
};
class Impl2;
class Derived21 : public Base
{
private: Impl2 impl;
// ...
};
No sneaky inheritance tricks involved.
The implementation classes need not be present in user-visible headers at
all if the Derived instances use them by pointer (rather than direct
embedding).
This sort of technique frequently does require writing forwarding functions
in the derived classes, but the decoupling is worth it. If you're sneaky,
you can write a template "shim" class which both embeds the re-used
implenentation AND inserts the forwarding functions into the derived class,
so you may only have to write them once:
class Base
{
// interface defined by virtual functions
};
template <typename D> class Impl1
{
// implementation of SOME of Base's interface.
// note that this class doesn't derive from Base
// template parameter D will be the type of the most-derived object,
// might be needed in here for the occasional cast
};
class Derived11 : public Base, public Impl1<Derived11>
{
// implementation of more (the balance) of Base's interface
};
That's just a sketch - IMO it's preferable to simply use delegation and not
resort to any such trickery.
Interesting question about the accessibility of nested classes for the
purpose of derivation, however.. I await the answers from the gurus.
-cd
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: firstian@nospam.rcn.com (Joe Chan)
Date: Sat, 15 Sep 2001 23:24:23 GMT Raw View
I don't know if this solves your problem, but have you considered using
the method described in Barton & Nickman called base class composition
(or something like that)? I don't remember the exact details, but it
does something like this:
class Base {...};
class Intermediate : public virtual Base {...};
class Specific : public virtual Base, private Intermediate {...};
which will not let you get from Specific* -> Intermediate*. Last time I
read the book was about 4 years ago, so you definitely want to check the
book out and not trust my description here.
Manuel <kessler@iag.uni-stuttgart.de> wrote:
> This issue is a little bit long, but it raises a subtle question about
> the order of granting and checking access rights when inheriting.
> Apparently this question has not been asked before (at least as I have
> checked the archives). Sorry if I am mistaken.
>
> For some kind of database library I would like to have a Base class
> representing a generic node (entry) of the database, while most nodes
> are much more specific. However, some of them share many
> implementation details. Therefore my design derives some intermediate
> classes from the base class, and the specific Node classes from the
> intermediate classes (no multiple inheritance). Just like this
> Base <- Intermediate <- Specific.
> So far, so good. However, I consider the Intermediate class an
> implementation detail, which should be of no interest for the user.
> Since I still want to be able to use any specific node as a generic
> node, the (implicit) cast Specific * -> Base * (or references) should
> be possible. This rules out private (or protected) inheritance between
> Base and Intermediate or Intermediate and Specific. What I don't want
> is the cast Specific * -> Intermediate *, such that the user simply
> can't do anything with the intermediate class (which may be changed in
> a future release or abandoned entirely).
> Fortunately, I don't have only a problem, but also a solution (at
> least I hope so). This is what I've come up with, to make the
> Intermediate class inaccessible:
>
> // An ugly hack (?) to disable implicit casting
> // to an intermediate implementation class
>
> class Base {
> public:
> Base() {}
> void foo() {}
> protected:
> class Intermediate;
> };
>
> class Base::Intermediate : public Base {
> public:
> Intermediate() {}
> void bar() {}
> };
>
> class Derived: public Base::Intermediate {
> public:
> Derived() {}
> void baz() {}
> };
>
>
>
> void f(Base & b) {
> b.foo();
> }
>
> void g(Base::Intermediate & i) {
> i.foo(); i.bar();
> }
>
> void h(Derived & d) {
> d.foo(); d.bar(); d.baz();
> }
>
> void i() {
> Derived derived;
> Derived * dp=&derived;
> dp->baz(); dp->bar(); dp->foo();
> Base::Intermediate * ip=&derived;
> ip->bar(); ip->foo();
> Base * bp=&derived;
> bp->foo();
> }
>
> Thus I make the Intermediate class a nested class inside the base,
> with protected access. The Derived class is finally derived from Base,
> and so has access to Base::Intermediate. The compiler should flag the
> definition of
> void g(Base::Intermediate & i) and in void i() the definition of ip
> and in the following line its use.
>
> At least that's what I think. Compilers do disagree: gcc 2.7.2.1 and
> 2.95.2 doesn't complain at all about this code, 3.0.1 complains as
> expected. SGI MIPSpro 7.30 and 7.3.1.1m don't like the derivation of
> Derived from Base::Intermediate ('Base::Intermediate is
> inaccessible'), and so does IBM xlC 5. Since gcc 3.0.1 is the newest
> one and has changed its behaviour from previous versions I am inclined
> to believe it is right (assuming change of behaviour goes in the right
> direction). After all, I really could use this feature, and therefore
> I named it the 'Hidden intermediate implementation class idiom'.
>
> The question is, are the (transitive) base class access rights granted
> BEFORE or AFTER access right to the base class in question itself is
> checked. As I don't have a copy of the standard available, I could
> only check with CD2 and didn't find any wording concerning the order
> of granting and checking access rights from inheritance. Things are
> even more complicated when multiple inheritance is involved: First all
> granting, then all checking, or the other way round, or for each base
> first granting, then checking, or for each base the other order.
> Perhaps someone more knowledgable could clarify this point.
>
> Thank you very much for your time.
>
> Ciao,
> Manuel
>
> PS: If there is a much more simpler solution to the original problem
> (disallow implicit casts to an intermediate class, but allow cast to
> the base class), I'd appreciate a little hint how to do it.
>
> PSPS: I know that this only works for a somewhat fixed hierarchy,
> where the number and names of Intermediate classes is well known
> beforehand (they have to be declared in the Base class), and that the
> definition of the Intermediate classes has to be available and
> therefore somewhat visible in the header file. However, this
> definition can be hidden behind an #include directive, and good
> documentation makes looking at the header file unnecessary anyway,
> doesn't it? The only usage of the Intermediate class still possible is
> to derive from it, but I can't see how to solve this without making
> the Intermediate classes private member of the Base class and all
> (intended) Derived classes friends of it.
>
> ---
> [ comp.std.c++ is moderated. To submit articles, try just posting with ]
> [ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
> [ --- Please see the FAQ before posting. --- ]
> [ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
--
Joe Chan
--------
Remove "nospam" from my email address
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: Valentin.Bonnard@free.fr (Valentin Bonnard)
Date: Sun, 16 Sep 2001 10:01:48 GMT Raw View
Manuel wrote:
> This issue is a little bit long, but it raises a subtle question about
> the order of granting and checking access rights when inheriting.
Indeed.
> Apparently this question has not been asked before (at least as I have
> checked the archives). Sorry if I am mistaken.
It's the first time I see it.
> For some kind of database library I would like to have a Base class
> representing a generic node (entry) of the database, while most nodes
> are much more specific. However, some of them share many
> implementation details.
It happens all the time (can I say that things are _always_ that way ?).
> Therefore my design derives some intermediate
> classes from the base class, and the specific Node classes from the
> intermediate classes
I see.
> (no multiple inheritance).
Why ?
> Just like this
> Base <- Intermediate <- Specific.
> So far, so good. However, I consider the Intermediate class an
> implementation detail, which should be of no interest for the user.
> Since I still want to be able to use any specific node as a generic
> node, the (implicit) cast Specific * -> Base * (or references) should
> be possible. This rules out private (or protected) inheritance between
> Base and Intermediate or Intermediate and Specific. What I don't want
> is the cast Specific * -> Intermediate *, such that the user simply
> can't do anything with the intermediate class (which may be changed in
> a future release or abandoned entirely).
Since Intermediate is protected, it isn't exactly an implementation
detail (implementation detail = private), unless you restrict
inheritance from Base to a finite set of classes (goes against OO...).
Let me summary:
public: Specific IS-A Base
?: Intermediate IS-A Base
private: Specific IS-A Intermediate
You should use private inheritance to model is-implemented-in-term-of
(or private IS-A).
> Fortunately, I don't have only a problem, but also a solution (at
> least I hope so). This is what I've come up with, to make the
> Intermediate class inaccessible:
> // An ugly hack (?) to disable implicit casting
That's natural, in a way, but still a hack, IMO.
And it is not ``ugly''.
> // to an intermediate implementation class
>
> class Base {
> public:
> Base() {}
> void foo() {}
> protected:
> class Intermediate;
> };
>
> class Base::Intermediate : public Base {
> public:
> Intermediate() {}
> void bar() {}
> };
>
> class Derived: public Base::Intermediate {
> public:
> Derived() {}
> void baz() {}
> };
Funny. Really cool.
But I really can't see if it is indeed legal or not. The question is:
what is in a class ? Is it what's between { and } ? Between : and } ?
In other words, the question is whether the act of enumerating base
classes is done by a class or by in the context of the one who
defines the class. By similarity with members, I would say that it
is the class being defined, but the standard isn't very clear.
(Or is it ?)
> At least that's what I think. Compilers do disagree: gcc 2.7.2.1 and
> 2.95.2 doesn't complain at all about this code, 3.0.1 complains as
> expected. SGI MIPSpro 7.30 and 7.3.1.1m don't like the derivation of
> Derived from Base::Intermediate ('Base::Intermediate is
> inaccessible'), and so does IBM xlC 5. Since gcc 3.0.1 is the newest
> one and has changed its behaviour from previous versions I am inclined
> to believe it is right (assuming change of behaviour goes in the right
> direction). After all, I really could use this feature, and therefore
> I named it the 'Hidden intermediate implementation class idiom'.
> Thank you very much for your time.
You are welcome.
> PS: If there is a much more simpler solution to the original problem
> (disallow implicit casts to an intermediate class, but allow cast to
> the base class), I'd appreciate a little hint how to do it.
The solution is very simple: inherit protectedly Intermediate and publicly
Base:
class Base {};
class Intermediate : ? virtual Base {};
class Specific : protected Intermediate, public virtual Base {};
-- VB
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: kessler@iag.uni-stuttgart.de (Manuel)
Date: Mon, 17 Sep 2001 13:14:01 GMT Raw View
Thank you very much for the insightful replies.
As some of you have (very validly) pointed out, this problem can (like
almost all other in IT) solved by introducing another level of
indirection. The most obvious one is deriving Intermediate and Derived
both public virtual from base and Derived additionally private from
Intermediate. Another possibility is to use delegation and decouple
the Intermediate classes (doing the real work) entirely from the
inheritance tree (or aggregation with some less decoupling).
However, in both cases I have to write plenty of forwarding functions
(possibly with the help of some kind of Barton & Nackman trick), about
30 times 10 functions, which is definitely to be avoided for a lazy
person like me, and I get another pointer indirection. Since we talk
about a database application in high performance computing (CFD), this
can be a problem. Database access times are no problem here, since the
entire database is read into memory (cached) and user access is then
basically only reading a memory adress (writing into the database is
much more seldom). Dereferencing an additional pointer is itself in
principle no big problem (although vector supercomputers are a little
bit slow on such scalar operations), but compilers for supercomputers
are very picky in their ability to vectorize and parallelize in the
presence of pointers. Therefore I would like to eliminate this
possible overhead, not because of its own (quite small) performance
loss, but due to its impact on the compilation of the surrounding
code, which absolutely needs to be really fast.
Of course, as we all know, all evil in computing comes from
optimization too early in the development, and there is not yet a
final decision whether the benefit is worth the effort (after all, the
code has to work on existing compilers for existing machines, to be
useful). But in my case I can avoid writing some hundred forwarding
functions (or some ten ugly macros doing the same work) and en passant
take this performance advantage. The first point seems to be important
in terms of maintenance cost as well.
And, detached from the problem at hand, the basic question is still
not answered. Is the proposed inheritance hierarchy conforming or not?
As I now do have a copy of the standard, I looked up there but found
no wording clearing up this issue. My feeling is that it should be
allowed, in some crude sense similar to the deferred checking of
access mentioned at clause 11 [class.access], paragraph 5, which
refers to return types of member or friend functions. The sentence
probably most close to the point in question is the accessability of a
member, in 11.2 [class.access.base], paragraph 4, where it is stated
[...] A member m is accessible when named in class N if
- [...]
- there exists a base class B of N that is accessible at the point of
reference, and m is accessible when named in class B.
The 'point of reference' is the
class Derived : public Base::Intermediate
where the members of Base may or may not be accessible. If nobody can
clarify this, perhaps the wording should be improved in a Defect
Report to state clearly the order of granting and checking access in
this case.
Thanks again for your time.
Ciao,
Manuel
PS: There is no infinite recursion involved, since Base::Intermediate
is only a class nested in Base, and therefore quite unrelated (despite
of syntax and access control) from Base itself.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: transwarpt@aol.com (TransWarpT)
Date: Mon, 17 Sep 2001 21:59:59 GMT Raw View
Have you tried:
class Derived : private Intermediate {
public:
Derived() {}
void baz() {}
operator Base&() { return *this; }
};
In article <af78075c.0109140711.34b124b2@posting.google.com>,
kessler@iag.uni-stuttgart.de (Manuel) wrote:
>class Base {
>public:
> Base() {}
> void foo() {}
>protected:
> class Intermediate;
>};
>
>class Base::Intermediate : public Base {
>public:
> Intermediate() {}
> void bar() {}
>};
>
>class Derived: public Base::Intermediate {
>public:
> Derived() {}
> void baz() {}
>};
Orson Bushnell
TransWarp Technologies, LP.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]
Author: kessler@iag.uni-stuttgart.de (Manuel)
Date: Fri, 14 Sep 2001 18:21:35 GMT Raw View
This issue is a little bit long, but it raises a subtle question about
the order of granting and checking access rights when inheriting.
Apparently this question has not been asked before (at least as I have
checked the archives). Sorry if I am mistaken.
For some kind of database library I would like to have a Base class
representing a generic node (entry) of the database, while most nodes
are much more specific. However, some of them share many
implementation details. Therefore my design derives some intermediate
classes from the base class, and the specific Node classes from the
intermediate classes (no multiple inheritance). Just like this
Base <- Intermediate <- Specific.
So far, so good. However, I consider the Intermediate class an
implementation detail, which should be of no interest for the user.
Since I still want to be able to use any specific node as a generic
node, the (implicit) cast Specific * -> Base * (or references) should
be possible. This rules out private (or protected) inheritance between
Base and Intermediate or Intermediate and Specific. What I don't want
is the cast Specific * -> Intermediate *, such that the user simply
can't do anything with the intermediate class (which may be changed in
a future release or abandoned entirely).
Fortunately, I don't have only a problem, but also a solution (at
least I hope so). This is what I've come up with, to make the
Intermediate class inaccessible:
// An ugly hack (?) to disable implicit casting
// to an intermediate implementation class
class Base {
public:
Base() {}
void foo() {}
protected:
class Intermediate;
};
class Base::Intermediate : public Base {
public:
Intermediate() {}
void bar() {}
};
class Derived: public Base::Intermediate {
public:
Derived() {}
void baz() {}
};
void f(Base & b) {
b.foo();
}
void g(Base::Intermediate & i) {
i.foo(); i.bar();
}
void h(Derived & d) {
d.foo(); d.bar(); d.baz();
}
void i() {
Derived derived;
Derived * dp=&derived;
dp->baz(); dp->bar(); dp->foo();
Base::Intermediate * ip=&derived;
ip->bar(); ip->foo();
Base * bp=&derived;
bp->foo();
}
Thus I make the Intermediate class a nested class inside the base,
with protected access. The Derived class is finally derived from Base,
and so has access to Base::Intermediate. The compiler should flag the
definition of
void g(Base::Intermediate & i) and in void i() the definition of ip
and in the following line its use.
At least that's what I think. Compilers do disagree: gcc 2.7.2.1 and
2.95.2 doesn't complain at all about this code, 3.0.1 complains as
expected. SGI MIPSpro 7.30 and 7.3.1.1m don't like the derivation of
Derived from Base::Intermediate ('Base::Intermediate is
inaccessible'), and so does IBM xlC 5. Since gcc 3.0.1 is the newest
one and has changed its behaviour from previous versions I am inclined
to believe it is right (assuming change of behaviour goes in the right
direction). After all, I really could use this feature, and therefore
I named it the 'Hidden intermediate implementation class idiom'.
The question is, are the (transitive) base class access rights granted
BEFORE or AFTER access right to the base class in question itself is
checked. As I don't have a copy of the standard available, I could
only check with CD2 and didn't find any wording concerning the order
of granting and checking access rights from inheritance. Things are
even more complicated when multiple inheritance is involved: First all
granting, then all checking, or the other way round, or for each base
first granting, then checking, or for each base the other order.
Perhaps someone more knowledgable could clarify this point.
Thank you very much for your time.
Ciao,
Manuel
PS: If there is a much more simpler solution to the original problem
(disallow implicit casts to an intermediate class, but allow cast to
the base class), I'd appreciate a little hint how to do it.
PSPS: I know that this only works for a somewhat fixed hierarchy,
where the number and names of Intermediate classes is well known
beforehand (they have to be declared in the Base class), and that the
definition of the Intermediate classes has to be available and
therefore somewhat visible in the header file. However, this
definition can be hidden behind an #include directive, and good
documentation makes looking at the header file unnecessary anyway,
doesn't it? The only usage of the Intermediate class still possible is
to derive from it, but I can't see how to solve this without making
the Intermediate classes private member of the Base class and all
(intended) Derived classes friends of it.
---
[ comp.std.c++ is moderated. To submit articles, try just posting with ]
[ your news-reader. If that fails, use mailto:std-c++@ncar.ucar.edu ]
[ --- Please see the FAQ before posting. --- ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html ]