Taking a step in the API independence directionYou'll notice that in the above code, the usage to say create a 2D texture class would be: class Texture2D : public Object< TexOps<GL_TEXTURE_2D> > { public: /* 2D Texture specific functions */ }; Now this is ok if you know that the texture you are dealing with is a 2D texture, but it is useful to be able to have a base class GLTexture, that you can refer to all textures by. Now this causes us a problem: we would like the base texture class to have a method void Bind() const. However, this method is defined in Object<> so simply using the seemingly obvious solution of multiple inheritance won't work. The solution comes in the form of a diamond inheritance hierarchy. This formation is normally used as an argument against multiple inheritance, but in this case we can use a handy trick called delegation to a sister class[1] to enable us to achieve our aim. As an indirect side effect of this we also achieve the ability to have an API independent base class for our textures! This is what our hierarchy will look like. To avoid having two copies of the base texture object we need to make the inheritance of Texture in GLObject<> and GLTexture virtual. Now this poses us a new problem, we've just made GLObject<> dependent upon Texture! Again templates come to the rescue, making the interface now: class Texture { public: virtual ~Texture() { } virtual void Bind() const = 0; /* Other abstract Texture functions */ }; template<class ObjectOps, class ObjectBase> class Object: public virtual ObjectBase { public: Object() : ID(ObjectOps::GenerateID()) {} ~Object() { ObjectOps::Destroy(ID); } void Bind() const { ObjectOps::Bind(ID); } protected: GLuint ID; }; class GLTexture : public virtual Texture // Notice the virtual keyword - very important! { public: /*GL specific functions and implementations of pure virtual functions from Texture */ }; class Texture2D : public GLTexture, private GLObject<TexOps<GL_TEXTURE_2D>, Texture> { public: /* 2D texture specific functions */ }; Unfortunately this breaks any existing code using the old structure, however this can be easily resolved using an empty base class as a default template parameter. Now we can obtain polymorphic behaviour on pointers to the GLTexture type. Calling the Bind function delegates the call to the GLObject<> sister class. Note that we can use private inheritance for GLObject<> since we are only using it to provide an implementation of the interface, not as an interface itself. When providing access to textures outside your renderer, then you simply return a pointer to a Texture object. This gives you API independence in a way that won't break any client code as long as the Texture interface doesn't change. This article doesn't consider the overall state management of the extensions that these classes affect, but this is a trivial matter to implement in a state management class. You may also want to try to implement a manager class for each of the base classes you have (textures, vertex/fragment programs) that selects at runtime the type of class to instantiate based upon the data given, for example it is quite simple to determine whether a vertex program is a NV or ARB program from the first string in the program. I have provided some sample header files to implement the common extensions that use this technique, however I will not be providing any of the concrete classes that are derived from them. Most of the code is presented here in the article anyway. References[1] C++ FAQ lite: http://www.parashift.com/c++-faq-lite/multiple-inheritance.html#faq-25.10 [2] SGI OpenGL Extension Registry http://oss.sgi.com/projects/ogl-sample/registry/ |
|