?
Solved

C++ OO trick

Posted on 2003-03-29
40
Medium Priority
?
1,528 Views
Last Modified: 2007-12-19
I'm using Visual C++ 6.0 with RTTI on an 80x86 processor.

I want to create an Object class that is an ancestor of all other classes in my framework.
In my object class i want to create a method Clone() that returns a new instance with the
same data as the one clone is called from. I also want all other derived classes to inherit
the clone function, and thus be able to clone themselves without having to modify the
original clone function.

I have already done something simillar with a method named IsKindOf()

Something like this:

class CObject {

public:
  ...
  CObject* Clone();
  ...
}

CObject::Clone() {
  // Some inline assembly, perhaps making use of RTTI,
  // or more likely the way objects are laid out in memory
 
// Should be able to create a clone of any instance decending from this class
};

class CSomeClass : public CObject {
};


CObject* pObjectA = new CSomeClass();
CObject* pObjectB = pObjectA->Clone();


pObjectB should now be a pointer to an instance of CSomeClass.

Any ideas on how to solve this?


TIP:
Maybe it is possible to copy the vtable pointers and the data, and
thus create a simillar structure somewhere else in memory, and then
return a pointer to this new structure?
0
Comment
Question by:lar_jens
[X]
Welcome to Experts Exchange

Add your voice to the tech community where 5M+ people just like you are talking about what matters.

  • Help others & share knowledge
  • Earn cash & points
  • Learn & ask questions
  • 16
  • 12
  • 6
  • +2
40 Comments
 
LVL 1

Author Comment

by:lar_jens
ID: 8232027
I think I remember something about an article in MSDN called C++ under the hood, regarding the way objects are laid out in memory. Anybody know where to find it?
0
 
LVL 8

Expert Comment

by:fl0yd
ID: 8232571
This sounds so easy, but it's really stretching the boundaries of my knowledge. Copying the datastructure and vtable sounds like a hack to me and is rather vulnerable if compiled with different compilers. The best bet I could come up with after about half an hour of research was the placement new operator. Although I'm rather sure that it won't work, you may want to give it a try. A sketchy snippet of the CSomeClass::Clone() method will illustrate what I'm thinking of here:

CSomeClass* CSomeClass::Clone() {
    CSomeClass* pRet = new( CObject::Clone() ) CSomeClass;
    // additional cloning of objects specific to CSomeClass goes here
    return( pRet );
}

I have to admit that I'm not 100% sure about the workings of the placement new operator. As far as I know it doesn't allocate any memory but rather constructs an object at a given address, which would in turn cause memory to get corrupted once you try to access data that is not present in the CObject base class. Anyway, it may be worth a try.

.f
0
 
LVL 1

Author Comment

by:lar_jens
ID: 8236809
Well, U see, the point here is that you are not supposed to do anything in the derived class. Just inherit the functionality.

Anyway, the largest parts of the PC market is using Wintel today.

It is no problem to override the functinality in the CObject class like this:

CObject* CSomeClass::Clone() {
  return new CSomeClass(*this);
}

CSomeClass& CSomeClass::operator = ( const CSomeClass& other ) {
  ( (CObject&)(*this) ) =  (CObject&) other;
  // Specific assignment for CSomeClass
}

0
Free Tool: SSL Checker

Scans your site and returns information about your SSL implementation and certificate. Helpful for debugging and validating your SSL configuration.

One of a set of tools we are providing to everyone as a way of saying thank you for being a part of the community.

 
LVL 8

Expert Comment

by:fl0yd
ID: 8236890
I doubt that your design decisions are ok, if you want to implement a function in a base class that will have to act differently in derived classes, but shouldn't be reimplemented there. I would suggest that you get the book "Design Patterns" and see if you are still on track with your design ideas.

.f
0
 
LVL 9

Expert Comment

by:BeyondWu
ID: 8236963
If you can add some paramters for the clone function, you can try this one.
CObject* CObject::Clone(int nSize)
{
     CObject* pNew = (CObject*)new char[nSize];
     memcpy(pNew, this, nSize);
     return pNew;
}
//...
CObject* pObjectA = new CSomeClass();
CObject* pObjectB = pObjectA->Clone(sizeof(CSomeClass));
//...
But I'd like to override all virtual "Clone" function in each sub-class of CObject.

