Victor.dMdB
Victor.dMdB

Reputation: 1019

Using a c++ class member function as a c callback function, thread safe version

The original question, got a great answer as to how to do the non thread safe version.

Here is the code, which I've tried to slightly modify to get to work:

#include <stdio.h>
#include <functional>
#include <thread>

void register_with_library(int (*func)(int *k, int *e)) {
   int x = 0, y = 1;
   int o = func(&x, &y);
}

typedef int (*callback_t)(int*,int*);

class A {
  template <typename T>
  struct Callback;

  template <typename Ret, typename... Params>
  struct Callback<Ret(Params...)> {
     template <typename... Args>
     thread_local static Ret callback(Args... args) {
        func(args...);
     }
     thread_local static std::function<Ret(Params...)> func;
  };
   public:
      A();
      ~A();
      int e(int *k, int *j);
    private:
      callback_t func;
};

template <typename Ret, typename... Params>
thread_local std::function<Ret(Params...)> A::Callback<Ret(Params...)>::func;

A::A() {
   Callback<int(int*,int*)>::func = std::bind(&A::e, this, std::placeholders::_1, std::placeholders::_2);
   printf("1.  C callback function ptr %p, C++ template function ptr %p Object ptr %p \n",func, Callback<int(int*,int*)>::func,  this) ;
   func = static_cast<callback_t>(Callback<int(int*,int*)>::callback);
   printf("2.  C callback function ptr %p\n",func) ;
   register_with_library(func);
}

int A::e(int *k, int *j) {
   return *k - *j;
}

A::~A() { }

int main() {
   std::thread t1 = std::thread { [](){ A a;}};
   std::thread t2 = std::thread { [](){ A a;}};

   t1.join();
   t2.join();
}

The result is

function ptr 0x400eef
function ptr 0x400eef

How would one make this work properly to create new callbacks for each new object, considering I have multiple threads creating different objects?

EDIT:

As suggested by e.jahandar, using thread_local works to partially resolve the issue (only if there is 1 object created per thread). Thanks to this, Callback<int(int*,int*)>::func is allocated on a thread basis. Although, the issue persists with Callback<int(int*,int*)>::callback.

Without thread_local:

1. C callback function ptr 0x403148, C++ template function ptr 0x609180 Object ptr 0x7ff9ac9f3e60 
2. C callback function ptr 0x403673
1. C callback function ptr 0x4031a6, C++ template function ptr 0x609180 Object ptr 0x7ff9ad1f4e60 
2. C callback function ptr 0x403673

with thread_local :

1. C callback function ptr 0x403230, C++ template function ptr 0x7fc1ecc756d0 Object ptr 0x7fc1ecc74e20 
2. C callback function ptr 0x403701
1. C callback function ptr 0x4031d2, C++ template function ptr 0x7fc1ec4746d0 Object ptr 0x7fc1ec473e20 
2. C callback function ptr 0x403701

Upvotes: 2

Views: 2863

Answers (2)

n. m. could be an AI
n. m. could be an AI

Reputation: 119877

The only standards compliant way to make a C++ function callable from C is to declare it with C linkage.

extern "C" int my_callback_wrapper (A*, int*, int*);

my_callback can be neither template, nor a member function (even static).

Anything beyond that is undefined behaviour.

In order to be standards-compliant, you have to manually wrap every single class member function with a separate callback wrapper. This solution is naturally thread safe as it doesn't use any global or static data.

Naturally it requires the C code to obtain pointer(s) to your A object(s), and pass the right pointer to the callback at the right moment. Hiding an instance of A from the C library amounts to storing A in some static/global storage, which means there can be only one per callback. If you are using C++11, you can also specify thread_local storage and have an object per thread.

extern "C" int my_callback_wrapper (int* x, int* y);
thread_local A* aptr = nullptr;
thread_local int (A::*fptr)(int*, int*) = nullptr;

void register_cxx_callback (A* a, int (A::*f)(int*, int*))
{
    if (aptr != nullptr || fptr != nullptr)
        fatal_error ("Trying to overwrite the callback!");
    aptr = a;
    fptr = f;
    register_with_library(my_callback_wrapper);
}

extern "C" int my_callback_wrapper (int* x, int* y)
{
    if (aptr == nullptr || fptr == nullptr)
        fatal_error ("Callback is called but the object is not registered!");
    printf ("aptr is %p\n", (void*)aptr);
    return (aptr->*fptr)(x, y);
}

A full working example is here.

This solution is also thread safe as long as the C library doesn't attempt to pass registered callbacks between threads. It is also standards compliant, as far as I can tell.

There are no templates because there are no C linkage templates. One can enclose the static/thread-local data in an class, but I don't see much point in classes that only have static data.

There is no way to portably register an arbitrary number of objects per thread. In the end, you have to provide a C function pointer, and C functions cannot portably have hidden arbitrary data (IOW you cannot build closures in standards compliant C). If you are interested in a non-portable solution, there is a library that does that.

Upvotes: 1

e.jahandar
e.jahandar

Reputation: 1763

If each thread just need one instance of specific object, you can use a global variable for object pointer with __thread storage class, __thread makes global variables unique to that thread.

Using monotonic class with static member for callback is another solution, like previous solution, you can use __thread for separating monothonic class instances for each thread.

Also be aware, __thread isn't standard thing

Edit

Here is an example

class.h

class someClass{
     private:
         someMethod(){ ... }
}

class.cpp

__thread void * objectPointer;

void initialize(){
    someClass * classPtr = new someClass();
    objectPointer = (void *) classPtr;
}

void * callbackFunction(void * args){
    someClass * obj = objectPointer;
    obj->someMethod();
}

Upvotes: 1

Related Questions