How to pass std :: function with capture through C API?

I have a C API, which is a queue used to send messages between threads. I want to pass through it std::function<void()>, but for this I need to degrade it into a POD fragment of data of constant length.

std::functionwill be mainly from C ++ 11 lambdas and will be written by reference or copy. I can use a bunch on both sides of the C queue.

The queue itself is a FreeRTOS queue, and it is built-in. There is some discussion about passing C ++ ish things through the queue on their forums. It basically says that it is ok if it is a POD or trivially constructed.

I am currently browsing struct { void (*fp)(void*); void* context; }and executing it as received.fp(received.context); but I would like a little better in terms of readability, without sacrificing much more resources. (EDIT: expanded on current usage and needs)

Any idea how to do this?

+4
source share
3 answers

I don’t know much about FreeRTOS, but with your requirements you should be able to use a pointer to your object std::function(either allocated in a heap, or a pointer to an object not allocated in a heap if its lifetime allows) - all raw pointers are POD, even if they point to C ++ objects.

Here is a quick example of using a linked pointer std::functionallocated on the stack:

std::function<void()> produce(int value)
{
    return [value]() {
        std::cout << "Value = " << value << std::endl;
    };
}

void consume(std::function<void()> *callback)
{
    (*callback)();
}

int main()
{
    auto cb = produce(42);
    consume(&cb);

    return 0;
}
0

std::function<void()>. - POD. casts enqueue/dequeuer , new/delete /.

, (, 100-1000 ), . , Id , , ( 1 1 , - , , RTOS + 2), -, , [s] .

, . , .

+3

, , API- API.

struct c_api {
  void(*f)(void*);
  void* ptr;
};
template<class F>
c_api c_api_wrap( F* f ) {
  return {
    [](void* pv) { (*static_cast<F*>(pv))(); },
    f
  };
}

std::function ( lambda, ), void .

void std::function ( ).


:

template<class Sig>
struct c_api;
template<class R, class...Args>
struct c_api<R(Args...)> {
  using fptr = R(*)(void*, Args...);
  fptr f = nullptr;
  void* ptr = nullptr;
  template<class F>
  explicit c_api( F* pf ):
    f([](void* vptr, Args...args)->R {
      return (*static_cast<F*>(vptr))(std::forward<Args>(args)...);
    }),
    ptr(pf)
  {}
};
// not needed unless you want to pass in a non-void returning
// function to a void-returning C API:
template<class...Args>
struct c_api<void(Args...)> {
  using fptr = void(*)(void*, Args...);
  fptr f = nullptr;
  void* ptr = nullptr;
  template<class F>
  explicit c_api( F* pf ):
    f([](void* vptr, Args...args) {
      (*static_cast<F*>(vptr))(std::forward<Args>(args)...);
    }),
    ptr(pf)
  {}
};

// wrap a pointer to an arbitrary function object to be consumed
// by a C API:
template<class Sig, class F>
c_api<Sig> c_wrap( F* f ) {
  return c_api<Sig>(f);
}

, c_wrap<void()>(&some_std_function). .ptr .f, API C.

.

You can pass additional arguments or process return values ​​by changing the signature void(); however, it assumes that the void*"self" component is the first argument. The fancier version can support void*anywhere, but it becomes verbose.

If you are interested in how this works, stateless lambda can be converted to a functoin pointer, which we use in the constructor c_apiabove.

+1
source

Source: https://habr.com/ru/post/1689915/


All Articles