Thread: avoiding compulsory call to superclass

  1. #1
    Registered User
    Join Date
    Jan 2007
    Posts
    330

    avoiding compulsory call to superclass

    I have an Animation class which is designed so that clients can create subclasses for their own custom animations. A client fills in the move logic of the animation in the Update function. But what every client *must* know is first to call the Update function of the superclass because there the time is updated that every animation needs.


    Code:
    /*virtual*/ Status CAnimation::Update(unsigned long ElapsedTime)
    {
    	m_TimeElapsed	+= ElapsedTime;
    
    	return CONTINUE;
    }
    
    Status CWinlineAnimation::Update(unsigned long TimeElapsed)
    {
        Status	RetVal	= CONTINUE;
    
        CAnimation::Update(TimeElapsed);
    
    //   ...... move logic
    }
    Is there a way that I can make sure that CAnimation::Update is called without forcing every client to remember calling Update first?

  2. #2
    Confused Magos's Avatar
    Join Date
    Sep 2001
    Location
    Sweden
    Posts
    3,145
    Make the custom animating method a protected abstract method. The call is always made to the base method, so rules like always updating the time is always made.
    Code:
    class Base
    {
      public void Update()
      {
        PrepareTime();
        Animate();
      }
    
      protected abstract Animate()
    }
    
    class Sub : Base
    {
      protected Animate()
      {
        //Custom animation code here
      }
    }
    
    Sub s = new Sub();
    s.Update();
    (code is C#-ish, you can translate to equivalent C++)
    MagosX.com

    Give a man a fish and you feed him for a day.
    Teach a man to fish and you feed him for a lifetime.

  3. #3
    Registered User
    Join Date
    Oct 2008
    Posts
    1,262
    No. And this, in my opinion, is one of the most disappointing things in C++. It can be very useful in some situations, but C++ just doesn't provide an easy way to do it. Constructors/destructors do it; but that's the only thing that can.

    One way to bypass it is not to make update virtual, but another function (subUpdate? My naming conventions are my weakness ;-). Then call update, which will call the base class update function. This base class update function then calls this->subUpdate(); which will be the function that will update the subclass specifics.
    (Edit: So, yes, what magos said)

  4. #4
    Registered User
    Join Date
    Jan 2007
    Posts
    330
    I think I found it. You can do call the Update function from the superclass directly like this:

    Code:
    Anim->CAnimation::Update(TimeElapsed);
    So I make sure that you always call update through a higher level animationlist which calls Update of CAnimation first and then of the subclass.

    Code:
    void CAnimationList::Update(unsigned long TimeElapsed)
    {
        for (AnimIter Anim = m_RenderList.begin(); 
              Anim != m_RenderList.end(); ++Anim)
       {
          Anim->CAnimation::Update(TimeElapsed);
          Anim->Update(TimeElapsed);
       }
    }

  5. #5
    Deprecated Dae's Avatar
    Join Date
    Oct 2004
    Location
    Canada
    Posts
    1,034
    No you'd call the Update function from the superclass normally. Use class Sub : public Super. Then from Update in the superclass you call the subclass functions, ie. Animate or Update2 if you will. It should be protected since you're only conforming to the superclass interface, the subclass just provides the operations.

    Code:
    class CAnimation
    {
    public:
      Status Update(unsigned long ElapsedTime)
      {
        m_TimeElapsed	+= ElapsedTime;
    
        return this->Animate(m_TimeElapsed);
      }
    
    protected:
      virtual Status Animate(unsigned long) = 0;
    };
    
    class CWinlineAnimation : public CAnimation
    {
    protected:
      virtual Status Animate(unsigned long TimeElapsed)
      {
        Status	RetVal	= CONTINUE;
    
      //   ...... move logic
      }
    };
    
    
        for (AnimIter Anim = m_RenderList.begin(); 
              Anim != m_RenderList.end(); ++Anim)
       {
          Anim->Update(TimeElapsed);
       }
    I think that's it.... nope, now maybe
    Last edited by Dae; 09-07-2009 at 05:27 AM.
    Warning: Have doubt in anything I post.

    GCC 4.5, Boost 1.40, Code::Blocks 8.02, Ubuntu 9.10 010001000110000101100101

  6. #6
    Registered User
    Join Date
    Jan 2007
    Posts
    330
    I dont see why you think my solution is not correct?

  7. #7
    Deprecated Dae's Avatar
    Join Date
    Oct 2004
    Location
    Canada
    Posts
    1,034
    Quote Originally Posted by KIBO View Post
    I dont see why you think my solution is not correct?
    I don't (think it's not correct). I thought you were trying to implement Magos' suggestion, which would be what I posted. What you have probably works, although it's usage a bit cryptic (hope you're the only user). I'd prefer what you have originally over that.
    Warning: Have doubt in anything I post.

    GCC 4.5, Boost 1.40, Code::Blocks 8.02, Ubuntu 9.10 010001000110000101100101

  8. #8
    Cat without Hat CornedBee's Avatar
    Join Date
    Apr 2003
    Posts
    8,895
    Quote Originally Posted by KIBO View Post
    I dont see why you think my solution is not correct?
    You've moved something that is the responsibility of the superclass (making sure that the time is updated) to the caller of the function. That is pretty much the worst place (from a design standpoint) you can put it.

    The correct solution is Dae's, and it even has a name: the non-virtual interface pattern (NVI). The rule is, you never make virtual functions public. No matter how trivial, you always make a public non-virtual function that is a wrapper around the virtual function, which is protected or even private. (Yes, private works.) This allows the base class to add functionality without requiring the subclass to add the explicit call to the inherited function.
    All the buzzt!
    CornedBee

    "There is not now, nor has there ever been, nor will there ever be, any programming language in which it is the least bit difficult to write bad code."
    - Flon's Law

  9. #9
    Registered User
    Join Date
    Jan 2007
    Posts
    330
    Very interesting. I didnt know about that pattern yet so thanks!

    however, I think I came up with something that is kind of the same with a little twist. A CAnimation is designed to be only used by CAnimationList and CAnimationManager. CAnimationManager manages CAnimationLists with a ZOrder. So I made all the virtual functions private and I made CAnimationList and Manager a friend. The idea is that the programmer maintaining CAnimationList *must* know the inner workings of CAnimation anyways so he has to remember to update the time.

    Code:
    class CAnimation {
    	friend class CAnimationList;
    	friend class CAnimationManager;
    
    public:
    	explicit CAnimation(unsigned long Timer);
    
    	virtual ~CAnimation();
    
    private:
    	virtual Status Init() = 0;
    
                    virtual void Render() = 0;
    
    	virtual Status Update(unsigned long TimeElapsed) = 0;
    
    	virtual void Cleanup() = 0;
    
    	virtual bool IsDone();
    };
    Then Update of CAnimationList:

    Code:
    void CAnimationList::Update(unsigned long TimeElapsed)
    {
      for (AnimIter Anim = m_RenderList.begin(); Anim != m_RenderList.end();  )
      {
         (void)(*Anim)->CAnimation::Update(TimeElapsed);
    
         if ((*Anim)->Update(TimeElapsed) == CAnimation::DONE)
        {
          (*Anim)->Cleanup();
          Anim = m_RenderList.erase(Anim);
        }
        else
           ++Anim; // only increment when erase is not called
        }
    }
    and CAnimationManager call Update for every CAnimationList from lowest Z-Order to highest: (the list is sorted)

    Code:
    void CAnimationManager::Update(unsigned long TimeElapsed)
    {
      for_each(m_lists.begin(), m_lists.end(), 
                     bind2nd(mem_fun_ref(&ContainedType::Update), TimeElapsed));
    }

    I'm kinda happy with this design but when I get time Ill check out and maybe implement the non virtual interface pattern exactly. The only difference I see here is that the interface is still private but I only give access to the classes that must know how to talk to the class.

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. Error C2664 - Trying to call an external Dll
    By jamez05 in forum C++ Programming
    Replies: 3
    Last Post: 08-08-2006, 06:07 AM
  2. Assembly example
    By Lynux-Penguin in forum C Programming
    Replies: 6
    Last Post: 04-24-2002, 07:45 PM
  3. Help needed with external call, WinExec() and timer.
    By algorithm in forum Windows Programming
    Replies: 9
    Last Post: 11-07-2001, 09:35 AM
  4. Pls help me to do this project in C I need source code
    By sureshmenon74 in forum C Programming
    Replies: 4
    Last Post: 10-04-2001, 06:57 AM
  5. call by reference and a call by value
    By IceCold in forum C Programming
    Replies: 4
    Last Post: 09-08-2001, 05:06 PM