2

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
Community
  • 1
  • 1
Victor.dMdB
  • 831
  • 2
  • 10
  • 25
  • 1
    This solution, apart from invoking undefined behaviour (assuming register_with_library in real code is a member of a C library written in C) , doesn't let you register two callbacks of the same type. It just overwrites the only static variable it has with the last registered callback, and silently discards the previous one. – n. 'pronouns' m. Dec 18 '16 at 07:10
  • @n.m Yes, that's exactly the problem. But is there a solution? – Victor.dMdB Dec 18 '16 at 07:58
  • See my answer. If you are impatient, skip right to the last paragraph. – n. 'pronouns' m. Dec 18 '16 at 08:01
  • It's impossible to do this totally without invoking at least implementation defined behaviour if you want a general solution. What you need is a thunk - basically allocate enough memory for the this pointer and a function stub for every callback. MFC does this iirc – Voo Dec 18 '16 at 18:33

2 Answers2

1

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();
}
e.jahandar
  • 1,667
  • 10
  • 27
  • I've tried implementing it here but I get : non-local variable ‘A::Callback::func’ declared ‘__thread’ needs dynamic initialization. Am I using it right? – Victor.dMdB Dec 17 '16 at 15:05
  • show the code, but you can't use __thread for non-static class members – e.jahandar Dec 17 '16 at 15:34
  • Ive tried using thread_local and added to the 3 lines: `thread_local static Ret callback(Args... args)`, `thread_local static std::function func;` and `thread_local std::function A::Callback::func;` – Victor.dMdB Dec 17 '16 at 15:37
  • using void * is allowed? you can make the void* ptr thread local and for callback cast it as the object you want. – e.jahandar Dec 17 '16 at 15:45
  • sorry, I'm not sure which ptr you're referring to? – Victor.dMdB Dec 17 '16 at 15:54
  • a thread local void pointer which holds an address of actual object, so you can cast it to that object and call one of its methods, casting and calling could be done in normal C style callback function and the void pointer is thread local. – e.jahandar Dec 17 '16 at 15:56
  • i've added an example – e.jahandar Dec 17 '16 at 16:01
  • I've tried declaring the `A` objects within the threads with `thread-local` but the issue always comes back to when the callback::callback template is declared. I updated my question to demonstrate the issue. – Victor.dMdB Dec 18 '16 at 05:51
0

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.

n. 'pronouns' m.
  • 95,181
  • 13
  • 111
  • 206
  • I think this might be good enough for me, since I don't need multiple objects per thread (though that would also be interested to see a solution for). When I implement it though I, even if I call my_callback_wrapper immediately after the assignment of aptr and fptr, they are still not defined. I had to include these in namespace braces in the header to avoid conflict, might that be it? – Victor.dMdB Dec 19 '16 at 03:44
  • I'm not sure what exactly you mean, can you show the code? – n. 'pronouns' m. Dec 19 '16 at 04:14
  • sorry, sorry it was a stupid mistake. I've called my_callback_wrapper to test that it works before i register it with the C lib, and it works fine. But once the C lib makes the call directly, I receive a segfault. – Victor.dMdB Dec 19 '16 at 04:20
  • yes, the issue is that aptr and fptr are no longer set when my C function makes a call to the callback – Victor.dMdB Dec 19 '16 at 06:07