How to pass a pointer to a member function to a C function? [duplicate]
Asked Answered
D

2

5

Possible Duplicate:
Using a C++ class member function as a C callback function

I'm writing an object-oriented library using a C library (winpcap). I need to pass the callback function that is called when a network packet arrives as a function pointer. I would like to pass a member function pointer to winpcap, to keep my design object oriented and to allow for different objects to receive different packets. However member functions as far as I understand have a different calling convention, and thus cannot be passed to a C function. Is there a way to fix this. My experiments with boost::bind (which I hardly manage to use other than trial and error) are not fruitful.

Is there a way to change the calling convention of a member function?

This is the definition of the callback function I use now and the actual passing of it to winpcap

void pcapCallback( byte* param, const struct pcap_pkthdr* header, const byte* pkt_data );

pcap_loop( adhandle, 0, pcapCallback, NULL );

The pcap_loop just takes the name of the function (which is on the global scope at the moment). This is the definition of the function pointer parameter (3rd parameter of pcap_loop). Since this is third party code I can't really change this. I would have to have a member function that can take this form:

typedef void (*pcap_handler)(u_char *, const struct pcap_pkthdr *, const u_char *);

As far as I understand it, the member function will be using thiscall and the c function pointer wants a cdecl

Duty answered 24/6, 2010 at 9:22 Comment(3)
Duplicate of Using a C++ class member function as a C callback function and quite some more.Ultrared
thanks, I didn't find that one.Duty
#1001163Celinecelinka
U
5

You can only pass static member functions to a C API.

Unscrew answered 24/6, 2010 at 9:24 Comment(3)
well, this is incorrect, see other answers and answers to the duplicate question. There are ways around this problem, and those ways is what I was after.Duty
@ufotds: While it is true that you can use clever tricks to make static member functions call non-static ones, it is also true that you can only pass (pointers to) static member functions to C APIs. So Paul is indeed correct, although there's more to this than he wrote.Gastropod
ok, obviously the title of my question should have mentioned a non-static member function... A static function in this respect is about as good (marginally better) as a global function that would call a member function. Don't take things to literally.Duty
G
4

If you want to have a C API call a member function, you have to pass two pieces of data: the (static member) function and the object for which it is to be invoked.

Usually, C API callbacks have some form of "user data", often a void*, through which you can tunnel your object's address:

// Beware, brain-compiled code ahead!

typedef void (*callback)(int data, void* user_data);

void f(callback cb, void* user_data);

class cpp_callback {
public:
  virtual ~cpp_callback() {} // sometimes needed
  void cb(int data) = 0;
  callback* get_callback() const {return &cb_;}
private
  static void cb_(int data, void* user_data)
  {
    cpp_callback* that = reinterpret_cast<my_cpp_callback*>(user_Data);
    that->cb(data);
  }
};

class my_callback {
public:
  void cb(int data) 
  {
     // deal with data
  }
};

void g()
{
  my_callback cb;
  f(cb.get_callback(), &cb);
}

That pcapCallback doesn't look as if it has user data, though, unless that's what param is. If that's indeed the case, you'll have to store the callback object's address in some global variable before calling the API. Something like this:

// Beware, brain-compiled code ahead!

typedef void (*callback)(int data);

void f(callback cb);

class cpp_callback {
public:
  cpp_callback() : the_old_cb_(this) {std::swap(the_cb_,the_old_cb_);}
  virtual ~cpp_callback()            {std::swap(the_cb_,the_old_cb_);}
  void cb(int data) = 0;
  callback* get_callback() const {return &cb_;}
private
  static cpp_callback* the_cb_;
  cpp_callback* the_old_cb_;
  static void cb_(int data, void* user_data) 
  {
    the_cb_->cb(data);
  }
};

class my_callback {
public:
  void cb(int data) { /* deal with data */ }
};

void g()
{
  my_callback cb;
  f(cb.get_callback(), &cb);
}

As always with global data, this is perilous if more than one instance of the callback is alive. I have tried to minimize the harm so that it works if their lifetimes are nested. Anything else, though, will hurt.

Gastropod answered 24/6, 2010 at 9:38 Comment(3)
This can be painful when you are dealing with functions like glutDisplayFunc which has a callback which is void(__cdecl *)(void); so there is no way for the display proc to know who called it without inevitably causing race conditions or requiring the use of locks(since there is no way to have different callbacks determine who called them without either storing their caller in global state, or mapping the function's address to it's previous caller using global state, but doing so means changing this map can cause a data race).Muns
@Dimitry: If a C API callback doesn't provide a user-provided argument. then it's either not meant to be used in conjunction with some specific piece of data or the API creators were dumb. Either way, you need to write a free function.Gastropod
yes but regardless, when the user wants to write a library on top of glut, they won't be able to have display be aware of their scenegraph without a threadlocal, or a possible future race.Muns

© 2022 - 2024 — McMap. All rights reserved.