views:

74

answers:

1

Hi everyone,

I have implemented a templated buffer class like this:

template <class T, class Impl>
class base_hardware_buffer : Impl
{
public:
    typedef const T& const_reference;
    typedef T* pointer;

    void push_back(reference r)
    {
        // Do some generic operation, call impl when needed
        // ...
    }

    pointer map()
    {
        static_cast<Impl*> (this)->map();
    }

    // ... etc
};

Then I defined two implementations, one for OpenGL and one for DirectX each instantiated in their own dynamic library. For OpenGL:

template <class T>
class glBuffer : base_hardware_buffer<T, glBuffer<T> >
{
public:
    typedef T* pointer;

    pointer map()
    {
        // Actual implementation of map
    }

    // Other implementation specific operation and members go here
};

This is all very nice, and works quite well. Now my problem is that my team mate who is working on the layer above my subsystems wants to implement an algorithm that requires access to the buffers.

How can I give him a uniform access to the buffer without exposing / hard-coding the specific implementations into his code, nor compromising type safety ?

A: 

I can explain how I implemented a solution to a similar problem.

Basically I have a "CreateBuffer" function that returns a pointer to a "base" buffer. Then when I call functions on that base buffer they move up to the relevant function.

class BaseBuffer
{
public:
    BaseBuffer(void);
    virtual ~BaseBuffer(void);

    virtual bool Init( GFXHandler* pHandler, unsigned int usage, unsigned int sizeofData )  { return false; }
    virtual bool Shutdown()                                             = 0;

    virtual bool LockBuffer( unsigned int flags, void** ppData )        = 0;
    virtual bool UnlockBuffer()                                         = 0;
};

Now the trick I used, to make sure that the end user doesn't care what the back end implementation of the buffer is, was to make "CreateBuffer" a function pointer. Then when I set up the graphics handler (e.g. GL2Handler) I assign the "GL2CreateBuffer" function to the "CreateBuffer" function pointer.

This way the end-user can just do the following:

BaseBuffer* pBuffer = CreateBuffer();
pBuffer->Init( /*init params*/ );

void* pData = NULL;
pBuffer->LockBuffer( /*lock flags*/, &pData );

memcpy( pData, pOriginalData, sizeOfOriginalData );

pBuffer->UnlockBuffer();

Job done. If a D3D9Handler was created it used the D3D9 implementation if a GL2Handler was created it uses the GL2 implementation etc.

Edit: In answer to your comment wouldn't you just be able to do the following:

template< typename Type > bool LockBuffer( unsigned int flags, Type** pData );

Or do you want to specifically create a class where the only returnable type is the type that it was created with? That would be a tad more complicated as you suddenly have tonnes of different types that would need assigning to the various function pointers as 2 seperate types passed into a template create 2 different functions.

Goz
Thanks for the answer, I think that this is a perfectly acceptable solution.One question though, how do I keep my type safety when locking the buffer ? You're using a void** data type, but in the past this has caused many bugs hence why I wanted to strongly type the buffer.I'm thinking maybe type traits + templated non-member function that would call the LockBuffer or emit an error if the data type do not match ?
LeSnip3R