0
 
LVL 1

Author Comment

by:lar_jens
ID: 8237040
Why are you all worried about my design decisions? I'm doing this for fun, not for profit!
0
 
LVL 1

Author Comment

by:lar_jens
ID: 8237061
To BeyondWu:

>> CObject* pObjectB = pObjectA->Clone(sizeof(CSomeClass));

Well, that won't work for me. I do not know in advance what kind of class pObjectA refers to.

Consider this function:

CObject* SomeFunction( const CObject* pObject ) {
 CObject* pLocalObject = pObject->Clone();
 printf ( "%s\n", pLocalObject->GetName() );
 return pLocalObject;
}

0
 
LVL 8

Expert Comment

by:fl0yd
ID: 8237077
First of all, it isn't 'all of us', but only me. So I would assume that you have had a similar responses somewhere else. Second, fun turns into agony if you make a wrong decision in an early design stage. I mean, what is the fun if all you do is learn bad habits?

Last but not least, I for one don't make a difference between commercial and fun projects -- they are all the same quality, no matter whether I get paid for it or do it as an educational exercise. One never knows if a fun project isn't going to become a commercial application in the future.

Anyway, it's your decision.

.f
0
 
LVL 9

Expert Comment

by:BeyondWu
ID: 8237100
I'd like to use virtual function for it.
If you really want to do this, I think the problem is we can't know the size of the object.
would you like to add a datamember m_nSize in the base class, and then rewrite all the construtor of all the classes.
class CObject
{
        int m_nSize;
public:
     CObject()
     {
          m_nSize = sizeof(CObject);
     };

     CObject* Clone()
        {
          CObject* pNew = (CObject*)new char[m_nSize];
          memcpy(pNew, this, m_nSize);
          return pNew;
        };
};
0
 
LVL 8

Expert Comment

by:fl0yd
ID: 8237169
What you want is impossible. You cannot have a baseclass take care of all possibilities that may arise without changing derived classes. Even if you get it to work for a subset of possibilities using RTTI, you will shoot yourself in the foot as soon as you use multiple inheritance. Plain and simple: it just doesn't work and appears to be a design issues rather than lack of implementational skills.

But since my feedback seems to be unwanted, I'm out now.

.f
0
 
LVL 1

Author Comment

by:lar_jens
ID: 8237748

How can the Visual C++ debuger know what to display in the watch window, if it doesn't know anything about the class it wants to display? The information the debuger displays must come from somewhere.

What I mean is this :

You know the size of the object if you use the heap functions, because the delete operator knows how much memory to release using these functions.

It is also possible to find out how classes are laid out in memory. Ask yourself how you would implement a C++ compiler for a yet unknown chip. If you know where the _vfptr is for that kind of class, and are able to copy the data, then you can create a dynamic clone() function in the object class.

You can create an IsKindOf() function in the object class that no derived class needs to implement using RTTI and a bit of assembly to override the parameters passed to the dynamic_cast<> operator, I've done this already ( actually it was just 8-10 lines of assembly code - easy to port ).

This, ofcourse, only works with the Visual C++ compiler on an Intel 80x86 processor (or AMD). But if you look at the market today, most people using a computer is using Microsoft Windows with an Intel/AMD processor. So if my fun project is going somewhere commercial then a lot of the PC market is ready for the taking :).

Nevertheless, I'm using the insight I hope to gain from this, to learn more about the underlying concept of programming OO from assembly (this is as you may have noticed an assembly group)

So what I am looking for is someone who knows something about the results generated from the Visual C++ compiler when programming with classes and objects.

PS : Multiple inheritance is not an issue. I won't be using it.!

To flOyd:
You'r feedback is wanted. It truly is. I'm just trying to elaborate on my question, so that all you guys in here knows what I'm asking for.

:):):)

0
 
LVL 1

Author Comment

by:lar_jens
ID: 8237776
The basic concept of a class and it's instance is like this:

