3

I'm thinking of the way how to call most base virtual function given a pointer to a base class and pointer to most base virtual function of that class. This call should happen in outher module, e.g. Invoker, this module shouldn't know anything about class and function it's calling.

Need to build event-emitting system that will not respect virtuality of the callbacks and call exactly that target's function which address was passed to Invoker.

I want to make mandatory for derived classes to use their own callbacks and subscriptions and in general don't do virtual callback in the hierachy. If client still needs something alike, he can try to use virtual handlers in non-virtual callback of the base class.

I tried all kind of casts. Doesn't help, maybe I use them wrong...

#include <iostream>

class A
{
public:
    virtual void Foobar() { std::cout << "A" << std::endl; }
};

class B : public A
{
public:
    virtual void Foobar() { std::cout << "B" << std::endl; }
};

using CallbackType = void(A::*)();

void Invoker(A* target, CallbackType function)
{
    (target->*function)();
}

int main()
{
    A a;
    B b;

    Invoker(&a, &A::Foobar);
    Invoker(&b, &A::Foobar);

    b.A::Foobar(); // how to make similar call inside Invoker(&b, &A::Foobar) ?

return 0;
}

The output will be:

A
B
A

I want Invoker somehow to call the most base function, so expected output is:

A
A
A
Remy Lebeau
  • 555,201
  • 31
  • 458
  • 770
Soup Endless
  • 439
  • 3
  • 10
  • 2
    You could achieve this, but it would be smelly. Why don't you simply move the parts of `A::Foobar` that you want into their own, non-virtual function? – alter_igel Aug 30 '19 at 19:16
  • @alterigel I'm just curios. I also intersting if this possible to statically restrict to pass virtual functions to the Invoker, but most intersting is to respect object thru the prism of base class's virtual table in runtime... – Soup Endless Aug 30 '19 at 19:20

3 Answers3

7

It's not possible to do this with a pointer to member function. Instead, a lambda would be more appropriate:

Invoker(&b, [](A& a) { a.A::Foobar(); });

You need to rewrite Invoker as a template so that it can accept both lambdas and pointers to members:

template <class F>
void Invoker(A* target, F&& function) {
    std::invoke(std::forward<F>(function), *target);
}
Brian Bi
  • 111,498
  • 10
  • 176
  • 312
  • Can this be achived without lambda? Also can I somehow statically restrict to pass virtual functions to ```Invoker```? Thank a lot! – Soup Endless Aug 30 '19 at 19:31
  • 2
    @SoupEndless As I said, you can't do it using a pointer to member function. You need something that can contain the `.A::Foobar()` syntax, hence a lambda. For detecting virtuality at compile time, see https://stackoverflow.com/q/22911112/481267 – Brian Bi Aug 30 '19 at 19:33
  • That's very kind of you. Actually I love lambda solution, I will use it. But is there some other way? Just for fun? – Soup Endless Aug 30 '19 at 19:41
  • 2
    @SoupEndless I really think that the only way to do it is to pass to `Invoker` a piece of code that contains the `.A::Foobar()` syntax. If you weren't passing a lambda, you'd have to pass some other function pointer or function-like object that would do a similar thing. – Brian Bi Aug 30 '19 at 19:42
2

From what I know, it's impossible.

You can't call a virtual function in a non-virtual manner through a member function pointer.

HolyBlackCat
  • 78,603
  • 9
  • 131
  • 207
1

Well, the lambda solution presented in the other answer is indeed the way to go.

However, I found a way to do what you want ... sort of. The downside is that it involves a slicing copy of the object:

void Invoker(A* target, CallbackType function)
{
    (static_cast<A>(*target).*function)();
}

or

void Invoker(A target, CallbackType function)
{
    (target.*function)();
}
bolov
  • 72,283
  • 15
  • 145
  • 224
  • I didn't mentined that because because this is sensless from event-emitting POV, but this is very good example of what it feels like to me. – Soup Endless Aug 30 '19 at 19:33