Jump to content

  • Log In with Google      Sign In   
  • Create Account


Enforcing inherited classes without pure virtual functions?


Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.

  • You cannot reply to this topic
9 replies to this topic

#1 Lil_Lloyd   Members   -  Reputation: 287

Like
0Likes
Like

Posted 16 January 2013 - 08:39 PM

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.

 



Sponsor:

#2 ApochPiQ   Moderators   -  Reputation: 14291

Like
2Likes
Like

Posted 16 January 2013 - 08:49 PM

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!
    }
};


#3 Servant of the Lord   Crossbones+   -  Reputation: 17144

Like
2Likes
Like

Posted 16 January 2013 - 09:10 PM

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".


It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.

[Fly with me on Twitter] [Google+] [My broken website]

All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.                                                                                                                                                       [Need free cloud storage? I personally like DropBox]

Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal


#4 ChaosEngine   Crossbones+   -  Reputation: 2140

Like
1Likes
Like

Posted 16 January 2013 - 09:35 PM

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

#5 swiftcoder   Senior Moderators   -  Reputation: 9603

Like
8Likes
Like

Posted 16 January 2013 - 10:45 PM

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 - Software Engineer @Amazon - [swiftcoding]


#6 Servant of the Lord   Crossbones+   -  Reputation: 17144

Like
0Likes
Like

Posted 17 January 2013 - 12:38 PM

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


It's perfectly fine to abbreviate my username to 'Servant' rather than copy+pasting it all the time.

[Fly with me on Twitter] [Google+] [My broken website]

All glory be to the Man at the right hand... On David's throne the King will reign, and the Government will rest upon His shoulders. All the earth will see the salvation of God.                                                                                                                                                       [Need free cloud storage? I personally like DropBox]

Of Stranger Flames - [indie turn-based rpg set in a para-historical French colony] | Indie RPG development journal


#7 Brother Bob   Moderators   -  Reputation: 7781

Like
1Likes
Like

Posted 17 January 2013 - 12:55 PM

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.



#8 ChaosEngine   Crossbones+   -  Reputation: 2140

Like
0Likes
Like

Posted 17 January 2013 - 04:26 PM

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

#9 Brother Bob   Moderators   -  Reputation: 7781

Like
1Likes
Like

Posted 17 January 2013 - 04:45 PM

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.

Edited by Brother Bob, 17 January 2013 - 04:48 PM.


#10 Lil_Lloyd   Members   -  Reputation: 287

Like
1Likes
Like

Posted 17 January 2013 - 06:14 PM

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!






Old topic!
Guest, the last post of this topic is over 60 days old and at this point you may not reply in this topic. If you wish to continue this conversation start a new topic.



PARTNERS