You have a set of functions somewhere in memory. All of these functions take a 'this' pointer as a parameters (passed on the stack). The 'this' pointer, points to a memory structure that holds data for a given instance of a class. This memory structure also holds pointers to the functions that will alter the data.


If you define a member function like this :
void CObject::MemberFoo(int i, double d);

In reality it becomes something like this:
void CObject::MemberFoo(CObject* this, int i, double d);

-LJJ
0
 
LVL 8

Expert Comment

by:fl0yd
ID: 8238509
lar_jens: "How can the Visual C++ debuger know what to display in the watch window, if it doesn't know anything about the class it wants to display?"

What sort of speculation is that? Don't jump to assumptions. For one thing, you have very likely compiled a lot of debug information into the executable being debugged, haven't you? Plus you have a program database sitting around the harddrive somewhere aiding the debugging process as well. With all this being helpful to the debugger, there is one thing you are missing: The debugger comes into play _AFTER_ _EVERYTHING_ has been compiled and linked, thus all the information you may ever need in the watch window is there. When writing your code in the base class, you may not even have though about a derived class yet. That is a big difference as far as information is concerned.

Another notice: The produced code isn't dependant on the cpu you are running but rather by the compiler/linker combination you are using. Compile the same code using gcc for example and you won't be able to link to and .obj file created with that. C++ name mangling isn't standardized at all, hence the need for a binary compatibility layer, COM being the most wide spread one.

So whatever you are trying to do, it will always be compiler/linker dependant. You don't even have a guarantee that a compiler's output will be consistent across versions, e.g. if you compiled source with msvc 6.0 you may not be able to link it with the linker provided with msvc.net.

So my suggestion remains unaltered: Check if your design decisions aren't getting in your way. It sure sounds like it. Whenever you have to jump to features that aren't part of the language's standard things are likely to give you a headache further down the road. Even if you are doing it 'just for fun'.

As for your last comment: That was the intermediate output of early c++ [pre]compilers that generated c-code to be fed into a c-compiler. On msvc 6.0 the this-pointer isn't part of the parameter list, i.e. it is not passed across the stack but rather in a register, esi, if I'm not totally mistaken.

.f
0
 
LVL 1

Author Comment

by:lar_jens
ID: 8238844
To floyd:
Thanks for the info on esi, or that the this pointer is passed in a register :) I will take a look at that. :)

To group:
But, even if I am stuck with VC++ 6.0. I've used that compiler for the last 3-4 years.. OK, so I have to convert some day, but for now.. I'm just learning :)

As an example the IsKindOf() function has saved me alot of work. :) And I thought that I could write a couple of functions like, Clone(), IsKindOf() and IsOfType() in the CObject class, and if I wanted to port my framework to another compiler/architecture, the only place to port would be in the CObject class.. :):) And new classes would not need to do anything, just surf on existing functionality :)


0
 
LVL 8

Expert Comment

by:fl0yd
ID: 8239094
Right, put IsKindOf() in your CObject class and modify it each and every time you create a derived class? In what way are you hoping to get a superior system this way instead of overriding a virtual Clone()-function? This approach just doesn't make any sense, since it isn't your base class that would know how to clone a derived class but the derived class itself is the only one that would.

On the IsKindOf(): If you have used it extensively I would take a far guess and say that your class hierarchy and design is bad. I believe I have already stated above that it's use is limited and most of the times I've seen it in action was when the design of the class wasn't well thought out in advance and this hackish approach stepped in. There are several issues with RTTI, too, making it a major pitfall. The wrong order in your switch statements is all that is needed to screw up your entire code. You may want to get your hands on A. Alexandrescu's 'Modern C++ Design' and expand your knowledge on what's possible with 'legal' c++ features that few have ever thought about. It did surprise me to say the least.

.f
0
 
LVL 9

Assisted Solution

by:BeyondWu
BeyondWu earned 132 total points
ID: 8243336
Try this one.
There is a limitation for debug version, the clone must is a virtual function of the base class, I think it only available on VC (6.0 no patch)

virtual CObject* Clone();

