|
I've done a lot of mucking around with this. Yes, the documentation SAYS that it should return NULL. However, dynamic_cast sometimes crashes when given an invalid pointer, and sometimes throws a bad_cast exception. Go figure!
And RTTI doesn't work with multiple levels of derivation. I've tried.
swinefeaster
Check out Aephid Photokeeper, the powerful digital
photo album solution at www.aephid.com.
|
|
|
|
|
swinefeaster wrote:
I've done a lot of mucking around with this. Yes, the documentation SAYS that it should return NULL. However, dynamic_cast sometimes crashes when given an invalid pointer, and sometimes throws a bad_cast exception. Go figure!
I'd love to see the code. I don't doubt you, but this has *never* happened to me and I use dynamic_cast constantly. MAybe we can find the reason you're getting such errors.
swinefeaster wrote:
And RTTI doesn't work with multiple levels of derivation. I've tried.
I haven't, but I'll have a play tonight ( not that I doubt you, I just like to prove stuff ). That is a little disappointing...
Christian
I have come to clean zee pooollll. - Michael Martin Dec 30, 2001
Picture the daffodil. And while you do that, I'll be over here going through your stuff.
Picture a world without war, without hate. And I can picture us attacking that world, because they would never expect it.
Sonork ID 100.10002:MeanManOzI live in Bob's HungOut now
|
|
|
|
|
Here's a sample project that I made in a couple of minutes to illustrate how dynamic_cast<> does NOT work as advertised. Pretty disappointing....
BadDynamicCast.zip
swinefeaster
Check out Aephid Photokeeper, the powerful digital
photo album solution at www.aephid.com.
|
|
|
|
|
It seems to me that you are accessing a pointer in an invalid range in the sample code that you submitted:
<br />
void CBadDynamicCastDlg::OnCrash() <br />
{<br />
CDialog* Crash = (CDialog*)2342;<br />
CAboutDlg* String = dynamic_cast< CAboutDlg* >(Crash);<br />
}<br />
Your pointer at 2342 or 0x926 is well below the 0x40000 range of protected memory. Try giving it a pointer that is above the 4 Meg boundary or NULL.
|
|
|
|
|
That's exactly the point! The documentation of dynamic_cast<> says at the bottom "The value of a failed cast to pointer type is the null pointer. A failed cast to reference type throws a bad_cast exception."
So, if the pointer is bad, it should ALWAYS return NULL. But no, we get a bad_cast exception sometimes, and even sometimes just making this call seems to screw up something pretty bad in the whole app and the whole thing crashes! Yikes.
swinefeaster
Check out Aephid Photokeeper, the powerful digital
photo album solution at www.aephid.com.
|
|
|
|
|
What is inside the angle brackets of dynamic_cast? The html post stripped out this part - to stop this happening, click the "Display this message as-is (no HTML)" checkbox.
e.g. dynamic_cast<...>(val);
--
Andrew.
|
|
|
|
|
Done .
But I've since fixed the bug...
Swine
Check out Aephid Photokeeper, the powerful digital
photo album solution at www.aephid.com.
|
|
|
|
|
Ok, I'm not sure you've sorted this out, but here's my 2 cents.
The dynamic_cast actually has to examine the data at the other end of the pointer you feed it. It needs to look for the vtables of the type to which you want to cast the pointer. I'm guessing that the code inserted for the dynamic_cast doesn't do the dereference intelligently. So if you feed it a pointer that would normally dump core when you dereference it, it's still gonna yak.
The documentation should probably mention this, although perhaps the authors just assumed you'd be feeding it actual object pointers.
J
|
|
|
|
|
Yes, I would expect it to 'yak', but in a way that the catch(...) handler would just be invoked with an exception and everything should continue as normal, no? But it seems that a bad dynamic_cast<> also modifies memory somewhere, and really messes things up.
swinefeaster
Check out Aephid Photokeeper, the powerful digital
photo album solution at www.aephid.com.
|
|
|
|
|
Another possible solution to your problem:
Use a map to track all valid handles in cService. When a handle is freed, remove the entry from the map. When a call is made which uses a handle, see if it exists in the map. If it does not, it is an invalid handle.
A numeric map should be pretty fast (just set the size of the hash table).
Just my 2 cents.
Matt Gullett
|
|
|
|
|
Actually that's exactly what I came up with after some more thought . But then I ran into a global / static object problem with this scheme. Check out my more Recent Post... I implemented the code as follows:
<br />
#pragma once<br />
<br />
#include "cMap.h"<br />
#include "cCriticalSection.h"<br />
<br />
<br />
class cParanoid : public cDestructible<br />
{<br />
public:<br />
cParanoid(void);<br />
virtual ~cParanoid(void);<br />
<br />
static cParanoid* GetParanoid(void* Pointer);<br />
<br />
private:<br />
static cMap<cParanoid*, cParanoid*, bool, bool> m_ParanoidMap;<br />
<br />
static cCriticalSection m_ParanoidSection;<br />
};<br />
<br />
cMap<cParanoid*, cParanoid*, bool, bool> cParanoid::m_ParanoidMap;<br />
<br />
cCriticalSection cParanoid::m_ParanoidSection;<br />
<br />
cParanoid::cParanoid(void)<br />
{<br />
m_ParanoidSection.Lock();<br />
{<br />
m_ParanoidMap[this] = true;<br />
}<br />
m_ParanoidSection.Unlock();<br />
}<br />
<br />
cParanoid::~cParanoid(void)<br />
{<br />
m_ParanoidSection.Lock();<br />
{<br />
if(!m_ParanoidMap.RemoveKey(this))<br />
{<br />
}<br />
}<br />
m_ParanoidSection.Unlock();<br />
}<br />
<br />
cParanoid* <br />
cParanoid::GetParanoid(void* Pointer)<br />
{<br />
cParanoid* Paranoid = NULL;<br />
<br />
m_ParanoidSection.Lock();<br />
{<br />
if(m_ParanoidMap.DoesKeyExist((cParanoid*)Pointer))<br />
{<br />
Pointer = (cParanoid*)Pointer;<br />
}<br />
}<br />
m_ParanoidSection.Unlock();<br />
<br />
return Paranoid;<br />
}<br />
The problem is that I get a pure virtual function called upon shutdown of the app. This is because CWinApp is a global, and the critical section and map of cParanoid get destoyed before CWinApp does, and there are some objects owned by CWinApp that are derived from cParanoid.
Any thoughts?
Thanks!
swinefeaster
Check out Aephid Photokeeper, the powerful digital
photo album solution at www.aephid.com.
|
|
|
|
|
First thought,
if the objects owned by CWinApp are allocated on the heap (ie. new), then I would add a method to my CWinApp derived class something like "NotifyObjDelete(cParanoid* pValue)". In my CWinApp class, I would see if I own that object and set it to NULL.
I would call the NotifyObjectDelete from my objects destructor.
This is not a pretty solution, but might work depending on where you are in your development process.
I'll keep thinking.
Matt Gullett
(Sorry for the Anonymous, I am not at my PC right now.)
|
|
|
|
|
Thanks for the input... but unfortunately this won't do.
First off, the objects can't know anything about the CWinApp object, as they can be destroyed via a thread other than the main gui thread (and you're not supposed to access CWnd objects from other threads, and I believe this goes for CWinApp objects as well).
Second, the real nuts of the problem is that by the time the CWinApp destructor is invoked, the cParanoid static members have already been destroyed. This includes the critical section and the map of valid cParanoid pointers.
There's gotta be a way to specify which global objects get deleted in which order. I just don't know it...
swinefeaster
Check out Aephid Photokeeper, the powerful digital
photo album solution at www.aephid.com.
|
|
|
|
|
Try adding code to your applications ExitInstance method to free the reference to these objects in your derived CWinApp object.
Tim Smith
Descartes Systems Sciences, Inc.
|
|
|
|
|
Now I wouldn't do this, but you might look into the #pragma init_seg pragma. This controls the order that objects are initialized. It would seem that it would also defined the order the are destroyed (reversed hopefully.)
Tim Smith
Descartes Systems Sciences, Inc.
|
|
|
|
|
This works great excluding one VERY minor point. There is a chance that a later created object could have the same address as one just freed. Thus a cUser object could be using an old pointer which just happens to match a new one. (I have no idea how rare this might be.)
We got around this problem by using an unique index system. Here is the basic layout.
1. Assume you have a table going from 1 to 4096 (4096 is just for example and the table size can grow if needed.) Each element of this table consists of the handle match value and a pointer to the real objects.
2. The handle is generated by taking the index in the table and oring it with a counter specific to that index. For example, let us say the handle was a DWORD. The low word is the index into the table. The high word is the unique counter for that index. Every time the index is allocated for an object, the unique counter is incremented. Thus, the first time index 20 is allocated, the handle would be 0x00010014. The next time it is allocated it would be 0x00020014. When a handle is allocated, the generated handle should be stored in the handle match value for that index.
3. When a handle is freed, it is placed at the end of a deleted list. Thus, all indexes are used prior to an index getting reused. This helps to make sure that a long period of time goes by before a handle is reused.
4. The table need not be a fixed size. If you start with 4096 entries and use all but 256 up, you can double the size of the table. There are two reasons to do this. First, you don't run out of indexes. Second, it helps to make sure handles don't get reused to quickly. If you had 4095 handles allocated and then allocated and freed the last one 65536 times quickly, it would reuse a handle quickly.
5. To look up an object from the handle. Take the handle and AND off the unquie counter. Locate the index in the object table. Compare the full handle against the handle match value for that index. If they do not match, then someone is using an old handle. If they do match, return the pointer.
6. This index system is actually faster than maps.
7. HOWEVER, 9 times out of 10 a map is a better solution because there is little worry about reusing the same object address as a handle.
(Hmm, maybe I should submit my handle to object class to CP.)
Tim Smith
Descartes Systems Sciences, Inc.
|
|
|
|
|
Very well thought out idea, Tim. Maybe you should indeed post the article. However, the handle reuse problem does not apply in my app, as I already thought of this before and avoided it in other ways... Hmmm, I'll look into that pragma you mentioned.
Cheers,
swinefeaster
Check out Aephid Photokeeper, the powerful digital
photo album solution at www.aephid.com.
|
|
|
|
|
Yeah, the #pragma init_seg(lib) directive works just great! I know it's a bit of a hack, but it'll do quite well for now. All those nasty crashes are gone! I can't believe that I was having so much trouble just because of a stupid dynamic_cast<>!
cheers,
swinefeaster
Check out Aephid Photokeeper, the powerful digital
photo album solution at www.aephid.com.
|
|
|
|
|
Hmmm looks like I am gonna have to implement this for one of my classes! I think I am having the handle reuse problem, though in a different module...
I will try to follow your suggestions, and see how it goes.
Thanks!
swine
[b]yte your digital photos with [ae]phid [p]hotokeeper - www.aephid.com.
|
|
|
|
|
does Win98 interpret a WM_KEYDOWN message differently than in WinNT ?
I have a method that extract a bunch of keyboard data whenever the WM_KEYDOWN is hit
It pulls off stuff like the virtual key code, the char representation, etc. On NT this works fine (and is my primary dev platform). But when I run it on 98 I get nothing! For example the VK codes that I get on NT turn to nothing on 98. Is there something special you have to do to properly handle this ?
|
|
|
|
|
From my experience this should be the same except some bizarre key combinations such as Shift + (keypad +,-).
|
|
|
|
|
I don't know if this is the appropriate forum to be asking this question, but i'll assume as VC++ programmers someone will know, because it's probably VC++ programmers who write the code for the JScript interpreter.
Do the following code optimizations apply to both compiled and interpreted code.
Common sub-expression elimination
Loop invariant hoisting
Dead store elimination which i'll assume is dropping unused functions...?
I gave this some thought and i figure if it works for compiled why shouldn't it work in scripts...? Do you think the JScript interpreter does this while reading the file into it's buffer...? dead store is obvious...less text, less size = quicker downloads. You get size opt, but wouldn't this make a difference with execution time as well...?
Merci buckets...
Tia
"An expert is someone who has made all the mistakes in thier field" - Niels Bohr
|
|
|
|
|
I don't know this for certain but I would be very surprised if any significant code optimization is done by the JS compiler. Speed of compilation is generally a higher priority than speed of execution.
|
|
|
|
|
Really...i would figure both would be about equal for scripting langs. Maybe even in favour for run time, seeing how people kinda expect to have a short delay in the download process.
I'm really curious if optimized code would work in a interpreted environment.
Take the following example:
for(i=0; i<10000; i++)
a[i] = b + c;
t = a + b;
for(i=0; i<10000; i++)
a[i] = t;
even in interpretation it one less operation to perform each iteration of the loop...?
I figure i'll try it out by executing similar loops and timing the diff. Only i don't know quite how to go about it. Would JScript allow me to clock it in the code itself or will i have to do it through the IWebBrowser2 or similar component
Minus 30 degrees C is so damn cold
"An expert is someone who has made all the mistakes in thier field" - Niels Bohr
|
|
|
|
|
If the "interpreter" is actually a JIT compiler, then it can do all those optimizations, but a true interpreter would be limited to statement-level common subexpression elimination.
|
|
|
|