最新消息:雨落星辰是一个专注网站SEO优化、网站SEO诊断、搜索引擎研究、网络营销推广、网站策划运营及站长类的自媒体原创博客

c++ - Assign a non-static member function as a same class member variable, which is a function pointer - Stack Overflow

programmeradmin3浏览0评论

The example snippet:

#include <iostream>
#include <functional>
#include <vector>

// Simulated callback type
using CallbackType = void(*)(void*, uint32_t);

class DataProcessor{
public:
    void process(void* data) {
        //process data
        std::cout << "process data" << std::endl;
    }
};
class ExampleClass {
public:
    ExampleClass() {
        // Initialize the callback with a lambda that wraps the member function
        callback = [this](void* data, uint32_t id) { this->memberFunction(data, id); };
    }

    void executeCallback(void* data, uint32_t id) {
        if (callback) {
            callback(data, id);
        }
    }

private:
    void memberFunction(void* data, uint32_t id) {
        std::cout << "Member function called with id: " << id << std::endl;
        // Process data...
        processor.process(data);
    }

    CallbackType callback;
    DataProcessor processor;
};

int main() {
    ExampleClass example;
    int dummyData = 42;
    example.executeCallback(&dummyData, 1);

    return 0;
}

I need to assign a member function to a function pointer void(*)(void*, uint32_t); as shown in the above code. The memberFunction need to be non-static, as it also needs to access a non-static member field processor. But the lambda I assigned to field callback gives error, any way to work around this issue?

compiler error

error: cannot convert ‘ExampleClass::ExampleClass()::<lambda(void*, uint32_t)>’ to ‘CallbackType’ {aka ‘void ()(void, unsigned int)’} in assignment 19 | callback = [this](void* data, uint32_t id) { this->memberFunction(data, id); };

Be noted, using CallbackType = void(*)(void*, uint32_t); is external code I cant change it.

The example snippet:

#include <iostream>
#include <functional>
#include <vector>

// Simulated callback type
using CallbackType = void(*)(void*, uint32_t);

class DataProcessor{
public:
    void process(void* data) {
        //process data
        std::cout << "process data" << std::endl;
    }
};
class ExampleClass {
public:
    ExampleClass() {
        // Initialize the callback with a lambda that wraps the member function
        callback = [this](void* data, uint32_t id) { this->memberFunction(data, id); };
    }

    void executeCallback(void* data, uint32_t id) {
        if (callback) {
            callback(data, id);
        }
    }

private:
    void memberFunction(void* data, uint32_t id) {
        std::cout << "Member function called with id: " << id << std::endl;
        // Process data...
        processor.process(data);
    }

    CallbackType callback;
    DataProcessor processor;
};

int main() {
    ExampleClass example;
    int dummyData = 42;
    example.executeCallback(&dummyData, 1);

    return 0;
}

I need to assign a member function to a function pointer void(*)(void*, uint32_t); as shown in the above code. The memberFunction need to be non-static, as it also needs to access a non-static member field processor. But the lambda I assigned to field callback gives error, any way to work around this issue?

compiler error

error: cannot convert ‘ExampleClass::ExampleClass()::<lambda(void*, uint32_t)>’ to ‘CallbackType’ {aka ‘void ()(void, unsigned int)’} in assignment 19 | callback = [this](void* data, uint32_t id) { this->memberFunction(data, id); };

Be noted, using CallbackType = void(*)(void*, uint32_t); is external code I cant change it.

