Thread: Transmuting types at runtime

  1. #1
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396

    Transmuting types at runtime

    Just for fun:

    Code:
    #include <iostream>
    
    class Animal
    {
    public:
    	virtual ~Animal() {}
    
    	virtual void Speak() = 0;
    };
    
    class Dog : public Animal
    {
    public:
    	void Speak() { std::cout << "Woof" << std::endl; }
    };
    
    class Pig : public Animal
    {
    public:
    	void Speak() { std::cout << "Oink" << std::endl; }
    };
    
    void Piggy( Animal *animal )
    {
    	Pig pig;
    	*(unsigned int *)animal = *(unsigned int *)&pig;
    }
    
    int main()
    {
    	Dog *dog = new Dog();
    	dog->Speak();
    	Piggy( dog );
    	dog->Speak();
    	return 0;
    }
    1. How does it work?
    2. Can you think of any real applications for this?
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  2. #2
    The larch
    Join Date
    May 2006
    Posts
    3,573
    1. Undefined behaviour achieved through an improper cast? It probably gets worse if the classes have any non-trivial members and such.

    2. I've heard of design patterns that can make an object appear to change its class without relying on some dark and suspicious code, e.g the state pattern.
    Last edited by anon; 05-06-2009 at 02:15 PM.
    I might be wrong.

    Thank you, anon. You sure know how to recognize different types of trees from quite a long way away.
    Quoted more than 1000 times (I hope).

  3. #3
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by anon View Post
    1. Undefined behaviour achieved through an improper cast? It probably gets worse if the classes have any non-trivial members and such.

    2. I've heard of design patterns that can make an object appear to change its class without relying on some dark and suspicious code, e.g the state pattern.
    The example I had in mind was some kind of object with copy-on-write semantics. Suppose one object shares its state with another, but if any attempt is made to change that state, the state is copied.

    So you could have one set of virtual accessors for the shared case. If these accessors were invoked in order to change the state, they would copy the state and then twiddle the vtable to get the accessors for the unshared case.

    Obviously, this is a stupid thing to do if the objects aren't exactly the same other than the implementation of some virtual functions. And twiddling the vtable is nonportable, of course.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

  4. #4
    Registered User
    Join Date
    May 2007
    Posts
    147
    Code:
    	*(unsigned int *)animal = *(unsigned int *)&pig;
    This is disasterous C++, but I assume you know already this isn't portable, and there's no way to assume it works on any particular platform or build.



    Back in the caller, it's worse.

    If you were able to transform a pig into a dog, the compiler itself would not "believe" this, and under certain kinds of optimization techniques, would lead to very strange results.

    There are times, for example, when the compiler realizes it can bypass the normal virtual mechanism and make a call to what is declared as a virtual function without going through the vtable (it's not all that common, but there are such optimizations) - in which case you'd have very odd results.


    On the other hand, there's no decent reason to use a "Dog" object in a way that has it transformed into a "Pig" without otherwise coding from the perspective of an object that CAN be transformed (as in a more generic Animal, Mammal, or other transformable concept). In other words, it defies reason to code while "thinking" of a Dog object specifically, and have any expectation that it SHOULD operate as a pig. There IS reason to code for a Mammal that is transformable. The implementation of that mammal may not necessarily be a derived object, but a pimpl - which could itself be substituted (transformed) through otherwise standard means.

    Conversely, there are dynamic dispatch approaches that deal with the notion of "commands" to objects, rather than function calls. Objective-C is like this. In Objective-C what C++ developers think of as function calls are, instead, commands which are dispatched to objects in such a generic way that if the receiver doesn't 'understand' the command, it can simply ignore it. There are no compile time errors - it's like being able to call any function you like, whether it actually exists in a class or not, then substituting instances of any type that may or may not understand the function.

    That is to say, Objective-C supports the kind of substitution you're thinking of, and I've heard some MAC developers extol this feature as if it were a gift from heaven. Unfortunately, it's a performance hit, and the feature often leads to strange bugs that could be prevented at compile time if they only considered the obvious flaw in the paradigm (instead of thinking that flaw were some kind of benefit).

    The concept of dynamic dispatch of this sort is embodied in the notions of functors, binding (as in STL and boost), and the pimpl design pattern - even simple function pointers can implement this kind of dynamic behavior in object designs without considering the non-portable notion you're considering hear.

    In other words, it's a 'cute' idea that's been well researched in other areas (smalltalk, Objective-C, various design patterns). In the latter, design patterns in C++, implementations retain the benefits of compatibility & compile time checking.
    Last edited by JVene; 05-06-2009 at 03:28 PM.

  5. #5
    Officially An Architect brewbuck's Avatar
    Join Date
    Mar 2007
    Location
    Portland, OR
    Posts
    7,396
    Quote Originally Posted by JVene View Post
    There are times, for example, when the compiler realizes it can bypass the normal virtual mechanism and make a call to what is declared as a virtual function without going through the vtable (it's not all that common, but there are such optimizations) - in which case you'd have very odd results.
    Yes -- I tried it both ways. On the two compilers I tried, the call was made non-virtually when the object was declared as an automatic instead of allocated with new. I expected as much.

    Although I intended this to be a toy I thought it was worth thinking through the possibilities. Obviously this can be done in other ways.
    Code:
    //try
    //{
    	if (a) do { f( b); } while(1);
    	else   do { f(!b); } while(1);
    //}

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. invalid types 'int[int]' for array subscript
    By kolistivra in forum C++ Programming
    Replies: 6
    Last Post: 12-11-2010, 12:57 PM
  2. Replies: 6
    Last Post: 08-23-2008, 01:16 PM
  3. The Interactive Animation - my first released C program
    By ulillillia in forum A Brief History of Cprogramming.com
    Replies: 48
    Last Post: 05-10-2007, 02:25 AM
  4. Dikumud
    By maxorator in forum C++ Programming
    Replies: 1
    Last Post: 10-01-2005, 06:39 AM
  5. FILES in WinAPI
    By Garfield in forum Windows Programming
    Replies: 46
    Last Post: 10-02-2003, 06:51 PM