我有一个C API,它是一个用于在线程之间传递消息的队列。我希望通过它传递一个std::function<void()>
,但为了做到这一点,我需要将它降级为POD的恒定长度的数据块。
std::function
主要来自C ++ 11 lambdas,将通过引用或复制进行捕获。我可以使用来自C队列两侧的堆。
队列本身是一个FreeRTOS队列,它是嵌入式的。他们的论坛上有一些discussion about passing C++ish things through the queue。如果它是POD或可以轻松构建,它通常会说它没问题。
目前我正在struct { void (*fp)(void*); void* context; }
传递,并以received.fp(received.context)
执行;但是在不牺牲更多资源的情况下,我在可读性方面更喜欢一些东西。 (编辑:扩大了当前的使用和需求)
知道怎么做吗?
答案 0 :(得分:3)
您可以将指针传递给std::function<void()>
。指针是POD。您可以使用强制转换来排队/取消,并使用堆即新建/删除来分配/解除分配。
但是,如果这是嵌入的并且你有大量的这些消息(我说每秒超过100-1000条消息),那么以这种方式强调内存管理器并不是一个好主意。如果是这种情况,我会重新编写消息,以便它们不使用动态内存,为正在进行的消息分配一个足够大小的池(如果你有1个消费者和1个生产者,那么足够的大小可能是RTOS队列长度+ 2),并在消费者线程消费消息后使用一些东西将消息指针回收到池中。
不幸的是,这增加了很多复杂性。没有动态内存意味着没有lambdas,并且回收池需要是线程安全的。
答案 1 :(得分:1)
我首先要完全解决您的问题,然后向您展示以通用方式解决问题的方法,该方法适用于许多不同的C 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
(或lambda)。
这是通用的解决方案:
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
以传递到您的C API。
它根本不管理记忆。
您可以通过更改void()
签名来传递其他参数或处理返回值;然而,它假设void*
&#34; self&#34; component是第一个参数。
如果您想知道它是如何工作的,可以将无状态lambda转换为functoin指针,这是我们在上面的c_api
构造函数中使用的。
答案 2 :(得分:0)
我对FreeRTOS了解不多,但根据您的要求,您应该能够使用指向std::function
对象的指针(堆分配或指向非堆分配对象的指针)如果它的生命周期允许) - 所有原始指针都是POD,即使它们指向C ++对象。
这是一个使用指向堆栈中分配的绑定std::function
的指针的快速示例:
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;
}