 |
C++Talk.NET C++ language newsgroups
|
| View previous topic :: View next topic |
| Author |
Message |
Dumaiu Guest
|
Posted: Tue Feb 14, 2006 1:06 am Post subject: Idea: A template-specialization-Cheshire-Cat alternative to |
|
|
Hello all,
Please forgive the newbie-ish tone of this, my first thread. I'd like
to float an idea I had and see what you guys think.
I first stumbled across the Pimpl (which goes by many names, including
the most charming 'Cheshire Cat' in GotW. As I understand it, it
eliminates header recompilation but introduces memory management and
complicates inheritance.
So, a thought. It's possible, by making a C-Cat with a template
specialization technique, to bypass some of the disadvantages but
retain the Pimpl's benefit of reducing compilation time. What follows
seems pretty strange to me, so I'll err on the side of overexplaining.
Is it useful, dangerous, or am I just covering old ground?:
//> classcat.hpp
// The implementation base class, needed by the primary header
(class.hpp) and the implementation file (class.cpp).
#ifndef CLASSCAT_HPP // Inclusion guards are very important here.
#define CLASSCAT_HPP
template <class tInheritor>
class ClassCat
{
// ctor, dtor, &c should be declared here:
protected:
ClassCat(int initval);// ctor
};// ClassCat
#endif
---------------------------------------
//> class.hpp
// The header file we want to avoid recompiling.
#include "classcat.hpp"
// Public interface for inclusion by client.
class Class : protected ClassCat<Class> // Use 'private' if you don't
want subclass access to the implementation.
{
public:
Class(int initval);// ctor will chain up.
void DoSomethingUseful(); // whatever you like
... // whatever else
};// Class
---------------------------------------
//> class.cpp
// Implementation file, with all changes.
#include "classcat.hpp" // Import template declaration
class Class; // Fwd dec
// Implementation data as template specialization:
template <>
class ClassCat<Class>
{
protected:
// Inherited ctor:
ClassCat(int initval): datum(initval) {}// ctor
int datum;
};// ClassCat<>
// Method definitions:
#include "class.hpp"
#include <cstdio>
Class::Class(int initval): ClassCat<Class>(initval) {}// ctor
void Class::DoSomethingUseful()
{ printf("Print private datum: %i\n", datum); }//
---------------------------------------
//> main.cpp
// Client program.
#include "class.hpp"
int main()
{
Class c(100);
c.DoSomethingUseful(); // -> 'Print private datum: 100', peachy.
}// main()
Looks pretty nice to me--no allocation/deallocation, dereferencing, or
casts. It works by compiling class.hpp with the memberless base class,
in classcat.hpp, but links in, in "bait and switch" fashion, the full
version in class.cpp. Thus the client never needs to be recompiled.
The executable produced for this example by gcc was actually about a KB
smaller than that of one I wrote using a standard Pimpl. Unfortunately
I don't know how to benchmark speeds.
It does introduce a second header file, the classcat.hpp. I haven't
examined every detail, but I did noticed a couple of (not too alarming)
curiosities:
1) If any of the four automatically-generated methods (ctor, dtor,
.... ) is defined in class.cpp:ClassCat<Class> but not declared in
classcat.hpp:ClassCat<>, it will be silently ignored. E.g., any of
these methods not told to chain up explicitly will call an
autogenerated empty version in ClassCat<>. So class.hpp:Class must
match classcat.hpp:ClassCat<> and class.cpp:ClassCat<Class> and its
class.cpp method definitions. A bit of a pain.
2) In my version of gcc, at least, trying to inline ClassCat<Class>'s
destructor in class.cpp produces a linker error:
"undefined reference to `ClassCat<Class>::~ClassCat
[not-in-charge]()'"
Normal definition is fine.
Just a thought.
Thanks in advance,
J. J.-S.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Valentin.Samko Guest
|
Posted: Tue Feb 14, 2006 11:06 am Post subject: Re: Idea: A template-specialization-Cheshire-Cat alternative |
|
|
| Quote: | //> classcat.hpp
template <class tInheritor
class ClassCat {}
//> class.hpp
class Class : protected ClassCat<Class> {}
//> class.cpp
#include "classcat.hpp" // Import template declaration
class Class; // Fwd dec
template
class ClassCat<Class> { ... };
|
This will result in an ODR violation as soon as your clients start
creating objects of your class in other translation units as they will
see a different definition of ClassCat<Class> than you do in the
class.cpp.
Valentin Samko
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Igor A. Goussarov Guest
|
Posted: Tue Feb 14, 2006 12:06 pm Post subject: Re: Idea: A template-specialization-Cheshire-Cat alternative |
|
|
Hi Dumaiu,
Dumaiu wrote:
[...]
| Quote: | I first stumbled across the Pimpl (which goes by many names, including
the most charming 'Cheshire Cat' in GotW. As I understand it, it
eliminates header recompilation but introduces memory management and
complicates inheritance.
|
I would put it in other way: it hides the implementation details
from the user of the class. Eliminating recompilation dependencies is
the effect.
| Quote: | So, a thought. It's possible, by making a C-Cat with a template
specialization technique, to bypass some of the disadvantages but
retain the Pimpl's benefit of reducing compilation time. What follows
seems pretty strange to me, so I'll err on the side of overexplaining.
Is it useful, dangerous, or am I just covering old ground?:
[...]
I haven't
examined every detail, but I did noticed a couple of (not too alarming)
curiosities:
[...] |
The example you've provided is broken. Let's first see why, and then
let's see why does Pimpl require an inderection.
First, what will the source files look like after preprocessing
(namely, after inclusion of all headers)? I've skipped irrelevant
declarations and definitions, to make the things more obvious:
class.cpp:
template <class tInheritor>
class ClassCat { };
class Class;
template <>
class ClassCat<Class> { };
class Class : protected ClassCat<Class> { }; // (1)
main.cpp:
template <class tInheritor>
class ClassCat { };
class Class : protected ClassCat<Class> { }; // (2)
In the first translation unit, at (1), the definition of
ClassCat<Class> is required. The compiler will instantiate explicitly
specialized definition ClassCat<Class>. In another translation unit, at
(2), the compiler will also require completely defined class
ClassCat<Class>. But since no explicit specialization was found in
scope, it will use implicitly instantiated ClassCat<Class>, taken from
your generic non-specialized definition.
As you see, the instantiation of ClassCat<Class> will be different
in these two translation units. I believe, this violates ODR (one
definition rule), see 3.2p5 in the standard.
A simple example to illustrate that your program is broken:
see what is the sizeof(Class) in main.cpp and in class.cpp.
Note: All the curiosities you've described are caused by the linker
being able or not being able to pick the proper method definition by the
name. You should never rely on the linking tricks - as you've seen they
may work and may not work depending on luck, which is not acceptable in
most projects :-)
Now about the indirection in Pimpl. *Declaration* and *definition*
are two different terms. Well, that's the whole point: declaration of a
data member
TImpl* pImpl;
is valid even if TImpl is not defined at this point. Which means that
the definition of TImpl is not required in any of translation units
where the containing class is defined and used.
If you were to use TImpl with no indirection - by means of
aggregation or inheritance - then you have no other option but to make
the definition of TImpl visible to all. Which would effectively break
the purpose of Pimpl idiom. You've tried to trick the compiler by
providing two different definitions: a generic public one and "the real"
specialized one, which is illegal.
Igor
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Maxim Yegorushkin Guest
|
Posted: Tue Feb 14, 2006 2:06 pm Post subject: Re: Idea: A template-specialization-Cheshire-Cat alternative |
|
|
Dumaiu wrote:
[]
| Quote: | So, a thought. It's possible, by making a C-Cat with a template
specialization technique, to bypass some of the disadvantages but
retain the Pimpl's benefit of reducing compilation time. What follows
seems pretty strange to me, so I'll err on the side of overexplaining.
Is it useful, dangerous, or am I just covering old ground?:
//> classcat.hpp
// The implementation base class, needed by the primary header
(class.hpp) and the implementation file (class.cpp).
#ifndef CLASSCAT_HPP // Inclusion guards are very important here.
#define CLASSCAT_HPP
template <class tInheritor
class ClassCat
{
// ctor, dtor, &c should be declared here:
protected:
ClassCat(int initval);// ctor
};// ClassCat
#endif
---------------------------------------
//> class.hpp
// The header file we want to avoid recompiling.
#include "classcat.hpp"
// Public interface for inclusion by client.
class Class : protected ClassCat<Class> // Use 'private' if you don't
want subclass access to the implementation.
{
public:
Class(int initval);// ctor will chain up.
void DoSomethingUseful(); // whatever you like
... // whatever else
};// Class
---------------------------------------
//> class.cpp
// Implementation file, with all changes.
#include "classcat.hpp" // Import template declaration
class Class; // Fwd dec
// Implementation data as template specialization:
template
class ClassCat<Class
{
protected:
// Inherited ctor:
ClassCat(int initval): datum(initval) {}// ctor
int datum;
};// ClassCat
|
The problem here is that you declare a specialization *after* its first
use, what violates the standard. It has a good chance of not working as
you expect it to.
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
Dumaiu Guest
|
Posted: Thu Feb 16, 2006 2:06 am Post subject: Re: Idea: A template-specialization-Cheshire-Cat alternative |
|
|
Thank you, gentlemen. As Mr. Goussarov points out, my technique will
result in a instances of a class having different sizes between
translation units, and, as Mr. Samko points out, this is bad. Standard
3.2p5 is, indeed, the most basic of several paragraphs that I treaded
on (*How* did you know which one off the top of your head?), although
14.7.3p7 gets more expressive:
When writing a specialization, be careful about its location; or to
make it compile will be such a trial as to kindle its self-immolation.
Wow! Although I do feel disappointment, of course, I am a bit
perversely pleased that I produced such a violently ill-formed program,
and got it to run, without reinterpret_cast(). As Mr. Goussarov put
it,
| Quote: | You should never rely on the linking tricks - as you've seen they
may work and may not work depending on luck, which is not acceptable in
most projects
|
To conclude: Are there *ever* any situations in which buffaloeing the
linker gives an advantage?
-J
[ See http://www.gotw.ca/resources/clcm.htm for info about ]
[ comp.lang.c++.moderated. First time posters: Do this! ] |
|
| Back to top |
|
 |
|
|
You cannot post new topics in this forum You cannot reply to topics in this forum You cannot edit your posts in this forum You cannot delete your posts in this forum You cannot vote in polls in this forum
|
|