CObject* CObject::Clone()
{
     DWORD nSize;
     void* pThis;
     _asm int 3;
#ifdef _DEBUG
     _asm mov eax, this
     _asm mov pThis, eax
     _asm sub eax, 4*4
     _asm mov eax, [eax]
     _asm mov nSize, eax
#else
     _asm mov pThis, esi
     _asm mov eax, esi
     _asm sub eax, 4
     _asm mov eax, [eax]
     _asm sub eax, 9
     _asm mov nSize, eax
#endif
     CObject* pNew = (CObject*)new char[nSize];
     memcpy(pNew, pThis, nSize);
     return pNew;
}
0
 
LVL 1

Author Comment

by:lar_jens
ID: 8243730
To floyd:

I think that you are missing the point here.. I put IsKindOf() in the CObject class, and then I do not have to do ANYTHING AT ALL when I create a derived class. It just works!! :):):)
0
 
LVL 1

Author Comment

by:lar_jens
ID: 8243757
To BeyondWu:
Thanks.. I'll get back to you when I have tested the code :)

To Floyd:
I have not created a single class in my framework yet, so it could not possibly be called a hack. A couple of questions for you:

Why does some people spend a great deal of time making scrolltexts and weird effects with bitmaps in assembly?

How come some people put a lot of effort into creating a program that can change the image in the boot-up screen in their bios?

Why was the first posting in here a question about who could create an assembly program with just ASCII characters in notepad, store it as a .COM file and be able to run it?
0
 
LVL 8

Expert Comment

by:fl0yd
ID: 8244253
Why don't you make it a legal implementation using templates?

template<class T>
class CObject {
    T* Clone() { /* ... */ }
    // ...
};

class CDerivedClass : CObject<CDerivedClass> {
    // don't have to do anything in here to get Clone to work
};

Although I doubt it will compile with msvc 6.0 it is legal c++ and a clean way to achieve what asm hacks can only hope for solving in a limited field of scenarios. Sure, this templated version does have deficiencies as well, for one thing you cannot have static data in your base class. Well you can but it will not be shared across instantions for different template arguments.

To answer your questions:
* Why do people make funky effects using asm? I don't know, performance could be one reason although most of the times proper use of a high level language is sufficient. I have come across very few cases when asm did make sense. In particular I only really felt the need when I had to access cpu specific functionality which wasn't accessible through language features: clock-cycle-accurate timers/MMX/SSE/SSE2/3DNow!
* How come ... boot-up screen in their bios? Beats me, don't have a clue, really, I couldn't care less about images that I would come across about once or twice a month
* The notepad thingie: Challenge. Guess that's a good reason for just about anything. Just for the record, the smalles such app I have seen must be farbrausch's Five-In-Your-Face.

Anyway... I hope the templated version will suffice for what you are trying to attempt, although I'm somewhat saddened that you are more in search for something that absolutely has to be done in assembly, rather than getting a valid solution to your problem.

.f
0
 
LVL 8

Expert Comment

by:fl0yd
ID: 8244762
Small correction: The derived class should be defined like this:

class CDerivedClass : public class CObject<CDerivedClass> {

Sorry for the confusion.

.f
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 8353332
An interesting discussion!  Stand back from the ultimate goal and take a look:

An IsKindOf() function and a Clone() function would be SPECTACULARLY USEFUL.   Every foundation class that you could buy would offer it.  You could do a deep copy.  You could DO a DEEP COPY without manual labor!

So..... why does MFC not have it built into CObject?  Or OWL? or why isn't there an STL base object that does it?

I have to say... because it is not possible in the C++ programming language.

As I understand it, Java has such a feature.  So how does Java do it?  I'd guess that it is part of the language.  The Java compiler must carry around everything that needs to be known about the object and all of its ancestors.  It would need to know the class name and its size.  It would have to handcuff programmers by requiring only references and never pointers.  You understand that void* would be a dissallowed data type... right?  Pointer coersion would be expressly forbidden... and so forth.

Just some food fer thot.

<Dan/>
0
 
LVL 1

Author Comment

by:lar_jens
ID: 8368538
To DanRollins,

The C++ has, however, the dynamic_cast<> operator, which works much like the IsKindOf functions. But you must link with RTTI to make use of it.

:)

