Sign in to follow this  
janta

preventing: delete &refToSomething

Recommended Posts

Hello, I have a Manager class that deals with Resources. I want to prevent the 'user' form using Ressources through pointers directly (new/delete), so how good is it to make a sort of: (veri basic pseudo-example)
const Resource& CreateResource(int resId)
{
    Resource* pRes = new Resource(resId);
    
    if(failure)
        return Resource::nullResource;
    else
        return *pRes;
}
- The first point is that no resource made available to the user should ever be null. You can think of nullResource as a 1x1 transparent texture for example - The second point is that it delegates the resource management to the Manager class. Yet I wondered how enforce the (basic idiot) user not to break the system's rule and call a:
const Resource& cRef = CreateResource(0x001122333);
delete &cRef;
I've tried this with a basic struct example at home and it does work perfectly. At first I thought that the const keyword would prevent one to call delete on it, but that seems not to be the case. (I havent deeply thought of what no-delete-on-const-ptr would imply in a larger scale, though) ( - little digression - or is it that the & operator applied to a const reference doest yield a const pointer ?) The only thing I've came up with is overloading operator delete without actually changing it, put placing it in the private access. Any thoughts about that ?

Share this post


Link to post
Share on other sites
I'd just not worry about it. If the user wants to cripple things by behaving badly, there's not much you can do to stop it. Providing good documentation about how to use it and get the best experience is really the best way.

I don't think overriding delete in the resource class will help. That'd work for methods of Resource calling delete, but not ouside the class. Plus, user could always just call ::operator delete(ptr) anyway.

Share this post


Link to post
Share on other sites
Yup you're quite true. But in fact, like most question I post here to GDNet, this one was curiosity-driven rather that need-driven, if you know what I mean :) So any other thoughs are still welcome !

Cheers,
Janta.

Share this post


Link to post
Share on other sites
Consider having your manager class return a smart pointer, such as boost::/std::tr1::shared_ptr<>, to your resource instead of a reference. This both conveys semantically that the resource should not be freed by the client and reduces the chances of the resource not being properly freed.

Share this post


Link to post
Share on other sites
Making the destructor private is also an option, though you will have to make the manager class a friend, so that it can properly dispose of the object. It will also prevent you from creating local variables of that type.

Share this post


Link to post
Share on other sites
Thats a good point, since a "Resource& res" could easily turn into a "Resource res", with the compiler barely noticing in some cases.

But now I've thought more about it, I'm questionning the point of returning references rather than just pointers, assuming that I cant prevent the end user from doing something stupid anyway. The delete and new could still be kept private, instanciation/delete could still be delegated tasks of the manager, and all Resource* pointers would be obtained through the same kind of "factory pattern" (not sure the name is really appropriate though), thus preventing them from ever be NULL

If my Resources were ref-counted, then handling pointer or references would be almost as bad. I mean with a pointer, a programmer could just set it to NULL or let it go out of scope without decrementing the ref count, but the problem would be the same with a reference (although I guess that with a reference, a programmer is not undergoing the temptation of deleting or "=NULL"-ing)

Thoughts thoughts thoughts... I never see the end of them. In addition, boost's smart pointers (which I had considered in fact) seem to be a very good solution too (though I tend to like gettings my hands dirty)

I appreciate your replies so far :)
Janta

Share this post


Link to post
Share on other sites
My thoughts:

- Don't return references to resources. This is a pain in the butt for the user. You can solve the "null resource is a valid 1x1 resource" issue just the same with pointers. Returning references means

TextureResource &tex = texMgr->GetTexture("foo");

if(!tex.CanBeRenderTarget()) {
// Oops, try a different texture.
tex = texMgr->GetTexture("foo2");
}

// use tex as a render target

does not do want you probably want, since references cannot be reseated. One part of resource management is given the resource "handles" (whatever they are) value semanatics so that the user can pay more attention to the use of the resource, rather than the management of the lifetime of its variable.

- boost::shared_ptr<> works wonderfully for this. Typedefs like:

typedef boost::shared_ptr< Texture> TextureH;

allow the texture handles to be treated basically as value types (excepting the use of operator-> to access them, which I prefer to . anyway, visually). The reference counting is done for you (no need to manually have users add or recall references), and if you ever want to switch to a different wrapper than shared_ptr, all you need to supply is the appropriate operator-> and the rest of your code should work fine (unless you invoke shared_ptr member functions anywhere). I've had success with a lightweight wrapper around shared_ptr as well (to prevent invocation of undesired shared_ptr methods as well as some other things).

- There's a point at which you can't stop the user from doing really stupid stuff. And well before that point, you'll be optimizing for the rare case when the user is doing something slightly stupid, and that's a waste of your time. If you are not afraid of the friend keyword, you could make your constructors and operator= private (leave the destructor public) and designate friendship to the manager. That way the user cannot dereference your pointer/handle/what have you an obtain a regular Resource from a Resource* (etc), at it would invoke the copy ct or operator= and cause a compiler error.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
you could also think about not giving the pointer/reference to the user in the first place. you could give the user an (otherwise meaningless) int instead. then the user has to invoke methods of the instance in question via the resource managed by passing that ID like
resource_mgr->do_something(id0, parameters_here);
basically you 'd tell the managed to do something with a given instance identified by id0.
i think that in (user created) game code this might actually be worth it. this could also be helpful if you're planning on introducing multi-threaded processing where you couldn't risk handing out pointers. using the above approach making sure everything is done in a synchronized way could happen at a central location.
just some thoughts, though.

Share this post


Link to post
Share on other sites
Guest Anonymous Poster
i 've been talking about the resource manageR .. i don't know why i kept hitting 'd' so much..

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this