Archived

This topic is now archived and is closed to further replies.

marijnh

(c++) Object copies and holding resources

Recommended Posts

I am having some trouble writing objects that hold a resource (open file, memory pointer, open GL texture object), which can be put into std:: containters without screwing up. The idea is that the object acquires the resource in the constructor and releases it in the destructor (pretty much like filestreams or autopointers), but when they are copied they should lose their resource and pass it on to the new object without re-allocating it. For example my texture object should, when copied, pass its texture id (gl texture object id) on to the new object and become invalid itself, but the new object should not reload and create the texture itself, it just receives an (already valid) texture id. This is needed mostly because i want to put these objects into containers in an efficient way and that generally involves a call to the copy constructor. Any clues or source code would be greatly appreciated.

Share this post


Link to post
Share on other sites
A couple of thoughts:

- use containers of pointers-to-resource-holders ;->
- have the copy ctor and/or assignment operator release the resource reference held by the input object
- use reference counting ( see below )

// dynamic memory allocation version
class ResourceHolderRef
{
public:
ResourceHolderRef( Resource *const resource ) : m_resourceHolder( new ResourceHolder( resource ) ) {}
ResourceHolderRef( ResourceHolderRef const &orig ) :
m_resourceHolder( orig.m_resourceHolder ) { m_resourceHolder->AddRef(); }
~ResourceHolderRef() { m_resourceHolder->Release(); }
inline Resource* GetResource() const { return m_resourceHolder->GetResource(); }
private:
class ResourceHolder
{
public:
ResourceHolder( Resource *const resource ) : m_resource( resource ), m_refCnt( 1 ) {}
inline Resource* GetResource() const { return m_resource; }
inline void AddRef() { ++m_refCnt; }
inline void Release() { if( --m_refCnt == 0 ) delete m_resource; delete this; }
private:
~ResourceHolder() {}
Resource* m_resource;
int m_refCnt;
};
ResourceHolder* m_resourceHolder;
};

// static memory allocation version
class ResourceHolderRef
{
public:
ResourceHolderRef( Resource *const resource ) : m_resourceData( resource ), m_resourceHolder( &m_resourceData ) {}
ResourceHolderRef( ResourceHolderRef const &orig ) :
m_resourceHolder( orig.m_resourceHolder ) { m_resourceHolder->AddRef(); }
~ResourceHolderRef() { m_resourceHolder->Release(); }
inline Resource* GetResource() const { return m_resourceHolder->GetResource(); }
private:
class ResourceHolder
{
public:
ResourceHolder() : m_resource( 0 ), m_refCnt( 0 ) {}
ResourceHolder( Resource *const resource ) : m_resource( resource ), m_refCnt( 1 ) {}
inline Resource* GetResource() const { return m_resource; }
inline void AddRef() { ++m_refCnt; }
inline void Release() { if( --m_refCnt == 0 ) delete m_resource; }
private:
~ResourceHolder() {}
Resource* m_resource;
int m_refCnt;
};
ResourceHolder m_resourceData;
ResourceHolder* m_resourceHolder;
};

Share this post


Link to post
Share on other sites
Use boost::shared_ptr. It reference counts whatever its containing, and you use it like an auto_ptr. When the ref count goes to zero, it runs the resource''s destructor. When you move it in and out of containers you pay only the cost of adjusting the ref count.

I realize this is against the spirit of gamedev though, by suggesting third party libraries.

Share this post


Link to post
Share on other sites
An easier, often better solution is to disallow copying by making the copy constructor and operator= private and unimplemented.

Users can still pass that object around by reference.


class Foo
{
private:
ResourceType some_resource;

Foo( const Foo& );
Foo& operator=(const Foo&);
};

void Function1( Foo& );
void Function2( Foo );

Foo f;

Function1( f ); // Ok

Function2( f ); // Error, can''t copy.



Or, if you REALLY want ownership transfer, hold the resource via an std::auto_ptr. But I strongly advise against it.

Pass-by-reference is your friend.


[ Start Here ! | How To Ask Smart Questions | Recommended C++ Books | C++ FAQ Lite | Function Ptrs | CppTips Archive ]
[ Header Files | File Format Docs | LNK2001 | C++ STL Doc | STLPort | Free C++ IDE | Boost C++ Lib | MSVC6 Lib Fixes ]

Share this post


Link to post
Share on other sites
Well, he said he wanted to put it in an STL container. STL uses references internally on a lot of implementations and you can''t have a reference to a reference currently in C++, so unfortunately references aren''t going to help here.

Share this post


Link to post
Share on other sites
I know the separate-resource-object-with-ref-counter approach, but i am looking for a more lightweigt solution here: i do not want to have multiple object pointing at the same resource, i just want to be able to stuff my object into containers in an efficient way. i figured out this method:


class TextureObject{
public:
TextureObject(const char* texturefile){
//Initialize the texture, save gl textureobject id to _id

_valid = true;
}
TextureObject(const TextureObject& other){
_id = other._id;
other._valid = false;
_valid = true;
}
~TextureObject(){
if (_valid)
//De-allocate texture object

}
void bind(){
if (!_valid)
throw horrible_exception();
//Set up openGL to use the textureobject _id

}
private:
bool _valid;
unsigned int _id;
};


The operator= would look similar to the copy constructor. I suppose having a private resource object is a little less error-prone since you can have multiple valid objects pointing at the same resource, but it requires more new/delete calls and more work, so i'll stick with this when it suffices.

Marijn


[edited by - marijnh on August 8, 2003 11:58:16 AM]

Share this post


Link to post
Share on other sites
quote:
Original post by marijnh
I know the separate-resource-object-with-ref-counter approach, but i am looking for a more lightweigt solution here : i do not want to have multiple object pointing at the same resource, i just want to be able to stuff my object into containers in an efficient way .

quote:
I realize this is against the spirit of gamedev though, by suggesting third party libraries.


*cough* Somebody called *that* one.
Lets think about this for a second: what''s more efficient:
1. adjusting a ref count (boost::shared_ptr)
2. copying the resources (which may or may not be trivial), flipping flags, *and* checking a flag on every operation.

Sorry but having a valid flag just sucks. If the object exists it is valid in my mind. The fact that a texture object might exist but no longer be valid is just silly, and the texture object should know NOTHING of implementation constraints like those.

If you''re so concerned about microoptimizations then just store pointers in the container and delete them at the appropriate time.

Share this post


Link to post
Share on other sites
well, the kind of objects i am aiming at here is supposed to be one-of-a-kind, they will be kept in a container (put there by a manager object) and stay in there, other parts of the program use references to those in-container objects. thus invalid objects should not be a problem - only the temporary object that gets pushed into the container is invalid for a few instructions before it gets deleted. i am quite sure this is more efficient than creating a separate object on the heap with a refcount. i know my solution is not idiot-proof, but it will do.

Marijn

edit: really confusing typo

[edited by - marijnh on August 8, 2003 12:59:20 PM]

Share this post


Link to post
Share on other sites
I still think putting pointers into the std container is the way to go...why can''t you do this?

Share this post


Link to post
Share on other sites
Putting pointers in containers is the easiest way to go, but that leaves you responsible for cleaning up the objects and you have to make sure not to copy them. I''ve ended up using a reference-counting ''sub-object'' inside these objects, thus i can treat them as normal self-destroying, safe copying object without risk of leaking memory.
The approach i showed above had a big mess up - i change something in an object referenced by a const reference, since the default copy-constructor takes a const reference. This can be solved with a const_cast, but that''s not very nice coding.

Marijn

Share this post


Link to post
Share on other sites