Re: Template technicality - What does the standard say?
On Wed, 15 Oct 2008 17:33:04 -0500, Paavo Helde <nobody@ebi.ee> wrote:
Stephen Horne <sh006d3592@blueyonder.co.uk> kirjutas:
This from the guy who just declared that offsetof is only there for C
compatibility, and shouldn't be used.
OK - what's the alternative, then?
As Stephen is not so prone to provide compilable code samples
The following compilable code sample has been provided many times...
template<class T>
struct whatever
{
T field;
};
....
x = offsetof(whatever, field);
For full compilable source, we're talking 7 separate source files with
sizes ranging from about 1000 to about 2500 lines each. Therefore,
descriptions and simplified examples.
I have also provided a reference to a Boost source file that does
basically what I do. Go to www.koders.com and you'll find other
examples. It's not everyday stuff, any more than member pointers or
virtual inheritance, but it's used.
Single inheritance as in your example won't break offsetof in
practice, but IMO it's not the right solution for this.
If any Derived uses Multiple inheritance or virtual inheritance, even
indirectly, offsetof definitely breaks. On some compilers, having
virtual inheritance will cause offsetof to crash immediately (or at
least generate nonsense), since the traditional macro expansion would
end up trying to read the virtual table of a non-existent instance.
OTOH, if Derived is no worse than single inheritance, having fields
that have multiple-inheritance or virtual-inheritance types won't
break it - in practice only - no guarantees from the standard since
your classes are non-POD (even without the inheritance issues from
above).
However, because this depends on users following a rule that the
compiler won't enforce for you, its unsafe. My experience is that
people will forget about that rule.
I'll assume you have good reasons for not making param1 a parameter of
Run.
I'd recommend using pure access functions declared in Base to get at
param1. The virtual call overhead *might* be a problem for you, but
don't optimise until you're sure you need to.
Even if you need to optimise, there are much safer ways to do it. e.g.
You could provide an pure access function in Base that returns a
reference or pointer to the variable within the instance. Then you can
update the buffer as often as you like with minimal cost, so long as
the instance stays put. If there's any possibility that the user might
put the buffer somewhere outside the instance, a lock-and-release
approach might be worthwhile to at least make it explicit that the
buffer is meant to stay put and for how long.
That gives you something like...
BUFFER buffer = obj->lock_buffer ();
// I'll assume exception-for-fail - why nullcheck when most
// implementations will be trivial and infallible.
while (...)
{
*buffer = whatever;
obj->Run ();
}
obj->release_buffer (buffer);
Even if you use a member pointer, the basic principle of converting to
a simple pointer before entering an inner loop still makes sense - and
one advantage if member pointers work for you is that the compiler
will probably do that optimisation for you.
But access functions are the basic workhorse for doing this kind of
thing - member pointers cause too many problems to be practical in my
experience, and offsetof is unsafe if the type of the instance is a
run-time variable.