I'm trying to implement a shader class. It should handle communicating with the graphics API, loading and compiling the shader files, and sending variables to the active shader. I've set myself some few goals for how the implementation should be written:

  • Shader variables should be class members. The class user should change shader variables by calling
    Code:
    shaderInstance.time.set(value)
    not
    Code:
    shaderInstance.setVariable("time", value)
    This is to avoid misspelling errors.
  • No code repetition when adding more variables to shader class. The shader variables should only be defined in one place, so that when adding another one to the code, I cannot forget to also add it to that other place (an example of not satisfying this would be having the variables as members, but adding them to some kind of list in the Shader constructor. When adding a new one, i would have to add it both to the header, and in the constructor, therefore that solution would not satisfy this criterion.).
  • The shader does need to iterate over the variables. This is in order to resend variables (AKA uniforms) to the shader, in case they've changed. And this is what makes things a bit tricky.

I should also note that the variables are in themselves not just plain data members, they are objects that contain state like "changed or not", functions for dealing with the graphics API, and more.

So, my current implementation:

"Uniform" class for the variables to be sent to graphics API. Shader has instances of these as members, and passes their name-in-the-shader-file (needed for graphics API) and reference to itself (the shader instance) to the Uniform constructor. This is all in the declarations in the header file. Code sample (inside Shader.h):
Code:
public:
    UVar<float>::Ptr time = UVar<float>::create("time", *this);
    // This wouldn't compile pre-C++11, I think.
I'm kind of implementing an Observer Pattern here, or at least I think I am. Shader has a list of observer references, and Uniform constructor adds a reference to itself to the observer list of the shader reference it was passed as parameter*.

(This is the part I deem not elegant and that I'd like to change, but I can't find any other way to do it while respecting the goals I set. I also don't like it because the uniform constructors reference the Shader instance before it has been constructed fully, which makes it necessary to put their declarations after the observer list declaration in the class declaration, risking a bug that's easy to miss.)

Later on, in the Shader constructor, after the shader itself has been compiled and linked, the registered observers (so, the class members of Uniform type) are iterated over and initialized (calling their own init function, which just talks to the graphics API and gets some important values). After that the shader is fully constructed. Now, the outside user may at any time call for example
Code:
shaderInstance.screenResolutionX.set(1920);
to set the value of that particular variable/uniform**.

The actual question: Is there a better way to solve this short of macros? Are my goals unreasonable, if so, where should they be changed? Is something I'm doing completely stupid?

Generally, any criticisms of my solution or requests for clarification are welcome.




*Here is some code to clarify:
Code:
Uniform::Uniform(std::string name, Shader& subject): name(name)
{
    subject.registerObserver(Ptr(this));
    this->name = name;
}
**The value is not actually sent to OpenGL just then, but flagged as "to be sent" in the Uniform. Before every draw call "sendUniforms" is called on the shader instance, which makes the shader iterate over all its observers and tell them now is the time to actually send.