5

I would like to pass my class method as an argument to a (third-party) function (listner - that cannot be changed) that accepts a function pointer and a void*. Following is an example:

#include <functional>

typedef void(*pfnc) (void*);

struct Foo
{
    static void static_foo(void*)
    {
    }

    void foo(void*)
    {
    }

    void listner(pfnc f, void* p)
    {
       f(p);
    }

    void test()
    {
        listner(static_foo); // works with static method

        auto f = [](void*) {};
        listner(f); // works with lambda

        std::function<void(void*)> stdf = std::bind(&Foo::foo, this, std::placeholders::_1);
        listner(stdf);  // does not compile with not static method
    }
};

Unfortunately my solution does not compile. What do I have to change?

4
  • 1
    Function pointers don't have state. You're going to have to make use of the void* parameter. On the plus side, the parameter exists, unlike some other specific C APIs I've come across. Commented Oct 11, 2017 at 16:14
  • 1
    Are you sure that the listener method accepts only pfnc? Usually such methods accept a user specified void * parameter that is passed during the callback to the handler method... Commented Oct 11, 2017 at 22:11
  • I usually use a lambda that captures this to call the method. Commented Oct 12, 2017 at 6:52
  • @DanielTrugman you are right, going to update the function. Commented Oct 13, 2017 at 8:19

3 Answers 3

1

From the look of the callback signal, the listener API takes a pointer to void as 'user defined data'. You can pass this as the data and a small stateless proxy function to route to the handler on Foo:

typedef void(*pfnc) (void*);

struct Foo
{
    static void static_foo(void*)
    {
    }

    void foo()
    {
    }

    void listner(pfnc f, void* user_data)
    {

        // eventually calls
        f(user_data);
    }

    void test()
    {
        listner(static_foo, nullptr); // works with static method

        auto f = [](void*) {
        };
        listner(f, nullptr); // works with lambda

        listner([](void* pv)
        {
            reinterpret_cast<Foo*>(pv)->foo();
        }, this);
    }

};
Sign up to request clarification or add additional context in comments.

Comments

1

For the example in the question it would be possible to pass the member function pointer. But of course the instance on which that member is to be called must be known. If the calling function is also a member function then we can use this to call the member function passed via the pointer.

struct Foo
{
    static void static_foo(void*)
    {
    }

    void foo(void*)
    {
    }

    void listner(pfnc f)
    {
    }

    void listner(void(Foo::*f)(void*))
    {
        (this->*f)(nullptr); // call member function pointer for this
    }

    void test()
    {
        listner(static_foo); // works with static method

        auto f = [](void*) {};
        listner(f); // works with lambda

        listner(&Foo::foo); // pass pointer to member function
    }
};

Comments

0

Your example 'works' with a static member function because these are almost equivalent to plain old functions

It also 'works' with the lambda you provide because it is a capture-less lambda which can be converted to a function pointer (cf Passing lambda as function pointer) It will not work with a lambda with a capture however, because, as chris said, function pointers don't have state, i.e. there is no place to store what would be captured.

The same goes for the bind. You can store it in a std::function because such an object has a state, but you cannot store it in a plain (stateless) function pointer.

If you're not convinced, have a look at what stdf.target_type() tells you and compare it with typeid(static_foo).name() You can also have a look at the return type of std::bind

In the end, as others have said, you will have to embark your object in the user data, that is, in the void*. It could be worth investigating whether you can wrap that in a helper function taking an std::function as a parameter...

Comments

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.