Share Improve this question edited Mar 17 at 12:05 Weijun Zhou 5,2562 gold badges21 silver badges41 bronze badges asked Mar 17 at 11:21 tesla1060tesla1060 2,8037 gold badges35 silver badges45 bronze badges 8
  • Please edit and post the error message you get. Anyway - only a capture-less lambda can be converted to a function pointer. You can make CallbackType a std::function instead. – wohlstad Commented Mar 17 at 11:24
  • @wohlstad posted, is there any work around if I want to preserve this calling structure? – tesla1060 Commented Mar 17 at 11:28
  • Not as long as you use a function pointer. Unless you would like to store your this in some global variable which is quite horrible. – wohlstad Commented Mar 17 at 11:30
  • 3 There is a void* argument in callback - most likely the callback is called with your custom data? The typical solution is then to pass a static member function/free function/captureless lambda that reinterpret_casts the void* back into original type and calls member function/capturing lambda with it. – Yksisarvinen Commented Mar 17 at 11:35
  • @Yksisarvinen static_cast is enough for that. – Weijun Zhou Commented Mar 17 at 12:07
 |  Show 3 more comments

3 Answers 3

Reset to default 3

Only a lambda without any capture can be converted and assigned to a function pointer.

There is no way to invoke a non-static method via a plain function pointer as is. You need your this context somehow.
There are 2 possible ways to overcome this:

  1. Use the void* parameter to the callback to allow to pass the this context. Then in your callback convert it to ExampleClass* and call the method. If you need another data pointer you can: (1) store it in an ExampleClass member or (2) instead of passing this as mentioned above pass a pointer to a pair of this and another data pointer.
  2. Store this in a global variable - this is quite horrible and should be the last resort.

A better solution (if you could have changed the external code) is to make CallbackType a type of std::function instead:

// Simulated callback type
using CallbackType = std::function<void(void*, uint32_t)>;

Live demo

But the lambda I assigned to field callback gives error, any way to work around this issue?

In C++, a lambda expression may be implicitly converted to a function pointer only if it does not capture any variables (i.e., has an empty capture list).

In your case, since you can not modify the function pointer API:

using CallbackType = void(*)(void*, uint32_t);

You just might need to find a way to save the this pointer context, so that the non-static member function pointer can be wrapped and assigned to CallbackType callback.

For example:

class ExampleClass 
{
   // Structure to hold both instance pointer and data
   struct Context 
   {
      ExampleClass* instance{ nullptr };
      void* userData{nullptr};
   };

public:
   ExampleClass()
   {
      // Initialize the context with this pointer
      context.instance = this;

      // Use static lambda as the callback (no captures)
      callback = [](void* contextPtr, uint32_t id)
      {
         if (Context* ctx = static_cast<Context*>(contextPtr);
             ctx && ctx->instance) 
            ctx->instance->memberFunction(ctx->userData, id);
      };
   }

   void executeCallback(void* data, uint32_t id) { ... }

private:
   void memberFunction(void* data, uint32_t id) { ... }

   Context context{ this, nullptr };
   CallbackType callback;
   DataProcessor processor;
};

See live demo

any way to work around this issue?

Yes, but we'll need to understand what the issue is, first.

using CallbackType = void(*)(void*, uint32_t);
This is an address of a single function. The CallbackType variable contains only the information necessary to call the function, everything else must be provided by the caller. Two calls using the same variable may be different only if either call arguments are different or the call checks some global state.

The memberFunction need to be non-static, as it also needs to access a non-static member field processor.

This means there may potentially be five different objects carrying five different processors and using them as callbacks should lead to different results. We have what seems like a contradiction, one of our assumptions has to be discarded.

  1. We may switch from a function pointer to an std::function object. Those can contain more data, "capturing" this. If you design all interfaces from the ground and not bound to being compatible with some existing library, that's a good choice.

  2. We may alter the call behavior by passing a different argument. Your callback signature already includes void* a.k.a. "any client-specified information", you may pass an instance of struct Onion {void* inner; ExampleClass* thiz;} and in the memberFunction (which by itself is a static member) cast data back to Onion* and use thiz->processor.

  3. Finally, for the sake of completeness, we may create a global or thread local storage ExampleClass* variable and use it as "pointer to the object whose processor should be used" in (once again, static) memberFunction. It is usually not a good approach but it's there.

与本文相关的文章

发布评论

评论列表(0)

  1. 暂无评论