Safe Pointers

Started by
36 comments, last by MtSMox 21 years, 7 months ago
Hi, I''m currently working on a GUI. In it I have windows (duh...). Pointers to the window are shared throughout the program. But I have one problem. Those pointers are invalid when I delete the window. So I have to make something to let the other pointers know they''re invalid. That''s why I came up with the "Safe Pointers". But how to implement them. It should be a class that lifes as long as there are pointers going on in the program to the window it points to. If something wants to delete the window it calls class.release() which makes the pointer invalid. But when to delete the class. And how to keep track of the pointers handed out? Is there a certain design pattern for this? thanks, Mox
Advertisement
"shared throughout the program". eek.

I don''t know exactly what u mean by that, but it sounds bad. An object should not have to have direct pointers to the windows, rather a class that it can interface with which keeps track of pointers. Make your classes more cohesive. There''s no reason, for example, an object of class Monster to know about what windows are active. Instead, give it a pointer to a WindowManager object that does need to know what is and isn''t active. When a window closes, it only has to notify one object (The WindowManager) it has closed. Similarly, Monster only has to worry about one class. Much nicer.

GSACP: GameDev Society Against Crap PostingTo join: Put these lines in your signature and don't post crap!
sounds like you were speaking of smart pointers. reference counted or reference linked ones, that is. only this approach has two disadvantages: the first it shares with java and any other garbage collected languages. it''s the problem of timely finalization and possible unnoticed memory leaks. when there''s a lot of pointers around, nothing will be deleted, or garbage collected. therefore, ressource leaks will most definitely occur if you keep storing the same pointer in a lot of different places. the solution is well known from java: add a dispose() method to xour class and have every access to an already disposed instance throw an exception. this way you''re gonna find those places where your window, for example, that you had thought long since gone is still referenced. of course, you will only notice when in these strange places the stored refs are also USED, so take my advice and make it your habit never to store a ptr to anything you''re not planning to use. at least, you can free any non-memory ressources in the dispose() method, so that even if a memory leak occurs in your program because of forgotten and unused ptrs, those ressources won''t be leaked, too.
the second problem of the ref counting/ref linking approach is that circularly referenced objects will never be deleted because their ref count will never reach zero. but if this problem occurs in your gui library then your code is real shit. (i hope you know that two windows should NEVER point to each other for the purpose of notification. now you''ve got one more reason to have these things managed in a seperate place)

conclusio: use ref counted or ref linked smart ptrs and seperate cleanup from destruction. that means, only if it wasn''t already disposed at destruction time (which should always be regarded an error) the destructor calls dispose() for cleanup and (important!) writes an angry line to a log file so you can easily fix it. this is exactly what i use finlizers in java for (together with an every 10 seconds call of System.GC.collect(), if practicable). of course, all finalizers are gone in release code.

the reason this ain''t a design pattern is that is is very simpla and has been long since know.

p.s. why are you writing your own gui framework?

shadi
quote:Original post by shadi
the solution is well known from java: add a dispose() method to xour class and have every access to an already disposed instance throw an exception.

This solution does not apply to C++, as the results of calling a member function on an invalid pointer are undefined. You cannot guarantee that dispose() will be called, or that the exception will be thrown.
quote:
the reason this ain''t a design pattern is that is is very simpla and has been long since know.

It is a design pattern. It''s called "Smart Pointer".

MtSMox: check out the smart pointers on the Boost Website.
Ok, it''s not that generally used, but I use the class window to be able to interface with the window.
Part of my GUI is based on callbacks. The callback is called to notify the user that an event has occured whithin a window. I pass a pointer to the window in question along so it can be used.
Those pointers are also used by my input part. The mousecursor notifies the window it is hovering over of mouse-event. The pointer to that window can become invalid (even in between 2 events: eg left mouse up, right mouse down). But I think I''m going to change this part, so the pointer is automatically changed if the window order changes.
But still the window pointer used by a user could become invalid, or should this just give an exception, because it''s just too bad The user should have known it was being deleted.

But the smart pointer is kind of what I was going for. I just have to add some things to make it work the way I want to. I''ll just think this all over and try to make it work...

Mox

ps. Why am I making a GUI framework? Should I just use Windows controls in my game?
Yes, of course. Technically, that''s absolutely no problem to use windows controls in your directx app, as long as you''re temporarily switch to flipping by the windows api''s bitblt function, so the controls dont get overridden and the main surfaces don''t get swapped all the time, that would be ugly. And for the looks... Just write an appropriate windows xp theme and have your game apply it at startup and restore the original one when closing or task switching. :-) (that was a joke)

In fact, I do use windows controls sometimes in my windows games, but only for special purposes and where it doesn''t look too bad. And bitblt()-ing the attached surface to the screen works quit well for this purpose, so the game screen doesn''t have to freeze when controls are visible.