-LJJ
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 8369446
So there you have it.   The very fact that a separate CPU-soaking process must be used illustrates that it is not possible using normal C++ techniques.   As I understand it, RTTI stores data in the vtable which must be carried around and processed constantly.  Even simple copy constructors become bulky, slow affairs.  A reasonable guess why MFC does not use RTTI (and instead has the DECLARE_DYNAMIC and IMPLEMENT_DYNAMIC thing):  RTTI causes massivie ineffiency all over the place and made MFC seem slow.

You must use C++ with RTTI, or switch to Visual Basic, or Java, or some other less-than-totally efficient language if you want this capability.  I'm sure that it is built into C# and .NET along with the garbage collection etc.

<Dan/>
0
 
LVL 8

Expert Comment

by:fl0yd
ID: 8369855
Hmmm, sorry, but this doesn't seem to be correct, Dan. RTTI is part of C++ and as such it is a 'normal C++ technique', unless you were talking about something else. RTTI is CPU-soaking? Not that I've ever heard of. It doesn't play a role in object construction nor is it data that has to be stored on a per-object basis. The objects merely have _one_ additional pointer in their v-table or somewhere else, an index into the per-type typeinformation database.

Btw, you can implement an IsKindOf()-function in c++ that retrieves information at compile-time. As stated in one of my previous posts, A. Alexandrescu has some information on this in his "Modern C++ Design".

.f
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 8370116
I was going on rumor -- I've often heard that turning on RTTI causes a performance hit, but I've never actually tried it myself.
-- Dan
0
 

Assisted Solution

by:ssnet
ssnet earned 132 total points
ID: 8921934
it's a noble goal, but fl0yd is right -- though i don't think he has rightfully stated why... the concept of a clone() method is fairly common, but making a SINGLE implimentation for a base class is, at first sight, *against the grain* of OOP as seen in C++, and upon closer inspection, somewhat disturbing in this context (assuming you take oop seriously).

the reason it's against the grain, is, as has been stated, because derivations of base classes are presumed to exist entirly for the purpose of specializing a case of the more general... in this situation, the base clase serves as an interface, which the derived class simply agrees to comply with, one way or another -- by providing it's own implimentation, or accepting the one that is inherited. the derived class may be different than the base class, so how then can the base class know how to clone()? the functionality you want (a single implimentation in the base class), then, could only be acquired by overriding ANOTHER virtual function, which is CALLED from the base class: which, as you can see, would be quite stupid... true, the actual implimentation of clone() in the base class needn't change, but then it needn't exist either... this is obviously not what you mean.

if you want a single implimentation of a clone() in a base class to work in a direved class, then derived classes must clearly MEAN (symantically) no more or less than the base class... that is to say, the internal data members must be identical, and be used in the same way... this is probably not what you would like either, but at least it's something... i quess... but you really can't use it with objects, since everything must be public/global, because private data may or may not exists -- and how then can one copy something that may or may not exist, and if it does, could mean anything? one seeming solution would be to disregard data hiding alltogether, and step out of c++...

but wait... what are we leaving behind by breaking the c++ data hiding provided by private/protected/public? YES it is possible to just copy a class, and all it's data, and that's fine, if you live in a global-var world, where everyone trusts everyone (which includes yourself -- note that data-hiding is as much to protect the programmer from his own idiocy than that of anyone else... the way you code may change from month to month, but atleast interfaces are explicit, and you won't break the rest of your code by changing the *implimentation* but not the interface (ie VIRTUAL functions cannot be avoided))... if you want to break this, you are no longer in c++... for the moment, we'll assume you don't care, because it doesn't matter in the end anyway (though it should be a clue that what you want may not be possible)...

indeed, the approach alluded to by lar -- access the vtable, copy data, etc -- WOULD work... but it would only work for a limited set of cases (perhaps as limited as the last case, where derived classes don't add/del data members, or alter their symantics)... firstly, internal members would be limited to some predetermined configuration/meaning (at minimum, an organizational protocol, so the copier can tell when he's done -- this is, for the most part, provided by the vtable... or is it?), and additionally it would be limited to *values*, and not *pointers* (which is a real pain in the arse, since that's half the fun of c/c++), because a static copy would mean a new pointer, to data that might be released without notification (ellaborate reference counting could be implimented but that would require more virtual function calls, and the permiation of ALL your code with such calls -- or encapsulation objects)... this is clearly not the case (fwiw, copy constructors give you a clean oppourtunity to initialize a new instance with derived data... pretty much the same functionality as a virtual clone() would.... but you don't seem to want a virtual clone()...)

even if it is just for a learning experience, or a just-for-fun reason, it is not actually do-able (what the!?)... why? because you are trying to apply the process of *organizing* and the process of *defining *in one step -- and that's simply not possible (stay with me here...)... you see, you can make a function act as an interface (be virtual)  or an implimentation (be static.. stick with me... i'll justify this), but not both at the same time.... just try to apply the keywords "static" and "virtual" to the same function... there's no meaning in the combination... to say you want virtuallity (=applicability... ie that a function applies to any inheriting object) completely contradicts the intention of "staticness" (that a function does not apply to any object)...

