Topic: In-place casting (object morphing): legal? portable?
Author: xleobx@qmailcomq.com
Date: Thu, 31 Jan 2002 10:45:53 CST Raw View
Michiel Salters <Michiel.Salters@cmg.nl> wrote:
>> Yes, all virtual methods are eventually referenced through the VMT or its
>> analog.
> Wrong. Virtual calls may even be inlined, if the compiler can deduce the
> dynamic type of the object.
Which it cannot in the case we're discussing, so your observation is
irrelevant. Although it is a good question whether a compiler
is permitted to inline a call here:
Base * pBase = new Derived;
/* case 1: nothing in between; case 2: a call outside the translation unit */
pBase->virtual_method();
> Of course, calling placement new to
> write over an object you haven't deleted is very likely to break this
> deduction.
> The solution to this problem is very simple; if you want to have a real
> VMT which isn't affected by compiler optimalizations, build one
> yourself. You can safely change such a vtable pointer, unlike the
> builtin virtual-fucntion mechanism.
That's what I demonstrated. Some compilers do not optimize it well.
Leo
---
[ 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: xleobx@qmailcomq.com
Date: Thu, 31 Jan 2002 10:46:01 CST Raw View
Balog Pal <pasa@lib.hu> wrote:
> I hope you have a system of objects doing real job, ready to experiment.
> Would you please provide some real-life measuring comparing the cases
> where you use VMT morphing and where a regular pointer to an external
> strategy object?
I have a system of objects doint geal job, but not ready to experiment.
The current state of affairs is as follows:
There are two strategies: a fast one and a slow one. It is known upon
object construction whether there may be a need for the slow strategy for
that object in the future.
If so, a class with the slow strategy is used. The portion
of such objects, as well as the portion of "potentially slow" objects that
can still use the fast strategy, varies very much depending on the input data.
I was planning to refactor that to make sure the fast strategy is used
whenever possible without incurring any memory overhead.
> inline void Morphto(const Test & newtype)
> {
> // copy the other class' VMT poiter to ours
> *(reinterpret_cast<void**>(this)) =
> *(reinterpret_cast<void*const*>(& newtype));
> }
> Could you show actual run times for alternative solutions? I'm really curious whether it would gain considerable speed.
If I go ahead and implement it with a strategy pointer, it will or will not
work faster than today depending on the slow vs. fast object ratio: it may
be slower (because of the cache pollution and all other memory-related
effects) if there are very few slow objects; or it may be faster
(because of the fast strategy being used whenever possible),
when there are many potentially slow objects for which the fast
strategy still works.
Leo (no Q's or X's in my 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: John Nagle <nagle@animats.com>
Date: Tue, 29 Jan 2002 10:39:33 CST Raw View
What he's asking for is a union of classes.
Fortunately, unions of classes with constructors,
destructors, or copy operations are not permitted.
(Personally, I don't think that unions should
be allowed to contain pointers, either.)
If you really need to change the type of
something, use indirection. Maintain a pointer
to the object and "change type" by deleting
one object and creating another, then update
the pointer. Encapsulate this as a handle
class and you can export exactly the functionality
you want.
John Nagle
Animats
---
[ 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: xleobx@qmailcomq.com
Date: Tue, 29 Jan 2002 12:06:43 CST Raw View
Ron Natalie <ron@sensor.com> wrote:
>> My point is that it is possible to write a copy constructor in a way
>> that it _will not_ disturb any memory but the one the compiler uses
>> for its own purposes, be it vtable or something else.
> It is not so possible. You are making a leap that does not exist.
> All placement says is use the memory provided. The compiler is quite
> within it's rights to scribble all over the allocation before it hands
> it to your constructor.
Oh, well...
>> Understood. In other words, if it is not a POD, there is no relying
>> on any cross-type layout similarities, to give the implementors more
>> freedom, right?
> It's worse than that. You can't make any of the assumptions: size,
> layout, vtable behavior, etc...
>> Anyway, if this trick does not work reliably now, are there any plans to
>> have such a feature in the future? Or anything that resembles Objective C
>> is a no-no?
> You haven't made the slightest indication what this kludge would be good
> for.
As I said, for strategy switch. A desktop window is drawn in Win'98 style;
the user says "I want it in twm style". The current way to do it involves
additional strategy objects, pointers to them or to member functions,
and additional pointer chasing.
When it is not a window manager, but, e.g. a program dealing with millions
of logic gates, or pixels, or triangles, it becomes important.
Leo
---
[ 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: "Ken Alverson" <Ken@Alverson.com>
Date: Tue, 29 Jan 2002 14:24:16 CST Raw View
<xleobx@qmailcomq.com> wrote in message
news:CtB58.263$JZ6.6520@dfw-read.news.verio.net...
>
> As I said, for strategy switch. A desktop window is drawn in Win'98 style;
> the user says "I want it in twm style". The current way to do it involves
> additional strategy objects, pointers to them or to member functions,
> and additional pointer chasing.
>
> When it is not a window manager, but, e.g. a program dealing with millions
> of logic gates, or pixels, or triangles, it becomes important.
Even if you could implement this funny concept you mention, it wouldn't
necessarily be any faster than an object containing pointers to strategy
objects. Assuming your trick worked, you would have to call through at
least one pointer (the one to your object, or at least its vtable). With an
object holding pointers, you would simply be calling through the vtable
pointed by the internal pointer. If the method you call on the containing
object is non-virtual, it can be inlined by an intelligent compiler.
If you can deal with the decision being made at compile time, you can use
template strategy classes and have full inlining with no indirect calls.
Ken
---
[ 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: Ron Natalie <ron@sensor.com>
Date: Tue, 29 Jan 2002 14:33:42 CST Raw View
John Nagle wrote:
>
> (Personally, I don't think that unions should
> be allowed to contain pointers, either.)
Believe me, that has bitten me in the past. The BSD kernel
used to do "pointer conversoin by union" all over the freaking
place. It was very tedious to fix this (was poriting to a machine
that encoded the operand size IN the pointer).
> If you really need to change the type of
> something, use indirection. Maintain a pointer
> to the object and "change type" by deleting
> one object and creating another, then update
> the pointer. Encapsulate this as a handle
> class and you can export exactly the functionality
> you want.
Or if you want to maintain the same memory allocation,
carefully manage it with placement new and destructor
calls (like the standard containers do internally), but
don't make goofy assumptions about how the compiler
implements thgings.
---
[ 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: xleobx@qmailcomq.com
Date: Tue, 29 Jan 2002 14:33:52 CST Raw View
John Nagle <nagle@animats.com> wrote:
> What he's asking for is a union of classes.
A very restricted union of classes.
> Fortunately, unions of classes with constructors,
> destructors, or copy operations are not permitted.
A non-restricted union would be a nightmare, this is true.
> (Personally, I don't think that unions should
> be allowed to contain pointers, either.)
> If you really need to change the type of
> something, use indirection. Maintain a pointer
> to the object and "change type" by deleting
> one object and creating another, then update
> the pointer. Encapsulate this as a handle
> class and you can export exactly the functionality
> you want.
Yeah, right. Abstraction penalty of, at the least, O(sizeof(T)),
not counting the memory manahement overhead? No, thank you.
Leo (no Q's or X's in my 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: "Balog Pal" <pasa@lib.hu>
Date: Tue, 29 Jan 2002 18:34:52 CST Raw View
<xleobx@qmailcomq.com> wrote in message news:CtB58.263$JZ6.6520@dfw-read.news.verio.net...
> > You haven't made the slightest indication what this kludge would be good
> > for.
>
> As I said, for strategy switch.
Strategy? And why it should be implemented using the class' VMT and by changing the object's class?
For me strategy mean something referenced. So It will be either a singleton referenced explicitly, or if you need it on by-object basis you have a pointer to it as member. Then you can switch it by simply overwriting the pointer, no dirty tricks at all.
> A desktop window is drawn in Win'98 style;
> the user says "I want it in twm style".
And sure that style is implemented as part of every class, especially those havig unique layout. But it's abstracted to its own class anyway.
> The current way to do it involves
> additional strategy objects, pointers to them or to member functions,
> and additional pointer chasing.
Sure. Could you demonstrate how you gain anything by having your class-morphing over that?
> When it is not a window manager, but, e.g. a program dealing with millions
> of logic gates, or pixels, or triangles, it becomes important.
IMHO changing a WMT pointer is not less work than to change a regular one. Or you just need to save the 4 bytes of that pointer storage?
Paul
---
[ 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: xleobx@qmailcomq.com
Date: Wed, 30 Jan 2002 02:07:56 CST Raw View
Balog Pal <pasa@lib.hu> wrote:
>> > You haven't made the slightest indication what this kludge would be good
>> > for.
>> As I said, for strategy switch.
> Strategy? And why it should be implemented using the class' VMT
> and by changing the object's class?
Well, it shouldn't only because doing it that way is not portable. The
strategy methods are not second-class methods.
> For me strategy mean something referenced.
Yes, all virtual methods are eventually referenced through the VMT or its
analog.
> So It will be either a singleton referenced explicitly, or if you need
> it on by-object basis you have a pointer to it as member.
> Then you can switch it by simply overwriting the pointer,
> no dirty tricks at all.
That is what I have to do, wasting valuable data cache.
>> The current way to do it involves
>> additional strategy objects, pointers to them or to member functions,
>> and additional pointer chasing.
> Sure. Could you demonstrate how you gain anything by having
> your class-morphing over that?
In my case, the object size will be sizeof(void*) smaller - it is 8 bytes
more and more often nowadays. A call to a strategy method will involve
one less indirection: object.virtual_method() is (in the VMT model) one load
before the indirect call; while object.strategy->virtual_method() is
two loads.
>> When it is not a window manager, but, e.g. a program dealing with millions
>> of logic gates, or pixels, or triangles, it becomes important.
> IMHO changing a WMT pointer is not less work than to change a regular one.
> Or you just need to save the 4 bytes of that pointer storage?
Changing is the same work, and the 4-8 bytes of extra storage are not
a big deal, but the data cache pollution and pointer chasing is
_sometimes_ an unacceptable abstraction penalty compared to
an implementation with pointers to structs of member pointers,
forgoing the C++-provided cirtual functiona completely,
and in effect implementing [modifiable] VMTs by hand; something like:
#include <iostream>
using std::cout;
struct Animal {
struct Behavior {
void (Animal::*voice) ();
void (Animal::*happy) ();
} * beh;
void DogVoice() { cout << "Woof\n"; }
void CatVoice() { cout << "Meow\n"; }
void DogHappy() { cout << "Wagging tail\n"; }
void CatHappy() { cout << "Purr-purr\n"; }
Animal(Behavior & b) : beh(&b) { }
void Morph(Behavior & b) { beh = &b; }
};
Animal::Behavior Dog = { &Animal::DogVoice, &Animal::DogHappy };
Animal::Behavior Cat = { &Animal::CatVoice, &Animal::CatHappy };
int main() {
Animal * a = new Animal(Dog);
(a->*a->beh->happy)(); // incredible ugliness
a->Morph(Cat);
(a->*a->beh->voice)();
}
Which, BTW, gets optimized very nicely by SunPro CC, but gcc produces
crappy code.
Leo (no Q's or X's in my 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: "Ken Alverson" <Ken@Alverson.com>
Date: Wed, 30 Jan 2002 09:57:37 CST Raw View
<xleobx@qmailcomq.com> wrote in message
news:NlL58.323$JZ6.8544@dfw-read.news.verio.net...
>
> Changing is the same work, and the 4-8 bytes of extra storage are not
> a big deal, but the data cache pollution and pointer chasing is
> _sometimes_ an unacceptable abstraction penalty compared to
> an implementation with pointers to structs of member pointers,
> forgoing the C++-provided cirtual functiona completely,
> and in effect implementing [modifiable] VMTs by hand; something like:
You could placement new into some raw RAM within your object instead of heap
allocated space if you think it would help your locality...
class Outer {
char rawMemory[sizeof(BaseStrategy)];
public:
Outer() {new (rawMemory) BaseStrategy();}
BaseStrategy* get_Strategy() {return
static_cast<BaseStrategy*>(rawMemory);}
template <class Strategy>
void Morph() {
assert(sizeof(Strategy) == sizeof(BaseStrategy));
assert(static_cast<BaseStrategy>(static_cast<Strategy>(0)));
get_Strategy()->~Strategy();
new (rawMemory) Strategy();
}
};
Ken
---
[ 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: Michiel.Salters@cmg.nl (Michiel Salters)
Date: Wed, 30 Jan 2002 10:14:46 CST Raw View
xleobx@qmailcomq.com wrote in message news:<NlL58.323$JZ6.8544@dfw-read.news.verio.net>...
> Balog Pal <pasa@lib.hu> wrote:
[ SNIP Balog's question ]
> Yes, all virtual methods are eventually referenced through the VMT or its
> analog.
Wrong. Virtual calls may even be inlined, if the compiler can deduce the
dynamic type of the object. Of course, calling placement new to
write over an object you haven't deleted is very likely to break this
deduction.
The solution to this problem is very simple; if you want to have a real
VMT which isn't affected by compiler optimalizations, build one
yourself. You can safely change such a vtable pointer, unlike the
builtin virtual-fucntion mechanism.
HTH,
--
Michiel Salters
---
[ 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: "Ken Alverson" <Ken@Alverson.com>
Date: Wed, 30 Jan 2002 11:42:27 CST Raw View
"Ken Alverson" <Ken@Alverson.com> wrote in message
news:a38dae$efo$1@eeyore.INS.cwru.edu...
> assert(static_cast<BaseStrategy>(static_cast<Strategy>(0)));
Oops, typo...that should have been:
assert(!static_cast<BaseStrategy*>(static_cast<Strategy*>(0)));
It's just there to make sure the objects are compatible by making sure the
casts compile. There's probably still a missed assumption somewhere like
the BaseStrategy object possibly having a different memory offset within
Strategy (multiple inheritance?) - probably impossible with the equal size
assumption, but may be possible if the memory block was expanded to allow
different sized Strategy objects. In that case, perhaps add:
assert(
static_cast<void*>(reinterpret_cast<BaseStrategy*>(rawMemory)) ==
static_cast<void*>(static_cast<BaseStrategy*>(reinterpret_cast<Strategy*>(ra
wMemory)))
);
Or include a pointer to the location of the Strategy object within the raw
memory to be initialized during morphing.
Also, get_Strategy should use reinterpret_cast, not static_cast. (Next
time, post *after* coffee)
Ken
---
[ 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: "Balog Pal" <pasa@lib.hu>
Date: Wed, 30 Jan 2002 14:37:57 CST Raw View
<xleobx@qmailcomq.com> wrote in message news:NlL58.323$JZ6.8544@dfw-read.news.verio.net...
> In my case, the object size will be sizeof(void*) smaller - it is 8 bytes
> more and more often nowadays. A call to a strategy method will involve
> one less indirection: object.virtual_method() is (in the VMT model) one load
> before the indirect call; while object.strategy->virtual_method() is
> two loads.
Well, that sounds reasonable, but I'd guess you'll have that other redirection in the function called then. Certainly that is just speculation.
> Changing is the same work, and the 4-8 bytes of extra storage are not
> a big deal, but the data cache pollution and pointer chasing is
> _sometimes_ an unacceptable abstraction penalty compared to
> an implementation with pointers to structs of member pointers,
I hope you have a system of objects doing real job, ready to experiment. Would you please provide some real-life measuring comparing the cases where you use VMT morphing and where a regular pointer to an external strategy object?
The following code works on MSVC/intel:
- ----------------
#include <iostream>
using std::cout;
class Animal {
public:
virtual void voice() = 0;
// nasty hack -- children, do not to this at home!
// assumptations: VMT pointer is located at th very start of class (this+0) and is sized like a void*
inline void Morphto(const Animal & newtype)
{
// copy the other class' VMT poiter to ours
*(reinterpret_cast<void**>(this)) = *(reinterpret_cast<void*const*>(& newtype));
}
};
class Dog : public Animal {
public:
void voice() { cout << "Woof\n"; }
};
class Cat : public Animal {
public:
void voice() { cout << "Meow\n"; }
};
main() {
Dog dog;
cout << "orig dog: ";
dog.voice();
Cat cat;
cout << "orig cat: ";
cat.voice();
{
Dog a;
Animal & animal = a;
cout << "animal (dog): ";
animal.voice();
animal.Morphto(cat);
cout << "animal (morphed cat): ";
animal.voice();
animal.Morphto(dog);
cout << "animal (morphed dog): ";
animal.voice();
}
return 0;
}
- ---------------- output:
orig dog: Woof
orig cat: Meow
animal (dog): Woof
animal (morphed cat): Meow
animal (morphed dog): Woof
- ----------------
The hack function being isolated you can keep the code 'practically portable' to systems using VMT pointers, just adjust the function to poke the correct location. Certainly the user is responsible to use it only with VMT-compatible objects. (And as long as all virtual functions 'behave' even your size restrictions are lifted.)
Could you show actual run times for alternative solutions? I'm really curious whether it would gain considerable speed.
Paul
---
[ 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: "Balog Pal" <pasa@lib.hu>
Date: Wed, 30 Jan 2002 17:20:18 CST Raw View
"Michiel Salters" <Michiel.Salters@cmg.nl> wrote in message news:cefd6cde.0201300601.4ed5fa85@posting.google.com...
> > Balog Pal <pasa@lib.hu> wrote:
> > Yes, all virtual methods are eventually referenced through the VMT or its
> > analog.
For the record, xleobx@qmailcomq.com wrote that.
> Wrong. Virtual calls may even be inlined, if the compiler can deduce the
> dynamic type of the object.
Yes, but that is not that usual. I'm positive that the calls in example under discussion will really go through VMT, as they are certainly called through a pointer/ref to a (abstract) base class.
> Of course, calling placement new to
> write over an object you haven't deleted is very likely to break this
> deduction.
:)
Paul
======================================= MODERATOR'S COMMENT:
Please wrap lines at <78 characters.
---
[ 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: xleobx@qmailcomq.com
Date: Fri, 25 Jan 2002 22:27:36 GMT Raw View
Consider this (requires a recent compiler, like g++ 3.0.2):
#include <iostream>
using std::cout;
class Animal {
public:
virtual void voice() = 0;
template<class T> void magic_cast() {
new ((void*)this) T(*static_cast<T*>(this));
}
};
#define domestic public // could not resist :-)
class Dog : domestic Animal {
void voice() { cout << "Woof\n"; }
};
class Cat : domestic Animal {
void voice() { cout << "Meow\n"; }
};
main() {
Animal * a = new Dog;
a->voice();
a->magic_cast<Cat>();
a->voice();
a->magic_cast<Dog>();
a->voice();
}
It does print "Woof\nMeow\nWoof\n" (with g++ 3.0.2, that is).
>From my perspective, this conversion is no more dangerous than
a static_cast: if misapplied, both may lead to disastrous results.
The necessary changes to copy constructors, if they are non-trivial,
are straightforward: every C::C(const C& src) will have to do
if (this == &src) return;
The questions are:
1. Is the compiler required to set up the vtable explicitly even in case
of a copy-constructor?
2. Is the compiler allowed to cache vtable pointers?
3. Is the compiler allowed to optimize out copy constructors altogether
when source and target are the same?
If the answers are "yes, no, no", then this technique, being portable,
may turn out to be quite useful.
If the answers are of any other 7 combinations, then an explanation of
the rationale for the mismatching answers is in order.
Thank you for your attention,
Leo (no Q's, no X's in my 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: Ron Natalie <ron@sensor.com>
Date: Sat, 26 Jan 2002 01:39:29 GMT Raw View
xleobx@qmailcomq.com wrote:
>
> >From my perspective, this conversion is no more dangerous than
> a static_cast: if misapplied, both may lead to disastrous results.
This is most likely to be disasterous than static_cast. First off, it's
not a cast at all, despite what you call it, it's a changes the object
it's being applied to which NO CAST OPERATOR DOES.
Second, it assumes that all the operands are the same size or else
it potentially writes off the end of the allocation of the casted
from object with the construction of the new object.
It assumes that a very trival vptr construction which proabably isn't
even going to hold true even on the one compiler you tried if you had
a more involved inheritance hierarchy.
>
> 1. Is the compiler required to set up the vtable explicitly even in case
> of a copy-constructor?
The ccompiler isn't required to even have vtables. Making any assumption
about how they work is certainly non-portable.
> 2. Is the compiler allowed to cache vtable pointers?
It's allowed to do whatever it pleases with them as long as the virtual
functions work as they are specified in the standard.
> If the answers are "yes, no, no", then this technique, being portable,
> may turn out to be quite useful.
It's incredibly non-portable and unsafe even in the implementation you
tried it in.
---
[ 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: xleobx@qmailcomq.com
Date: Sat, 26 Jan 2002 07:01:28 GMT Raw View
Ron Natalie <ron@sensor.com> wrote:
>> >From my perspective, this conversion is no more dangerous than
>> a static_cast: if misapplied, both may lead to disastrous results.
> This is most likely to be disasterous than static_cast. First off, it's
> not a cast at all, despite what you call it, it's a changes the object
> it's being applied to which NO CAST OPERATOR DOES.
Ok, bad naming. Let it be a morph operator.
> Second, it assumes that all the operands are the same size or else
Naturally (type_info::size(), if existed, would have helped to check it).
The same way as static_cast<> assumes that the programmer
knows what is being done. Layout equivalence of all constituent classes
may even be required for that operator to have defined behavior.
> it potentially writes off the end of the allocation of the casted
> from object with the construction of the new object.
My point is that it is possible to write a copy constructor in a way
that it _will not_ disturb any memory but the one the compiler uses
for its own purposes, be it vtable or something else.
> It assumes that a very trival vptr construction which proabably isn't
> even going to hold true even on the one compiler you tried if you had
> a more involved inheritance hierarchy.
Well, if only the most derived object (of the "interface" kind")
differs (and I consider it the preferred usage model [strategy switch],
instead of gang reassignment of pointers to member functions or something
similar), the construction of all underlying objects is legal by 3.8/7,
and for the outermost interface-like object it is trivial enough.
>> 1. Is the compiler required to set up the vtable explicitly even in case
>> of a copy-constructor?
> The ccompiler isn't required to even have vtables. Making any assumption
> about how they work is certainly non-portable.
>> 2. Is the compiler allowed to cache vtable pointers?
> It's allowed to do whatever it pleases with them as long as the virtual
> functions work as they are specified in the standard.
>
>> If the answers are "yes, no, no", then this technique, being portable,
>> may turn out to be quite useful.
> It's incredibly non-portable and unsafe even in the implementation you
> tried it in.
Understood. In other words, if it is not a POD, there is no relying
on any cross-type layout similarities, to give the implementors more
freedom, right?
Anyway, if this trick does not work reliably now, are there any plans to
have such a feature in the future? Or anything that resembles Objective C
is a no-no?
Thanks,
Leo (no Q's or X's in my 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: "Balog Pal" <pasa@lib.hu>
Date: Sat, 26 Jan 2002 22:39:29 GMT Raw View
<xleobx@qmailcomq.com> wrote in message news:GUk48.246$A%3.4026@ord-read.news.verio.net...
> class Animal {
> public:
> virtual void voice() = 0;
> template<class T> void magic_cast() {
> new ((void*)this) T(*static_cast<T*>(this));
> }
> };
I hope ths is a joke...
> main() {
> Animal * a = new Dog;
> a->voice();
> a->magic_cast<Cat>();
> a->voice();
> a->magic_cast<Dog>();
> a->voice();
> }
No, you mean it.
> It does print "Woof\nMeow\nWoof\n" (with g++ 3.0.2, that is).
Try to add a single string member to your base class, and it will most likely explode.
Add any member to one of your derived classes, and it will launch nukes.
> >From my perspective, this conversion is no more dangerous than
> a static_cast: if misapplied, both may lead to disastrous results.
What conversion?
What you do is called 'trampling over' an innocent object. It will call a ctor on memory which is not raw, but has an already constructed object there.
The only reason problems does not surface that your objects in the example are empty, having nothing no content at all. So all the compiler is changing is the VMT pointer (your compiler uses it).
>
> The necessary changes to copy constructors, if they are non-trivial,
> are straightforward: every C::C(const C& src) will have to do
>
> if (this == &src) return;
Really? And where you put that? Having a string member s in your base class, how you prevent a ctor call of s?
Paul
---
[ 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: xleobx@qmailcomq.com
Date: Sun, 27 Jan 2002 19:13:16 GMT Raw View
Balog Pal <pasa@lib.hu> wrote:
>> main() {
>> Animal * a = new Dog;
>> a->voice();
>> a->magic_cast<Cat>();
>> a->voice();
>> a->magic_cast<Dog>();
>> a->voice();
>> }
> No, you mean it.
>> It does print "Woof\nMeow\nWoof\n" (with g++ 3.0.2, that is).
> Try to add a single string member to your base class, and it will most likely explode.
I dod not say that it will work with _all_ classes. To make it work,
one needs to have complete control over all the constituent classes
to make sure that copy ctors are either default or have the
"copy to itself" check.
> Add any member to one of your derived classes, and it will launch nukes.
Duh. In my response to Ron Natalie I specified that the intended usage
is to switch interfaces/strategies of a class.
> What conversion?
> What you do is called 'trampling over' an innocent object.
> It will call a ctor on memory which is not raw, but has an already
> constructed object there.
And that (copy) ctor is either a trivial one or is specifically constructed
not to trample over any user data.
> The only reason problems does not surface that your objects in the example are empty, having nothing no content at all. So all the compiler is changing is the VMT pointer (your compiler uses it).
And this is _exactly_ what I want the compiler to do, and nothing else,
whichever method the compiler uses to assosicate typeids with memory
locations. I say: "hey, change the typeid of memory area pointed by that
pointer, and trust me, the storage size and layout are the same".
The only "legal" obstacle is that the standard does not guarantee for
class B { /* some data, some virtual methods */ };
class D1 : public B { /* overriding some virtual methods one way */ };
class D2 : public B { /* overridding some virtual methods another way */ }
that sizeof(D1) == sizeof(D2) and that, roughly speaking,
offsetof(D1, B) == offsetof(D2, B). If this is given (note that
sizeof(B) == sizeof(D1), or that offsetoff(D1, B) == 0 is not required),
then the compiler will be unable to trample on user data while changing
the VMT pointer, if it is used. If it is not, it's even better.
>> The necessary changes to copy constructors, if they are non-trivial,
>> are straightforward: every C::C(const C& src) will have to do
>>
>> if (this == &src) return;
> Really? And where you put that? Having a string member s in your base class, how you prevent a ctor call of s?
Again, I am not saying this will work for a class that you have no control
over.
My question, effectively, is: if two classes are declared as
"identical twins" (i.e. their declarations,
including the declarations of their class data members and base classes,
recursively, are, after typedef expansion, identical, give or take some
1-to-1 name mapping), does the standard guarantee that their layout
is identical? If yes, it will mean that what I'm doing is no worse
than 3.8/7. And if not, why not? It does not tie implementor's hands
in any way, IMHO.
Leo (no Q's or X's in my 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: Ron Natalie <ron@sensor.com>
Date: Mon, 28 Jan 2002 18:27:07 GMT Raw View
xleobx@qmailcomq.com wrote:
>
> My point is that it is possible to write a copy constructor in a way
> that it _will not_ disturb any memory but the one the compiler uses
> for its own purposes, be it vtable or something else.
It is not so possible. You are making a leap that does not exist.
All placement says is use the memory provided. The compiler is quite
within it's rights to scribble all over the allocation before it hands
it to your constructor.
> Understood. In other words, if it is not a POD, there is no relying
> on any cross-type layout similarities, to give the implementors more
> freedom, right?
It's worse than that. You can't make any of the assumptions: size,
layout, vtable behavior, etc...
> Anyway, if this trick does not work reliably now, are there any plans to
> have such a feature in the future? Or anything that resembles Objective C
> is a no-no?
You haven't made the slightest indication what this kludge would be good
for.
---
[ 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 ]