"Icosahedro n" <no***@nowhere. com> wrote in message
news:q0******** *************** @bgtnsc04-news.ops.worldn et.att.net...
I've been going through some old code trying to clean it up
and rearchitect it based on more modern C++ idioms. In the
old code I often used the Pimpl idiom on a class by class
basis, creating impl within a class as such:
[snip]
Still, this has not quite gotten me where I want to be. I
would like to be able to have an A::_impl and a B::_impl
that I can distinguish inside of B based on the methods that
have to be called. The other is that, if possible, I wish
to refer to them as A::_impl rather than Pimpl<A>::_impl and
likewise for B.
Any suggestions? Is this whole thing just bad form? Should
I just stick with the original approach?
FWIW, I finally got what I wanted, more or less. Here's a sample showing
what I've done so far. Amazing how simple these things always seem to boil
down to.
#include <iostream>
using namespace std;
template <typename T>
struct Impl;
class C;
class A {
struct Impl<A>* _impl;
template <class T> friend class Impl;
public:
A();
};
class B : public A {
struct Impl<B>* _impl;
template <class T> friend class Impl;
public:
B();
void Print(C*);
};
class C {
struct Impl<C>* _impl;
template <class T> friend class Impl;
public:
C();
};
struct Impl<A> {
A* that;
Impl(A *a) { that = a; }
void Print(void) { cout << "In Impl<A>::Print" << endl; }
};
struct Impl<B> {
B* that;
Impl(B *b) { that = b; }
void Print(C* c);
};
struct Impl<C> {
C* that;
Impl(C *c) { that = c; }
void Print(void) { cout << "In Impl<C>::Print" << endl; }
};
A::A() : _impl(new Impl<A>(this)) {}
B::B() : _impl(new Impl<B>(this)) {}
C::C() : _impl(new Impl<C>(this)) {}
void Impl<B>::Print( C* c)
{
cout << "In Impl<B>::Print" << endl;
that->A::_impl->Print(); // shows accessing a base class' impl
c->_impl->Print(); // shows accessing another class' impl
}
void B::Print(C* c)
{
cout << "In B::Print" << endl;
_impl->Print(c); // forward to impl class
}
int main(void)
{
C c;
B b;
b.Print(&c);
}
As you may see, I dropped the Pimpl being inherited. I don't know why I was
thinking it, but I was doing that to allow for the automatic declaration of
the Impl class, which would force the implementer to define a nested Impl
class. Unfortunately, there is no way to extend friendship between these
classes to the nested Impl classes or the main classes well.
As for the Pimpl idiom being correct or not for this application, I chose it
because it is a cross platform application wherein I wanted the
implementation specific details to be kept out of the interface classes
(though truthfully the interface classes contain data common to all
platforms too, so they are not pure interface classes). I could have just
defined the methods themselves for the classes on a platform by platform
basis (each implementation for A could have been in separate directories for
instance), and just kept the data in the impl structures, which would have
worked I believe, and still given me the same opacity with regards to the
specifics, but I wanted the freedom to add methods instead of just helper
functions, though the distinction is pretty moot.
The main reason I wanted this was that in my particular application, a
renderer, the impl classes need access to details in other impl classes
(such as texture id from the TextureImpl for setting the current texture in
the CameraImpl class), and I didn't want to have to have friends defined for
every particular combination. (The friends were required because to get to
the _impl member of another class required that you have access to the
private members of that class.) I could have done it one of several ways I
believe:
1) make the _impl member public - this means anyone can access it, but
without the interface, it's pretty hard to do anything with it. This also
could be done also with a GetImpl member function. Opaque, but not guarded
from modification well.
2) friends. Of course there's the obvious friend class declaration, but as
mentioned, for every shader for instance, the Camera::Impl would have to be
put in, and likewise each Shader::Impl would have to be listed in Camera.
And that has to be in the main header file, not the implementation files.
Or, I could do as I did and make all the Impl classes friends by default
with one line. Not perfect, but done only once and still get the benefits
of hidden implementation and access restriction from the outside.
Along with #2, I am considering this is being designed to allow extension by
other developers (with their classes being first class members of the
library as well rather than some hacked in extension mechanism), I wanted
some way for them to add to the library without having to add their classes
as friends to the main classes.
So, the main mechanism is that you have to have an Impl<T> member named
_impl and a friend declaration "template <class T> friend class Impl;" that
will automatically define other Impl<T> classes in the module as friends of
the class. I keep the Impl class definition in the Render namespace, to
avoid any potential conflicts and to preserve some semblance of module
integrity.
Well, that's the short of it. Please feel free to comment, as I do read
them, if I don't respond to each one. Thanks again.
Jay
P.S. The above code compiles fine on GCC 3.2.3