you may be doing a double-take right now, and saying "hold on spaz... i didn't say squat about "static'... static means such a function does NOT take an implicit 'this' parameter", but since my super clone() function WOULD, it ain't static... but in reality, that's exactly what you are saying by saying you want to NOT have to re-impliment it in latter classes... you are implicitly dropping the this param, or at least the type information of it which grows as it is inherited, and which contains a reference to the object being worked on (which must be of some min-type (the base clase... which you desire to impliment your clone() method in))... since all you know with the this param, is that the object is OF your min class, that's like wanting to define a regular function, in global scope, to take a void *, and wanting that function to copy everything "in" the "object" pointed to by the void *... you CANNOT know where the object's data ends, or if it's organized contiguously, or, really, even if void * is the memory address of the BEGINNING of the data (it's really inconsiquentiall that we know about the structure ofthe vtable, because it doesn't help us with *meaning*, which is only given via the inheritance tree). having a pointer to a class, without any knowledge of the class, literally means you can do NOTHING with the class with certainty -- thus, you want to impliment a clairvoyant function... which would rock if it were possible, but is utterly devoid of meaning.

so, the short answer is: it's not possible.
the long answer is: it's not possible, BECAUSE it's not possible

i think of greater interest is this iskindof() function, which you claim to be using with success, but give no detailed information of -- not even parameters.... have you found a reflection gem i'm missing out on? :P
0
 
LVL 1

Author Comment

by:lar_jens
ID: 9034189
To ssnet :

I have generated the isKindOf function like this (VC++ 6.0) :

// Function that compares the type of this instance with
// type information for a class
BOOL SDCObject::isKindOf (const type_info& other_type) const {

      // Declare a variable to hold the wanted result
      DWORD result = 0;

      // Declare a variable that will hold the
      // type information for this instance
      const type_info& this_type = typeid(*this);

      // Use inline assembly to call the dynamic_cast<> operator
      // given the type information we have collected.

      // We want to dynamically cast from the this_type
      // to the other_type (the variable to this function)

      // The rest of the code will be something like :
      // return ( dynamic_cast<other_type> (this) ) ? TRUE : FALSE;
      // in plain C++, but that is impossible without assembly

      __asm {
            ; Push variables on the stack
            push 0                                    ; (Not known)
            push other_type                        ; Cast to the other_type type
            push this_type                        ; Cast from the this_type type
            push 0                                    ; (Not known)
            push this                              ; Pointer to the instance we want to cast
            call __RTDynamicCast            ; Call the dynamic_cast<> operator
            add esp,014h                        ; Adjust the stack to the position before we pushed
            mov result, eax                        ; move the result into a variable
      }

      // Now we can find out if the casting was successful.
      // Return the result to the caller.
      return (result) ? TRUE : FALSE;
}
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 9034596
That's really cool code.... except for two things:  

1) It causes and an unhandled exception upon call __RTDynamicCast

2) Why drop down to ASM?  If you can find documentation on the RTDynamicCast library function, then why not not just use standard C syntax to call it?

-- Dan
0
 
LVL 1

Author Comment