For example, I like to use the windows default dialogs for opening/saving files, because they are so much more practical for browsing directories where appropriate and necessary than the things I would write myself. combo boxes do a nice job, either, just like grouped radio buttons and sometimes even labels.

Besides, I like to write real dialog windows, of course frameless and with a background of choice, like made (half)transparent by bitblt()-ing the attached surface. For animations keeping running in the background I have any transparent controls, including of course the full-screen main window itself, repainting (including bitblt() of course) themselves once per frame (informed via a linked list of java-like event listener objects).

But I don''t yet see a way to avoid flickering of user painted elements unless every one of them is placed on a non-transparent region that is excluded from update (sub window, for example, or explicit manual exclusion, depends on purpose). For regions that are hidden by a window this problem doesn''t exist, because the windows api''s bitblt() function uses a GC that simply won''t ever override any hidden regions.

I prefer only to use windows controls (including image controls and controls I have written myself)

Very important: don''t forget to place delays of 5 ms or the like in your app''s main loop for else everything may get extremly slow, like moving through honey.

In the end, you have a very powerful GUI API at your hands that does not at all have to look like the windows gui. Only two drawbacks:

1. directly in the gameplay, you need self-drawn controls (but these should be simplistic anyway, to remain usable without pausing the game), because

2. Don''t expect bitblt wouldn''t have an impact on your frame rate, compared to surface->Flip(). For background animations or a still visible animated game screen in the "menu mode" of your game, this won''t matter so much.

P.S. Don''t forget to Flip() your surfaces in case the primary one is hiden at the moment when you enter your "menu mode", for else no single windows control will be visible (yet they accept input). This can be a confusing bug, when in 50% of all cases everything works fine, in the other 50% not, and the only reaction your computer gives you is a "beep" when you accidentally click outside of the active "open file" dialog. I''ve seen this :-)

-- Smart Pointers remark:

Are you sure that is an official **PATTERN**? I thought the pattern was called "Bridge" aka "Handle-Body" and smart pointers were only a technique, an idiom at best, to achieve it.

I would not prefer to go around using smart pointers directly, with the exception of auto_ptr. I''d rather build a class library based on the Bridge Pattern, using a smart pointer template class to achieve this goal. Because when you''re freely using any kind of pointers just as seems fit, you are most certain to get in trouble. In this case, it''s hidden, never deleted refs (memory leaks) and, of course circular refs, if the smart ptr uses ref counting or ref linking (and it will, I assure you).

Of course, these are problems youre perfectly welcome when you use ref-type-like Bridge Pattern based classes, only the logic of what you''re doing will be far more obvious.

shadi
quote:Original post by shadi
Are you sure that is an official **PATTERN**?

What do you mean by "official"? Don''t fall into the trap of thinking the 23 patterns in the DP book are the only patterns in existence - that would be to deny the nature of patterns. But yes, Smart Pointer is a pattern. If you really insist on having a reference to an in-print publication, then POSA has the Counted Pointer pattern.
quote:
I thought the pattern was called "Bridge" aka "Handle-Body" and smart pointers were only a technique, an idiom at best, to achieve it.

An idiom *is a* pattern. Other than that, you''re correct that smart pointers are a form of handle-body, but note there are many names for such patterns, also including Cheshire Cat and pImpl. They''re all just variations on a theme (which is part of the nature of patterns), and there are not really "official" names - just common names.
quote:
I''d rather build a class library based on the Bridge Pattern, using a smart pointer template class to achieve this goal. Because when you''re freely using any kind of pointers just as seems fit, you are most certain to get in trouble.

Only if you use them inappropriately, and there''s probably less chance of doing that than there is of using a normal pointer inappropriately.
Well, pImpl, or pimpl, as I prefer to call it, is just an extremely reduced form for reducing of compile time dependencies. My approach to smart ptrs is not to regard them as implementing the bridge pattern themselves.

I simply think, smart pointers are to the Bridge Pattern in general what the auto_ptr is to the pimpl idiom.

And for any other purpose there are better solutions, including auto_ptr, which is the only smart pointer that I would ever recommend for direct use outside of class design.

Using smart ptrs in collections is stupid.

On the stack? use auto_ptr.

For global data? There is no global data. singletons at best.

for notification? use soft refs if you have to, use softly ref''ing collections if available.

for manipulating raw memory byte- or wordwise? use native pointers. they won''t try to delete() that red pixel in the upper left corner of your screen when after you''ve been playing with raw directX surface data.

for passing fuctions as parameters? use Functors, maybe created from a function, holding a raw pointer that never has to be touched directly. even its construction will go without a ''*''.

I simply see no point in using smart pointers directly in late stages of software design. and even in early stages I try to put them at their proper place and hide them from my eyesight as soon as possible.

shadi
quote:Original post by shadi
Using smart ptrs in collections is stupid.

