9

As opposed to virtual member functions, I need a solution where a function implemented at each level class derivation can be registered for later call by the base class. ( Not just the most derived implementation)

To do this, I was thinking on providing a mechanism for derived classes to register their function with the base class such as during the derived class constructor.

I'm having trouble with the member function pointer argument though. I was thinking that Derived is derived from Base, the this pointer should be automatically casted.

Can this be done close to what I am trying or do I need to use static member functions, void *, and static_cast?

class Base
{
protected:
    typedef void (Base::*PrepFn)( int n );
    void registerPrepFn( PrepFn fn ) {};
}

class Derived : public Base
{
    Derived() {
        registerPrepFn( &Derived::derivedPrepFn );
    };

    void derivedPrepFn( int n ) {};

}

Compiler error:

error: no matching function for call to 'Derived::registerPrepFn(void (Derived::*)(int))'
note: candidates are:                 'void Base::registerPrepFn(void (Base::*)(int))'
NoahR
  • 1,347
  • 3
  • 16
  • 30
  • 1
    I'm not sure exactly what you want to do. But it's certainly not possible this way; a member function defined in `Derived` cannot be pointed to by a `Base::*`. Perhaps if you explain the top-level goal, someone might be able to suggest a better solution. – Oliver Charlesworth Apr 08 '12 at 21:37
  • 3
    FTR, `void*` and `static_cast` won't work either, because a pointer to member function is not a pointer (yes, it's horribly named). – R. Martinho Fernandes Apr 08 '12 at 21:41
  • regarding static cast, I was thinking I could implement static member functions and explicitly pass around the this pointer – NoahR Apr 08 '12 at 21:42
  • @R.MartinhoFernandes: Not really, `static_cast` will work. – jpalecek Apr 08 '12 at 21:43
  • @NoahR: But for that to work, you'd need to cast the `this` pointer to the right type before calling the static function, which means that the implementation of `Base` would need to be aware of all the possible types that the pointer could take on. – Oliver Charlesworth Apr 08 '12 at 21:43
  • Maybe you could find your answer here: http://stackoverflow.com/questions/10021062/member-function-pointers-and-inheritance/10021253#10021253 – jpalecek Apr 08 '12 at 21:44
  • 1
    @jpalecek http://ideone.com/QRGpt – R. Martinho Fernandes Apr 08 '12 at 21:45
  • So why did I set off this way? I have existing scheme where classes have a basic Construct phase coming out of a factory, then later a setup phase after more aspects of the application have been established. Each setup method of the derived classes produces a product that the parent classes do not know about. I wasn't trying to address the following aspect with this question, but it would be super to have a scheme that, like the constructor/destructor enforces that every derived level function must be called. – NoahR Apr 08 '12 at 21:51
  • @NoahR: It's still not 100% clear, but what you're describing sounds a bit like the [template pattern](http://en.wikipedia.org/wiki/Template_method) (aka the "non-virtual interface pattern"). – Oliver Charlesworth Apr 08 '12 at 21:53
  • @R.MartinhoFernandes: Obviously, you can't cast to `void*`, but if you cast to `void (Base::*)(int)`, it will do for OP (as I understand it). – jpalecek Apr 08 '12 at 21:54
  • i'll sketch out the static member function approach... – NoahR Apr 08 '12 at 21:57
  • static function use like this works I guess. http://ideone.com/2LdQz but @jpalecek 's static_cast is much more straightforward. So does one have better relative merit? As jpalacek cautions, of course the programmer would only static cast when source is a derived. – NoahR Apr 08 '12 at 22:25

2 Answers2

15

If all you need is beating the error message, then casting will do:

class Derived : public Base
{
    Derived() {
        registerPrepFn( static_cast<PrepFn>(&Derived::derivedPrepFn) );
    };

    void derivedPrepFn( int n ) {};

}

Call it normally with a Base* p (provided it actually points to a Derived): (p->*registered)(0)

See http://ideone.com/BB9oy for a working example.

jpalecek
  • 45,889
  • 7
  • 97
  • 139
  • well, I don't want to just suppress the error message. Will this be functional? – NoahR Apr 08 '12 at 21:59
  • @NoahR: Yes, it will work (but beware of the condition in boldface) – jpalecek Apr 08 '12 at 22:02
  • 1
    The Standard has this to say on such a conversion: "If class B contains the original member, or is a base or derived class of the class containing the original member, the resulting pointer to member points to the original member. Otherwise, the result of the cast is undefined." I fear that such a cast either won't do what the OP wants at best, or doesn't produce a useful value at worst. – Luc Danton Apr 08 '12 at 22:36
  • 2
    @LucDanton: "If class B ... *is a base or derived class of the class containing the original member* ...". That condition is fulfilled if you call it with an object of dynamic type D and the pointer is to a member of D, which means it does what everybody wants (explicitly stated by the note that follows your quote). It only breaks if the member is contained in an unrelated class of `B` (can happen with multiple inheritance, when the pointer to `int D::*` is actually an `int A::*` and `A` is unrelated to `D`) – jpalecek Apr 08 '12 at 22:45
  • I'm lost in the spec, but I prototyped @jpalecek's idea and it seems to work with just one derived class: http://ideone.com/R67eZ – NoahR Apr 08 '12 at 22:46
  • ... I meant "`A` is unrelated to `B`" in the previous post, not `D`. – jpalecek Apr 08 '12 at 22:55
  • Thanks @jpalecek the static cast syntax seems to be the way to go for what I'm starting with. If all were being created from scratch, a different object design might be best. Between this and the static member function approach, this seems to be much more compact for the same effect. – NoahR Apr 08 '12 at 22:59
0

This is not allowed with oop. Behavioural switching is accomplished by polymorphing the object's class at object creation time.

If you need post-object-creation behaviour switching, you might refactor the dynamic behaviour to another set of polymorphic classes and hold a "pointer" to an instance of a class with the correct behaviour. Please Google the "decorated class" software pattern.

scorpdaddy
  • 274
  • 2
  • 13