by:lar_jens
ID: 9034647
To DanRollins..

This code works on my PC, and a friend of mine have tested it successfully on several PC's..

The reason I am using ASM, is because the following can not be done in C++ :
  return ( dynamic_cast<other_type> (this) ) ? TRUE : FALSE;

The dynamic cast operator can only be used with types available at compile time,
the assembly code solved this problem, so that I was able to use it at run time..

Also, the RTDynamicCast function is not a library function, it is the dynamic_cast operator
used from C++. The assembly code I have generated is very simmiliar to the assembly
code generated by the compiler, except that I use some extra code to get the type_info
of the this instance.

I use it something like this :

  if ( pObject->isKindOf( typeid (CButton) )
  {
    .. Handle button event
  }

PS : You have to turn on RTTI ( but I guess you already knew that ).
0
 
LVL 8

Expert Comment

by:fl0yd
ID: 9034689
lar_jens:

I'm not sure who told you that dynamic_cast<> can only be used on types available at compile time. That is, quite frankly, rubbish. The dynamic_cast<> operator is explicitly there to check for type relationships at _run time_.

.f
0
 
LVL 8

Accepted Solution

by:
fl0yd earned 136 total points
ID: 9034779
Short follow-up:

You can easily retrieve a pointer to the exported function from msvcrt.dll:

typedef void*(*myFxnPtr )( type_info*, int, type_info*, type_info*, int );

HMODULE hModule = GetModuleHandle( _T( "msvcrt.dll" ) );
myFxnPtr RTDynamicCast = (myFxnPtr)GetProcAddress( hModule, _T( "__RTDynamicCast" ) );

Now you can use RTDynamicCast like any other function, no need for the asm-hackery. If you are that much in love with asm, go for signal/image process and get a grasp on SIMD extensions.

.f
0
 
LVL 1

Author Comment

by:lar_jens
ID: 9034794
fl0yd:

Yes, I know this, but...

Have you tried to do the following:


BOOL CObject::isKindOf ( parameter )
{
  return ( dynamic_cast<parameter> (this) ) ? TRUE : FALSE;
}

and if you have, please tell me how you solved the problem, cause I
would much rather use C++ than ASM on this one, if I can.

Remember -> The goal is the isKindOf function, not the dynamic_cast operator...
0
 
LVL 1

Author Comment

by:lar_jens
ID: 9034808
To fl0yd:

Thanx for the tip.. :)
I will try to rewrite the function so that it uses the msvcrt.dll instead..
0
 
LVL 1

Author Comment

by:lar_jens
ID: 9034815
And one more question to fl0yd:
Why all the sarcasm?
0
 
LVL 49

Expert Comment

by:DanRollins
ID: 9034932
lar_jens,
Perhaps some sarcasm is due:  This thread was started over 4 months ago, then you dropped off the face of the earth.  Now you're coming back with this RTTI kludge as if it were some sort of solution to the problem that was originally stated.
0
 

Expert Comment

by:ssnet
ID: 9035177
ohhhhh.... buuuurrrnnn. :p

anyhoo: thanks for the isKindOf info... bold assembly man. thinkin outside the box.

i know what you were trying to do with the dynamic_cast, and i'll try to explain why it didn't work... basically, the casting operators take TYPES not instances, so

BOOL CObject::isKindOf ( parameter ){
 return ( dynamic_cast<parameter> (this) ) ? TRUE : FALSE;
}

won't work... (it SHOULD be doable with a properly designed typeid operator... but that's a different can o' worms)

what you need is something that takes a TYPE as a param... that sounds like a template to me... infact, i've had luck with something similar to what you have here... you're already using the typeid op, so no extra overhead is incurred with dynamic_cast... try something like this

class BaseClass {
      public:
            template<class T> bool isA () {
                  return (dynamic_cast<T *>(this) != NULL);
            }

            virtual void bargle(){}
};