Please qualify this statement.
quote:
On the stack? use auto_ptr.

On the stack what? If you mean the pointee, then no, that would be an erroneous use of auto_ptr - you can''t delete something on the stack.

[snip tangential stuff]

quote:
I simply see no point in using smart pointers directly in late stages of software design. and even in early stages I try to put them at their proper place and hide them from my eyesight as soon as possible.

Quite simply, smart pointers should be preferred anywhere that a pointer is relevant to solve a problem. Boost''s smart pointers fill the glaring omission of shared_ptr and scoped_ptr from the Standard. If you don''t want to use smart pointers in your designs, that''s up to you. However, if you want to go around offering advice like "using smart pointers in collections is stupid", you need to explain why it is stupid.
quote:Original post by shadi
And for any other purpose there are better solutions, including auto_ptr, which is the only smart pointer that I would ever recommend for direct use outside of class design.

Golly.

I dunno about you, but auto_ptr is the smart pointer type I use least often, simply because destructive copy semantics are rarely what I want.

boost::scoped_array is handy, because it offers (IMHO) greater convenience than a vector for creating buffers, as well as smaller overheads.

boost::shared_[ptr, array] are useful because I can stick ''em in a collection without having to care about deleting the damn things.

quote:Using smart ptrs in collections is stupid.

Yes, because it''s soooooooooooo much better to have to remember to delete the contents of your container each time you remove something, isn''t it?

:rolleyes:

quote:On the stack? use auto_ptr.

Use auto_ptr for what? auto_ptr can''t point to arrays, and auto_ptr has annoying copy semantics.

quote:for notification? use soft refs if you have to, use softly ref''ing collections if available.

I assume that by "soft" you mean "weak"? Weak references are just another kind of smart pointer....

quote:for manipulating raw memory byte- or wordwise? use native pointers.

Uh... why?

quote:they won''t try to delete() that red pixel in the upper left corner of your screen when after you''ve been playing with raw directX surface data.

Nor will a correctly-chosen smart pointer.

quote:I simply see no point in using smart pointers directly in late stages of software design.

Then you must enjoy remembering where and when to call delete. I don''t; I have the tools to permit such lifetime management to be performed by the objects themselves, which frees me to concentrate on more interesting things.

quote:and even in early stages I try to put them at their proper place and hide them from my eyesight as soon as possible.

:rolleyes:

All I can say is, you''re lucky to never have to work with C APIs. I dunno about you, but I often have to write things that look like:
size_t size = 0;SomeLegacyAPI(NULL, &size);boost::scoped_array buffer(new char[size]);someLegacyAPI(buffer.get(), size);// do something with buffersize = SomeOtherLegacyAPI(size, NULL);buffer.reset(new char[size]);someOtherLegacyAPI(size, buffer.get());// do something with new buffer// etc. 


Now, what do you propose I do instead?

I could use raw pointers... but to be honest, that''s a pain. I very rarely omit deletes, but I have done so at least twice in the past three years of professional programming.

I could use std::Vector<char>... but that''s got problems of its own. It''s not (yet) mandatory for it to use contiguous storage. And even if it were, the Defect Report doesn''t provide an acceptable answer. Reason being, it doesn''t consider alignment issues. The memory from new char[N]; is guaranteed to be suitably aligned for all types T such that sizeof(T) <= N. Even with the fixed (guaranteed contiguous) vector, that (stricter) requirement isn''t made. An implementation would be permitted to have &v[0] where v is a std::vector<char> be an address that''s aligned on a boundary unsuitable for some non-char type. In addition, I can''t easily shrink a vector. There is a common idiom to effect such a thing, however it''s not (yet) strictly correct; a Defect Report is pending, so once a library TC is created, the "correct" behaviour should be correct. In any case, the use of a vector is IMHO uglier (&buffer[0] vs. buffer.get(), vector<char>().swap(buffer) vs. buffer.reset(), etc.), and a vector certainly carries more overhead than a simple scoped_array.

Or, I could use boost::scoped_array, and be both guaranteed that I won''t leak and have simple syntax.

In the absence of any better solution (and, no, wrapping Win32 in a bunch of classes isn''t a better solution), smart pointers remain far and away the best way of doing things.
char a[99999],*p=a;int main(int c,char**V){char*v=c>0?1[V]:(char*)V;if(c>=0)for(;*v&&93!=*v;){62==*v&&++p||60==*v&&--p||43==*v&&++*p||45==*v&&--*p||44==*v&&(*p=getchar())||46==*v&&putchar(*p)||91==*v&&(*p&&main(0,(char**)(--v+2))||(v=(char*)main(-1,(char**)++v)-1));++v;}else for(c=1;c;c+=(91==*v)-(93==*v),++v);return(int)v;}  /*** drpizza@battleaxe.net ***/

This topic is closed to new replies.

Advertisement