Sign in to follow this  
zyrolasting

[C++] Parent to derived method

Recommended Posts

zyrolasting    280
Each derivative of my base class updates the state of the object once per frame though one virtual method. I need to invoke parent implementations before derived ones. (Imagine a generic camera object needing to update before the FirstPersonCamera object)
#include <iostream>
using namespace std;

struct A { virtual void foo() { cout << "Hello, "; } };

struct B : A
{
	B() { foo(); }

	void foo()
	{
		// Need parent impl first
		__super::foo();
		cout << "World!" << endl;
	}
};

int main()
{
	B b; // Hello, World!
	return 0;
}
Note that while the code above does what I want, I don't want to explicitly call parent implementations. Is it possible to automate calls from the top level class down to the most derived?

Share this post


Link to post
Share on other sites
nullsquared    126
Quote:
Original post by zyrolasting
Can I get details on why?


How do you expect to do it?

C++ provides a method of calling the parent class's implementation *if you need to*. There is no way to make it so that the parent class's implementation is called automatically. What if it has some sort of parameters, or a special return value?

Share this post


Link to post
Share on other sites
Antheus    2409
Quote:
Original post by zyrolasting
Can I get details on why?


That's the way language is designed.

One problem is multiple inheritance (diamond inheritance in particular). In which order are the functions called?

A better solution:
struct Base {
void update() {
before();
specific_update();
after();
}
protected:
virtual void specific_update() = 0;
private:
void before();
void after();
};

Share this post


Link to post
Share on other sites
Sneftel    1788
This is impossible. It cannot be done, period... and beyond that, should not be done. Keeping that firmly in mind...


class A
{
protected:
class BaseToken // only A can create this
{
private:
BaseToken() { }
friend class A;
};
public:
// override it if you like, but you've gotta get a BaseToken from somewhere
virtual BaseToken f() {
// doStuff();
return BaseToken();
}
};

class B : public A
{
public:
virtual BaseToken f()
{
BaseToken b = A::f();
// doOtherStuff();
return b;
}
};


Incidentally, this is still prone to extreme circumvention measures: B could cache the token the first time around, then reuse it for subsequent invocations without calling the base function. Nevertheless, it does have one significant advantage over the inversion method Antheus described: With some modifications, it can enforce a call chain having more than two levels.

Share this post


Link to post
Share on other sites
zyrolasting    280
There is only one method I wish to do this for, and the return codes merely add to a counter. It didn't seem hard to do at first. I managed to automate calls from parent to child, although I do need to allocate an instance of every object type once and need to know the very method beforehand. (which I do). There's just no way to improve on the below, huh? (Dependant on Loki typelist)

#include <iostream>
#include <typelist.h>
using namespace std;
using namespace Loki;

struct A
{
virtual void foo()
{
cout << "Hello, ";
}
};

struct B : A
{
void foo()
{
cout << "World!" << endl;
}
};


template<class TList, class F>
struct automater;

template<class H, class T, class F>
struct automater<Typelist<H,T>,F>
{
automater()
{
H h;
F f = &H::foo;
(h.*f)();
automater<T,void (T::Head::*)()> n;
}
};

template<class H, class F>
struct automater<Typelist<H,NullType>,F>
{
automater()
{
H h;
F f = &H::foo;
(h.*f)();
}
};

int main()
{
automater<TYPELIST_2(A,B),void (A::*)()>();
system("pause");
return 0;
}

Share this post


Link to post
Share on other sites
Sneftel    1788
I'm not sure what problem that code snippet is trying to solve. Obviously if you're willing to just write out every path down the inheritance chain to derived classes (aka your typelist down there), there's no need to automate anything. Just have a function that calls A::foo and then B::foo.

Share this post


Link to post
Share on other sites
zyrolasting    280
My object hierarchies realistically can go several levels.

I.E.

Entity -> Enemy -> Normal -> Goomba
Entity -> Enemy -> Boss -> Bowser

Each derivative updates the object state appropriately. I would use more normal methods and drop this model altogether if I wasn't so damned tired of babysitting a Direct3D9 device.

The largest hierarchy I ever had was 5 levels, and each derivative needed to call a parent implementation. It's easy to see why I would want to make something generic out of this.

So, I need to call parent implementations explicitly. Alright. Thanks for your help.

Share this post


Link to post
Share on other sites
Antheus    2409
Quote:
Original post by zyrolasting
There's just no way to improve on the below, huh? (Dependant on Loki typelist)


It's one facet of fundamental problem with OOP, it doesn't model certain things well.

The problem here isn't related to polymorphism or inheritance, but about your particular design not being able to model the domain well.

The actual requirements here require function prolog and epilogue. Without all the template mess, your actual requirements are (most likely):
prolog();
func();
epilogue();
, but where such calls may be nested. This illustrates the order much better - there is no reason compiler should assume that one prolog must be called before another.

With typelists you don't really need, or want polymorphism, especially with value types, especially with this type of processing. Typelists know concrete type of each list member, so run-time polymorphism is redundant. Your case seems to be only using it to establish the previous concept.

OO works well with opaque interfaces: foreach (IFoo : foos) foo->something();

Anything else is effectively trying to subvert the mechanism, and relying on parent functions is discouraged since it breaks this. There have been various arguments that inherited members should never have access to anything but public interface, making such calls impossible in first place.

So instead consider reorganizing the above into concrete and strong interface:
struct A {
void prologa();
void epiloguea();

virtual void update();
};

struct B : A{
void prologb();
void epilogueb();

virtual void update(); // override
};

struct C : B {
void prologc();
void epiloguec();

virtual void update(); // override
};


This may seem redundant, but as far as design goes, it prevents many fallacies that occur. It does require you to call a specific function, but is cleaner in the long run, since it allows arbitrary inheritance without obscuring true intent.

Java's super, as trivial as it may seem, needs to enforce considerable and annoying constraints on when and how super may be called to avoid obscure (but surprisingly common and abusable) corner cases.

Quote:
The largest hierarchy I ever had was 5 levels, and each derivative needed to call a parent implementation


As mentioned, this is considered a "code smell", even in which Java has much simpler inheritance rules and many more guarantees. If you need to reuse functionality, consider exporting that into separate functions, and then call them directly. Unlike Java, this can be done somewhat simpler and perhaps even cheaper in C++. Free functions are often preferred, following the public-interface-only rule.

This type of calling the parent also wreaks havoc on dependencies and coupling, since calling things down the hierarchy can either break, or introduce expectations of invariants. When A<-B<-C and A<-B<-D are involved, changing B can break C but not D, meaning the entire dependency is coupled in a very undesirable way, since changes to C are seemingly unrelated to D.

Share this post


Link to post
Share on other sites
zyrolasting    280
@ Antheus

I have something like that. To make batching easier to implement, I group entities into subsets and have OnEventStart/End() methods surrounding the group. This makes D3D Begin()/End() pairs flow nicely with groups of entities. However, it still leaves the entity hierarchies as described. I certainly will try to do what you are suggesting in another form. I think I can see one way to take advantage of it.

Share this post


Link to post
Share on other sites

Create an account or sign in to comment

You need to be a member in order to leave a comment

Create an account

Sign up for a new account in our community. It's easy!

Register a new account

Sign in

Already have an account? Sign in here.

Sign In Now

Sign in to follow this