3

DISCLAIMER I DO NOT USE BOOST OR OTHER LIBRARIES

Finally I've learned how PointerToMemberFunction works. This is my example code.

#include <iostream>

using namespace std;

class Foo
{
        public:
                void foo ( )
                {
                        cout << "I'm a foo method\n";
                };
};

class Bar
{
        public:
                void bar ( Foo* fooPtr , void(Foo::*fooFnPtr)() )
                {
                        (fooPtr->*fooFnPtr)();
                };
};

int main()
{
        Foo* foo = new Foo();
        Bar* bar = new Bar();

        bar->bar ( foo , &Foo::foo );

        return 0;
}

Now, what the problem is. Bar::bar must be modified somehow, because in real project it won't know, what class fooFnPtr is a pointer to. In other words Bar::bar must work with any class, not only with Foo. I won't know, a pointer to an instance of what class is passed to Bar::bar.

The one thing which can help is that all classes which will work with Bar::bar are children of one class!

Is this achievable and how? How do i fix my code? Thanks in advance!

Kolyunya
  • 5,973
  • 7
  • 46
  • 81
  • Use std::function as the argument, and when passing the actual parameter use std::bind to bind a member function with an object pointer/reference/value. – Igor R. Aug 24 '12 at 14:09
  • 1
    There are 1billion dupes of this problem. – Puppy Aug 24 '12 at 14:30

2 Answers2

9

You could make bar a template function:

template<class T>
void bar ( T* fooPtr , void(T::*fooFnPtr)() )
{
    (fooPtr->*fooFnPtr)();
}

Of course, if you only want to pass pointers to members that exist in the common base class, you can simply do this:

#include <iostream>

using namespace std;

class Foo
{
        public:
                virtual void foo ( )
                {
                        cout << "I'm a foo method\n";
                };
};

class Derived: public Foo
{
        public:
                virtual void foo ( )
                {
                        cout << "I'm a Derived method\n";
                };
};


class Bar
{
        public:
                void bar ( Foo* fooPtr , void(Foo::*fooFnPtr)() )
                {
                        (fooPtr->*fooFnPtr)();
                }
};

int main()
{
        Derived* derived = new Derived();
        Bar* bar = new Bar();

        bar->bar ( derived , &Foo::foo );

        return 0;
}
Henrik
  • 23,186
  • 6
  • 42
  • 92
  • +1 for the template approach. Regarding the inheritance case, it will only work if the function if present in the base, so the base must offer the full interface, and cannot be just a *tag* – David Rodríguez - dribeas Aug 24 '12 at 14:29
  • @DavidRodríguez-dribeas yes, this is why I wrote *if you only want to pass pointers to members that exist in the common base class* – Henrik Aug 24 '12 at 14:31
  • The only real answer to this problem is `std::function`. The template is a nice touch, but is only good for templatable functions, as it were, and won't take lambdas or other function objects. – Puppy Aug 24 '12 at 14:33
  • @DeadMG and why do you think the OP needs lambdas or function objects? – Henrik Aug 24 '12 at 14:35
  • Because it always progresses that way. More relevantly, there's a reason why `std::function` is Standard now, and it's because *it's the right solution to the problem*. – Puppy Aug 24 '12 at 14:39
  • @DeadMG: The question title is quite explicit: *Pointer to member function of an UNKNOWN CLASS* Also, while the simple example in the question could be done with a `std::function` where the instance and the member are bound together, in real code there might be multiple uses of the object before or after, or even multiple uses of the pointer to member. Finally, if there is no need for a more generic approach, using `std::function` will incur a (minimal) overhead. Minimal as it might be, if there is no need, *why pay for it*? – David Rodríguez - dribeas Aug 24 '12 at 14:40
  • Using the instance or the member after using `std::bind` is perfectly safe and legitimate. If you want to bind to a reference, `std::ref` can do that. And there is always a case for a more generic approach when it has *reduced* code complexity and no evidence of a performance problem. – Puppy Aug 24 '12 at 14:43
  • Minimal is good, I agree with @DavidRodríguez-dribeas. But in the other hand, it is important to name to somebody who has "just learned" function pointers which one is the more painless way to solve the problem, the one that will work well and for good, is easy, scalable in compiling time (that is, without having to move all your code to headers), and that should be used 99% of the time if you are just learning and in danger of being scared away by the intrinsic complexity of C++. Not using libraries is something for people with very specific requirements, not newcomers. – dsign Aug 24 '12 at 14:58
  • The trick to function pointers is to not use them unless it's compatibility with a crappy API. – Puppy Aug 24 '12 at 15:01
  • 1
    @DeadMG: I pretty much dislike blank statements as that, because there are correct uses of pointer to members and function pointers. Note that the issue is not recommending people to avoid them, but stating that they are to be avoided always. This to me is akin to the blank statement *an array is a pointer*, which holds true for most uses of an array (any use as an rvalue) but blindly misleads people. And BTW, you might not have realized that you are calling the standard library a *crappy* API – David Rodríguez - dribeas Aug 24 '12 at 15:07
  • Last I checked, the Standard library uses function objects, not function pointers, except for the objects which are helpers for crappy APIs. Also, the Standard library is a crappy API in many, many, many ways. – Puppy Aug 25 '12 at 15:12
1

I guess you are after closures and delegates, or the equivalent in C++. In short, they are instances that can be called, and that contain both the pointer to your class instance (or the instance itself) and the pointer to the method there.

Take a look to std::bind. They normally return cryptic instances of template classes, that are all convertible to std::function<...>, in case you need to use an uniformly-typed argument as parameter of a function or method.

So, your code would look like this:

    void Bar::bar(std::function<void()> f)
    { 
       ....
    }

    Foo* foo = new Foo();
    Bar* bar = new Bar();
    auto f = std::bind( &Foo::foo, foo);
    bar->bar ( f );

Of course, if you have decided not to use any library, you might nonetheless take a look at how those libraries have solved the problem before, so that you can reinvent the wheel in the best possible way.

dsign
  • 12,340
  • 6
  • 59
  • 82
  • `std::function` and `std::bind` are the only real solution to this problem. – Puppy Aug 24 '12 at 14:39
  • What about a lambda? Personally I think they're much cleaner to read than binds. – bstamour Aug 24 '12 at 18:15
  • Lambdas are great, but -- discounting that they generate an unnamed type just like bind -- I find that they obscure the intention if what you are doing is just binding together a function pointer and an instance. – dsign Aug 24 '12 at 21:50