Enforcing inherited classes without pure virtual functions?

Started by
8 comments, last by Lil_Lloyd 11 years, 3 months ago

Hi again dear community, I have another conundrum for which that I request your counsel. Heavy sounding introductions aside, I think this should be an easy to answer question.

In the realms of OOP there is a tendency to 'over generalise'. I like to use abstract classes but to derive super specific use cases from them. In this case, I have a 'static mesh' class that I'm rather proud of, because it's one of the few things I've coded myself that works as expected. I'm sure many programmers have similar classes in their collection.

Up until now I've been merely playing around with the staticMesh class, setting shaders and shader uniforms in calling code and then calling the 'render' function from the calling code. However, now that my codebase is scaling up in terms of sophistication, I would like to create specific classes that inherit from staticMesh like "palmTreeMesh" that will use a vertexBasedLighting shader (no significant specular term to speak of)

and so will use a render function as follows:


//pseudo code!

void PalmTree::Render()
{
  glUseProgram(texturedShader->getHandle());
  texturedShader->UpdateUniforms("MVPMatrix",someValue);
  texturedShader->UpdateUniforms("Time",someValue);

  StaticMesh::Render();
}

StaticMesh::Render() has no business having its code cut and pasted into several different files, it's great how it is.

So the main crux of my inquiry is thus:

StaticMesh::Render is not 'pure virtual' so static mesh is not a abstract class. However I'd like to enforce use of inherited, specific meshes

and not messy, lazy code where everything is done in a calling class. How do I do this? Just use a pure virtual 'stub' like so:


class BaseClass
{
public:
  virtual void DoSomething();
  virtual void Stub() = 0;
};

class Derived : public BaseClass
{
public:
  void DoSomething();
  void Stub() {};      //declaration and definition of 'stub'
};

Or is there another alternative to this? Thanks in advance.

Advertisement
Here's one common approach:
class Widget
{
public:
    void DoSomeStuff()
    {
        CommonBeginningStuff();
        InternalStuff();
        CommonFinishingStuff();
    }

protected:
    virtual void InternalStuff() { }

private:
    void CommonBeginningStuff();
    void CommonFinishingStuff();
};

class SpecificWidget : public Widget
{
protected:
    virtual void InternalStuff()
    {
        // Specificity goes here!
    }
};

Wielder of the Sacred Wands
[Work - ArenaNet] [Epoch Language] [Scribblings]

You could make the base class constructor protected; then only inherited classes can access the base class' constructor, so no non-derived classes can construct the base class. Constructing a MyBase directly then gives the warning: "'MyBase::MyBase()' is protected".

Another way would be to mark the destructor of StaticMesh pure virtual, but also provide an implementation.


class StaticMesh
{
   virtual ~StaticMesh() =0
   {}
}

This forces people to implement derived classes for StaticMesh.

Yet another option would be to mark StaticMesh::Render as pure virtual (again leaving the implementation of it intact). This forces inheritors to override Render but still allows them to call the base implementation, although some people will find this confusing.

Personally, I'd go with ApochPiQs solution as it communicates the intent most clearly.

if you think programming is like sex, you probably haven't done much of either.-------------- - capn_midnight
Up until now I've been merely playing around with the staticMesh class, setting shaders and shader uniforms in calling code and then calling the 'render' function from the calling code. However, now that my codebase is scaling up in terms of sophistication, I would like to create specific classes that inherit from staticMesh like "palmTreeMesh" that will use a vertexBasedLighting shader (no significant specular term to speak of)

I'm assuming that this is an intentionally contrived example, but on the off chance it isn't...

Inheritance is generally speaking the wrong tool for the job in this case. It's not terribly useful to say that a PalmTree is a mesh, one would prefer to think of it as PalmTree has a StaticMesh and a Shader - i.e. composing multiple objects, rather than inheriting from either one.

This let's you get away from the overgeneralisation. A StaticMesh is solely responsible for drawing it's own geometry, a Shader is solely responsible for setting graphics state, and so on...

Tristam MacDonald. Ex-BigTech Software Engineer. Future farmer. [https://trist.am]

Another way would be to mark the destructor of StaticMesh pure virtual, but also provide an implementation.

...

Yet another option would be to mark StaticMesh::Render as pure virtual (again leaving the implementation of it intact). This forces inheritors to override Render but still allows them to call the base implementation, although some people will find this confusing.

That's pretty odd! Is that valid C++? huh.png

Another way would be to mark the destructor of StaticMesh pure virtual, but also provide an implementation.

...

Yet another option would be to mark StaticMesh::Render as pure virtual (again leaving the implementation of it intact). This forces inheritors to override Render but still allows them to call the base implementation, although some people will find this confusing.

That's pretty odd! Is that valid C++? huh.png

Sure. There's nothing with pure virtual functions that stops you from providing an implementation and calling it.

In fact, a pure virtual destructor is required to have an implementation and to have it called. Not required by the language syntax, but a derived class' destructor will call its base class' destructor. If you don't provide an implementation for the destructor, you'll get an undefined external symbol linker error.

That's pretty odd! Is that valid C++? huh.png

Sure. There's nothing with pure virtual functions that stops you from providing an implementation and calling it.

In fact, a pure virtual destructor is required to have an implementation and to have it called. Not required by the language syntax, but a derived class' destructor will call its base class' destructor. If you don't provide an implementation for the destructor, you'll get an undefined external symbol linker error.


That was my opinion as well. I'd read before that it was valid, just obscure, but I just checked my copy of the C++03 standard and now I'm not sure.

C++ Standard - section10.2
[Note:a function declaration cannot provide both a pure-specifier and a definition—end note]
[Example:

struct C {
virtual void f() = 0 { }; //ill-formed
};
—end example]

Any language standard experts care to clarify?

if you think programming is like sex, you probably haven't done much of either.-------------- - capn_midnight
It means that you cannot put both the definition and the pure specifier on the declaration, not that a pure function cannot be defined. Solution:

struct C {
    virtual void f() = 0;
};

void C::f() {
}
edit: And yeah, that was new to me, actually. I thought you could do both, but MSVC doesn't complain about it even with language extensions disabled. Looks like G++ is doing the right thing though.

I've followed swift coder's advice - namely PalmTree HAS a mesh and a shader. Actually makes everything less of a headache than what I was trying. Interesting discussion though!

This topic is closed to new replies.

Advertisement