I had an interesting problem that can be solved using a little bit tricky C++ code. Imagine a singleton that holds some pointer to a class that has many methods with identical params, but different purposes. Now, imagine you're creating some wrapper for this class in some other class (the first one was a ID3D11Device in my case, so I didn't modify it) and your code is also identical, despite this single method call. You can obviously copy paste this problem, creating n-times more lines of code. But this is an ugly solution, so below I paste the nice one. Class A is the ID3D11Device, class B is my wrapper. In my case fun1 was enough (i knew A type while implementing this), but because i find this useful - i paste code for case with unknown A type. Of course in practice, fun1 and fun2 would be big functions with this single call embedded somewhere deep.
void a(int param) { std::cout << "a " << param << std::endl; }
void b(int param) { std::cout << "b " << param << std::endl; }
};
struct B {
template< void (A::*F)(int) >
void fun1() {
A* a = new A; // obtain A, this may be singleton or anything else
(a->*F)(1);
delete a;
}
template< typename T, void (T::*F)(int) >
void fun2(T* t) {
(t->*F)(2);
}
};
int main() {
B b;
b.fun1< &A::a >();
b.fun1< &A::b >();
A a;
b.fun2< A,&A::a >(&a);
b.fun2< A, &A::b >(&a);
return 0;
}
The only aspect that may need some explanation at the moment is how to pass ID3D11Device method marked with STDMETHODCALLTYPE as template parameter. Notice, that STDMETHODCALLTYPE is just a macro for __stdcall. We can pass this additional keyword using syntax like:
template< HRESULT (__stdcall ID3D11Device::*CreateShader)(const void*, SIZE_T, ID3D11ClassLinkage*, ID3D11VertexShader**) >
bool load_(const char* filename) {
...
}
Ok, now for the real example - the code will be a little bit more tricky, because we have to use derived template param as param for functions used as template params, blah, compliacted - look at the code :)
class Shader {
protected:
ShaderType* shader;
public:
Shader() {
shader = NULL;
}
virtual ~Shader() {
if(shader) {
shader->Release();
}
}
template< HRESULT (__stdcall ID3D11Device::*CreateShader)(const void*, SIZE_T, ID3D11ClassLinkage*, ShaderType**) >
bool load_(const char* filename, const char* entryPoint, const char* shaderModel) {
...
}
template< void (__stdcall ID3D11DeviceContext::*SetShader)(ShaderType*, ID3D11ClassInstance* const*, UINT) >
void bind_() {
...
}
};
class VertexShader : public Shader< ID3D11VertexShader > {
public:
__forceinline bool load(const char* filename) {
return load_< &ID3D11Device::CreateVertexShader >(filename, "mainVS", "vs_4_0");
}
__forceinline void bind() {
bind_< &ID3D11DeviceContext::VSSetShader >();
}
};
class PixelShader : public Shader< ID3D11PixelShader > {
...
};
class GeometryShader : public Shader< ID3D11GeometryShader > {
...
};
class DomainShader : public Shader< ID3D11DomainShader > {
...
};
class HullShader : public Shader< ID3D11HullShader > {
...
};
class ComputeShader : public Shader< ID3D11ComputeShader > {
...
};
Of course you may decide to make load and bind virtuals, and to pack them into some kind of manager - this is just a design decision, whether you want to automate use of those classes. In such case design would get a little bit more complicated. My load is virtual, bind isn't. Additionally, VertexShader is strongly coupled with VertexLayout (ID3D11InputLayout), which is created while loading, so my structure looks something like this: