hmm well.... ive just taken a look at the IDirect3DDevice9 interface and i'm surprised: my interface looks like a subset of it - even i have pointers to interfaces everywhere - the major difference is, that i don't use plain pointers in the interfaces, but pointer-objects which can hold any type derived from IPointer (a pointer interface).
the rule for the render device is, that all resources created with it (e.g. vertex buffers, index buffers) must managed - and must be guaranteed to work with at least reference counted pointers (so cyclic dependencies of managed pointers are allowed to cause troubles). so if all pointer objects pointing to a certain object go out of scope/are reassigned/are deleted then the resource must be release too.
no matter how the pointer object is implemented (so no matter what garbage collection strategy it uses) the interface remains the same.
so that would be some interfaces
Code:
template <typename T>
class _IPointer
{
public:
// operators like ->, *, comparision etc... so all operators you'd use with a pointer to a single object (not arrays)
};
class IVertexBuffer
{ .. };
class IIndexBuffer
{ .. };
// _AnyPtr is the magic pointer template that can hold any object
// that is derived from the IPointer interface. also _AnyPtr provides the IPointer
// interface itself.
// that way _AnyPtr has the same interface and behaviour as the wrapped object.
// so the most important attribute of _AnyPtr is a boost::any attribute.
typedef _AnyPtr<IVertexBuffer> AnyVertexBufferPtr;
typedef _AnyPtr<IIndexBuffer> AnyIndexBufferPtr;
class IRenderDevice
{
public:
virtual
AnyVertexBufferPtr create_vertex_buffer(unsigned size,
EBufferUsage buffer_usage,
const void *p_data = NULL) = 0;
virtual
AnyIndexBufferPtr create_index_buffer (unsigned size,
EBufferUsage buffer_usage,
const void *p_data = NULL) = 0;
...
};
and that would be an implementation:
Code:
class RenderDeviceOpenGl
: public IRenderDevice
{
// same signatures as in the interface
};
Code:
AnyVertexBufferPtr
RenderDeviceOpenGl::create_vertex_buffer(unsigned size,
EBufferUsage buffer_usage,
const void *p_data)
{
...
// SRefCntPtr<T> is a template for a simple reference counted pointer, which is derived from IPointer<T>.
// since _SRefCntPtr<IVertexBuffer> is derived from IPointer<IVertexBuffer> it can be stored in an
// _AnyPtry<IVertexBuffer> object.
return _SRefCntPtr<IVertexBuffer>(new VertexBufferOpenGl(...));
}
i called it simple reference counted pointer because i did a simple implemenation of it - although more sophisticated methods are possible
and just for fun we use a different smart pointer type for the index buffer
Code:
AnyIndexBufferPtr
RenderDeviceOpenGl::create_index_buffer(unsigned size,
EBufferUsage buffer_usage,
const void *p_data)
{
...
// _SophisicatedRefCntPtr<T> must be derived from IPointer<T> again
return _SophisicatedRefCntPtr<IIndexBuffer>(new IndexBufferOpenGl(...));
}
fun - is it?
ok, i was pretty much talking to myself with this... its almost 7:30 am in the morning, i've been reading tons of docs, and i need to go to the doctor in 30 min anyway - so i just spent the time thinking the whole thing over again - but i think its not bad the way i have done it.
the overhead for accessing the object pointed to by the smart pointer compared to an ordinary pointer is none, since _AnyPtr<T> caches the pointer to the object itself (and operator->() is an inline function).