-1

I have a class Shader, in it's constructor there are statements to compile shader, bind attributes and then link the shader program. The problem is I'd want a child class to have different attributes, but that must happen before the linking. Virtual methods don't work here. What should I do?

Shader::Shader(const char* v, const char* f) {
    program = glCreateProgram();

    const char* vsrc = nullptr;
    const char* fsrc = nullptr;

    tls::readTextFile(std::ifstream(v, std::ios::binary), vsrc);
    tls::readTextFile(std::ifstream(f, std::ios::binary), fsrc);

    m_vertShader = compile(vsrc, GL_VERTEX_SHADER);
    m_fragShader = compile(fsrc, GL_FRAGMENT_SHADER);

    glAttachShader(program, m_vertShader);
    glAttachShader(program, m_fragShader);

    bindAttribs(); // it must happen before linking, in child class too.

    glLinkProgram(program);

    getUniforms();
    setUniforms();
}

and child class:

void BasicShader::bindAttribs() {
    bindAttribute(0, "pos");
    bindAttribute(2, "vt");
} // this method is not called
daedsidog
  • 1,732
  • 2
  • 17
  • 36
GPlayer
  • 131
  • 1
  • 7
  • Reopened. The question is **not** why virtual functions don't act the way someone expected, which is what the purported duplicate addressed. The questions is **what to do instead**. Not that there's a good answer to that... – Pete Becker Dec 26 '18 at 19:20
  • @Pete _"Not that there's a good answer to that..."_ Exactly. I could think of a _Factory Pattern_ however. – πάντα ῥεῖ Dec 26 '18 at 19:30

2 Answers2

2

The issue is that your constructor is monolithic. Split it up into a "pre-bake" protected constructor and a "finalize construction" helper function that does the linking (also protected). That way you can customize what happens during construction but before linking.

A more general approach would be a factory function.

rustyx
  • 80,671
  • 25
  • 200
  • 267
1

As far as I can see there are two options here:

1. Pass attributes to the constructor as a parameter

Shader::Shader(const char* v, const char* f,
    const std::vector<std::pair<int, std::string>> &attrs)
    /* Or some simpler type with the same functionality */
{
    ...
    for (auto &&attr : attrs)
        bindAttr(attr.first, attr.second);
    ...
}

2. Move all the initialization from the constructor into a separate method

bool Shader::initialize(const char* v, const char* f)
{
    // Here you actually can use virtual methods, yay!
    // ...and even report errors via return value (unless you were planning to use exceptions)
    return true;
}
r3mus n0x
  • 5,954
  • 1
  • 13
  • 34