the virtual function bargle() is just there to give us a vtable (dynamic cast only works on stuff with vtables, since it's for runtime casting and all)... if we don't want to compare types from different trees, we actually don't need this, because the compiler can do it at quote{compile-time}... assuming you have virtual functions in the root of your tree (almost certainly the case), you're fine and can use any type...

so you would use something like this as so:

BaseClass myBaseClass;

myBaseClass.isA<std::string>(); // false, unless you inherit from std::string
myBaseClass.isA<BaseClass>(); // true

i'm really not a fan of templates (i'd rather just go to an interpreted lang, or even use extrenuous casting, given the kludge of naming a full-out class template), but here it saves some typing... much like inline functions save typing (ie. the typist's overhead is small).

it's interesting to note from this that there are many cases that could and should be resolvable at compile time BY the compiler, and while this method works in the generic case -- for dynamically linked code, and statically linked code -- the statically linked relationships would be more efficiently resolved at compile-time... but then, this would require some sort of crazy compile_cast<>() that evaluates at compile-time rather than runtime, like a macro or something, or one that resolves at compile time only if the source scope resolves then... the effect simply being a compile-time way to test inheritence relationships, without having to dig through all the documentation... (wich may be too rediculous a desire to be important)

it's something i find interesting is all...

hope it helps...
0
 
LVL 8

Expert Comment

by:fl0yd
ID: 9035196
The Problem with your isKindOf( parameter ) is a limitation of the language C++ -- it doesn't provide a data type that refers to a type, i.e. there is no 'type t1 = CButton;' in contrary to other languages like Lisp.

RTTI is a partial solution to the problem, but especially in this context there are cleaner ways since all types are known at compile time (if you want to check whether some object was derived from your generic base, CObject):

template<class T, class U>
class Conversion {
    typedef char Small;
    class Big { char dummy[2]; };

    static Small Test( U );
    static Big Test( ... );
    static T MakeT();
public:
    enum { exists = sizeof( Test( MakeT() ) ) == sizeof( Small ) };
};

Now you can do the following:

BOOL b = Conversion<CButton*, CObject*>::exists;

A bit of magic is going on here, but all within the rules of the language's standard. What the template does is basically exploit the compiler's ability to check for convertibility. If type T can be converted to type U, the function returning Small will be used. For all other cases the one returning Big will be invoked. MakeT() is needed to make sure the template also works for types that have a private/protected default c'tor -- otherwise a simple T() would have been enough. The last step is to compare the sizes of the returned values to see whether the compiler was able to convert T to U. [sizeof( Small ) is guaranteed to be 1, whereas sizeof( Big ) is implementation defined and can be anything. It will always be larger than 1 though.]

.f
0
 
LVL 1

Author Comment

by:lar_jens
ID: 9035280
DanRollins,

I simply answered the following :

>> i think of greater interest is this iskindof() function, which you claim to be using with success,
>> but give no detailed information of -- not even parameters.... have you found a reflection gem i'm
>> missing out on? :P

from ssnet, which was posted 07/14/2003 06:36PM PDT
My posting was also directed to ssnet, and was not an attempt
to solve the problem originally stated..

But enough about that. It seems that people are interrested in what is going on in here anyways.

0
 
LVL 49

Expert Comment

by:DanRollins
ID: 10139180
recommendation: split points between fl0yd, BeyondWu, ssnet
0

Featured Post

Want to be a Web Developer? Get Certified Today!

Enroll in the Certified Web Development Professional course package to learn HTML, Javascript, and PHP. Build a solid foundation to work toward your dream job!

Question has a verified solution.

If you are experiencing a similar issue, please ask a related question

This is a fine trick which I've found useful many times, when you just don't want to accidentally run a batch script or the commands needs administrator rights.
This article will show how Aten was able to supply easy management and control for Artear's video walls and wide range display configurations of their newsroom.
If you’ve ever visited a web page and noticed a cool font that you really liked the look of, but couldn’t figure out which font it was so that you could use it for your own work, then this video is for you! In this Micro Tutorial, you'll learn yo…
Sometimes it takes a new vantage point, apart from our everyday security practices, to truly see our Active Directory (AD) vulnerabilities. We get used to implementing the same techniques and checking the same areas for a breach. This pattern can re…
Suggested Courses

801 members asked questions and received personalized solutions in the past 7 days.

Join the community of 500,000 technology professionals and ask your questions.

Join & Ask a Question