Neppomuk
Neppomuk

Reputation: 1210

How do I hand over a pointer to a non-static member method to an extern "C" function?

I've got a class named tmc, which now contains (among other things, which are not relevant here) a private constructor, a static factory method, and an instance method named ReadLoop (void*):

extern "C" {
#include <pigpiod_if2.h>
}

class tmc {
public:
  static tmc* Initialize ();
  static int  main ();

private:
  void *ReadLoop (void *params);
  tmc ();
  pthread_t *tmc_receiver_reader;
};

tmc::tmc ()
: tmc_receiver_reader (start_thread (tmc::ReadLoop, NULL))
{
}

void* tmc::ReadLoop (void *params)
{
  return params;
}

tmc* tmc::Initialize ()
{
  tmc* instance = new tmc ();
  return instance;
}

int tmc::main ()
{
  return (tmc::Initialize ()) == NULL ? 0 : 1;
}

The issue is now the following: How shall I use the ReadLoop as a function pointer for use with the start_thread () function contained in pigpiod_if2? The code shown here doesn't compile because of the following error:

error: invalid use of non-static member function ‘void* tmc::ReadLoop(void*)’
   tmc_receiver_reader  (start_thread (tmc::ReadLoop, NULL))

I've seen several questions here at SO with the same error message, but none of them was about a pointer to a non-static member method given to a C function. Please note that even if the object created here is a singleton, I can't make ReadLoop () static. I'm using the g++ 6.5.0 on Raspbian Buster. Thank you.

Upvotes: 0

Views: 146

Answers (2)

Remy Lebeau
Remy Lebeau

Reputation: 596527

There is simply no way to use a non-static class method where a standalone function is expected. They are not compatible.

You are going to have to use a static or non-member proxy function instead. Fortunately, start_thread() allows you to pass a user-defined parameter to the function, so you can use that to pass the this pointer of your tmc instance, eg:

class tmc {
  ...
private:
  static void* ReadLoopProxy(void *params);
  void* ReadLoop();
  tmc ();
  pthread_t *tmc_receiver_reader;
};

tmc::tmc ()
  : tmc_receiver_reader (start_thread (tmc::ReadLoopProxy, this))
{
}

void* tmc::ReadLoopProxy(void *params)
{
    return static_cast<tmc*>(params)->ReadLoop();
}

void* tmc::ReadLoop()
{
  return NULL;
}

...

Upvotes: 2

aschepler
aschepler

Reputation: 72401

Luckily the C interface provides a void* pass-back to help with this. You can use that to point to the correct tmc object:

extern "C" void* tmc_pigpio_ReadLoop_callback(void* userdata);
class tmc {
    // ...
private:
    void* ReadLoop();
    friend void* tmc_pigpio_ReadLoop_callback(void*);
    // ...
};

void* tmc_pigpio_ReadLoop_callback(void* userdata)
{
    auto* tmcp = static_cast<tmc*>(userdata);
    return tmcp->ReadLoop();
}

// Note "this" passed to start_thread.
tmc::tmc ()
: tmc_receiver_reader (start_thread (tmc_pigpio_ReadLoop_callback, this))
{
}

Or if you were already using the void* userdata for something different, add a tmc* pointer to a struct it points at, or create a new struct containing a tmc* pointer and the other data to pass.

Upvotes: 2

Related Questions