使用C ++ 11接口包装C回调的最好方法是什么? [英] What's the best way to wrap a C callback with a C++11 interface?

查看:97
本文介绍了使用C ++ 11接口包装C回调的最好方法是什么?的处理方法,对大家解决问题具有一定的参考价值,需要的朋友们下面随着小编来一起学习吧!

问题描述

假设这是一个要包装的C函数:

  void foo(int(__ stdcall * callback) ; 

C函数指针回调的两个主要缺陷是:




  • 无法存储绑定表达式

  • 无法存储捕获lambdas



我想知道最好的方式来包装像这样的函数。第一个对于成员函数回调特别有用,第二个对于使用周围变量的内联定义很有用,但这不是唯一的用途。



其他属性这些特定的函数指针是他们需要使用 __ stdcall 调用约定。这个,据我所知,消除lambdas作为一个选项,完全,是一个有点麻烦,否则。我至少允许 __ cdecl



这是我能够做到的最好的没有东西开始弯回到依赖于函数指针没有的支持。它通常在一个头。以下是 Coliru 的以下示例。

  #include< functional> 

//在另一个头中的C函数我不能控制
externCvoid foo(int(__ stdcall * callback)()){
callback
}

命名空间详细信息{
std :: function< int()>回电话; //假装extern并在cpp中定义

//与API兼容,但将工作传递给上面的变量
externCint __stdcall proxyCallback(){//在cpp中定义b $ b //可能的额外处理
return callback();
}
}

template< typename F> // take anything
void wrappedFoo(F f){
detail :: callback = f;
foo(detail :: proxyCallback); //用代理调用C函数
}

int main(){
wrappedFoo([&]() - > int {
return 5;
});
}

然而,存在一个重大缺陷。这不是再入。如果变量在使用之前被重新赋值,旧的函数将永远不会被调用(不考虑多线程问题)。



我试过的一件事情,返回本身是存储 std :: function 作为数据成员和使用对象,所以每个将操作一个不同的变量,但没有办法传递对象代理。将对象作为参数会导致签名不匹配,并且绑定不会将结果存储为函数指针。



我有一个想法,但没有play是一个向量 std :: function 。然而,我认为唯一真正安全的时间,从它擦除将是清除它,当没有什么使用它。但是,每个条目首先添加在 wrappedFoo 中,然后在 proxyCallback 中使用。我想知道一个计数器是增加在前者和减少在后者,然后检查零之前清除向量将工作,但它听起来像一个更复杂的解决方案比必要的。



有任何方法用函数指针回调函数来包装C函数,使得C ++包装版本:




    <允许任何函数对象
  • 允许超过C回调的调用约定(如果它是相同的,用户可以传入具有正确调用约定的东西)

  • 是线程安全/重入的



注意:明显的解决方案Mikael Persson的答案是利用应该存在的 void * 参数。然而,这不幸的是不是一个所有的,最终的选择,主要是由于无能。

如果没有这个选项的函数有什么可能存在的地方是有趣的,



有些方法可以在运行时生成代码,例如你可以在

href =http://llvm.org/docs/LangRef.html#trampoline-intrinsics =nofollow> LLVM trampoline内在性其中,您生成转储功能存储额外的状态,非常类似于lambdas,但








$ b

传递状态的最简单的解决方案是...实际传递状态。



定义良好的C回调将采用两个参数:




  • 指向回调函数本身的指针

  • A void *



后者不被代码本身使用,只是在被调用时传递给回调。根据接口,回调负责销毁它,或者供应商,甚至第三个destroy函数可以传递。



有了这样的接口,你可以有效地传递状态在线程安全&

  template< typename Result,typename ... Args)
结果包装器(void * state,Args ... args){
using FuncWrapper = std :: function< Result(Args ...)>
FuncWrapper& w = * reinterpret_cast< FuncWrapper *>(state);
return w(args ...);
}

template< typename Result,typename ... Args)
auto make_wrapper(std :: function< Result(Args ...)>& func)
- > std :: pair< Result(*)(Args ...),void *>
{
void * state = reinterpret_cast< void *>(& func);
return std :: make_pair(& wrapper< Result,Args ...> state);
}






不提供这样的设施,你可以hack一点,但最终你是非常有限的。正如所说,一个可能的解决方案是使用全局变量保持外部状态,并尽力避免争用。



这里有一个粗略的草图:

  // FreeList,Store和Release函数取决于你,
//你可以使用锁,原子, 。
template< size_t N,typename Result,typename ... Args>
class Callbacks {
public:
使用FunctionType = Result(*)(Args ...);
using FuncWrapper = std :: function< Result(Args ...)>

static std :: pair< FunctionType,size_t>生成(FuncWrapper& func){
// 1.使用自由列表,找到存储func的索引
size_t const index = Store(std :: move );

// 2.选择适当的调用函数并返回
assert(index< N);
return std :: make_pair(Select< 0,N-1>(index),index)
} //生成

static void Release(size_t);

private:
static size_t FreeList [N];
static FuncWrapper State [N];

static size_t Store(FuncWrapper& func);

template< size_t I,typename = typename std :: enable_if<(I< N)> :: type>
static结果调用(Args ...& amp; args){
return State [I](std :: forward< Args>(args)...)
} //调用

模板< size_t L,size_t H>
static FunctionType Select(size_t const index){
static size_t const Middle =(L + H)/ 2;

if(L == H){return Call< L> ;; }

return index< = Middle?选择< L,Middle>(索引)
:选择< Middle + 1,H&
}

}; // class Callbacks

//静态初始化
模板< size_t N,typename Result,typename ... Args>
static size_t Callbacks< N,Result,Args ...> :: FreeList [N] = {};

template< size_t N,typename Result,typename ... Args>
static Callbacks< N,Result,Args ...> :: FuncWrapper Callbacks< N,Result,Args ...> :: State [N] = {};


Let's say this is a C function to be wrapped:

void foo(int(__stdcall *callback)());

The two main pitfalls with C function pointer callbacks are:

  • Not being able to store bind expressions
  • Not being able to store capturing lambdas

I would like to know the best way to wrap functions like these to do so. The first is particularly useful for a member function callback, and the second for an inline definition that uses surrounding variables, but those are not the only uses.

The other property of these particular function pointers is that they need to use the __stdcall calling convention. This, to my knowledge, eliminates lambdas as an option completely, and is a bit of a nuisance otherwise. I'd like to allow at least __cdecl as well.

This is the best I am able to come up with without things starting to bend back to relying on support that function pointers don't have. It would typically be in a header. Here is the following example on Coliru.

#include <functional>

//C function in another header I have no control over
extern "C" void foo(int(__stdcall *callback)()) {
    callback();
}

namespace detail {
    std::function<int()> callback; //pretend extern and defined in cpp

    //compatible with the API, but passes work to above variable
    extern "C" int __stdcall proxyCallback() { //pretend defined in cpp
        //possible additional processing
        return callback();
    }
}

template<typename F> //takes anything
void wrappedFoo(F f) {
    detail::callback = f;
    foo(detail::proxyCallback); //call C function with proxy 
}

int main() {
    wrappedFoo([&]() -> int {
        return 5;
    });   
}

There is, however, a major flaw. This is not re-entrant. If the variable is reassigned to before it's used, the old function will never be called (not taking into account multithreading issues).

One thing I have tried that ended up doubling back on itself was storing the std::function as a data member and using objects, so each would operate on a different variable, but there was no way to pass the object to the proxy. Taking the object as a parameter would cause the signature to mismatch and binding it would not let the result be stored as a function pointer.

One idea I have, but have not played around with is a vector of std::function. However, I think the only real safe time to erase from it would be to clear it when nothing is using it. However, each entry is first added in wrappedFoo, then used in proxyCallback. I'm wondering if a counter that is incremented in the former and decremented in the latter, then checked for zero before clearing the vector would work, but it sounds like a more convoluted solution than necessary anyway.

Is there any way to wrap a C function with a function pointer callback such that the C++ wrapped version:

  • Allows any function object
  • Allows more than just the C callback's calling convention (if it's critical that it's the same, the user can pass in something with the right calling convention)
  • Is thread-safe/re-entrant

Note: The obvious solution, stated as part of Mikael Persson's answer, is to make use of the void * parameter that should exist. However, this is sadly not a be-all, end-all option, mostly due to incompetence. What possibilities exist for those functions that do not have this option is where this can get interesting, and is the primary route to a very useful answer.

解决方案

You are, unfortunately, out of luck.

There are ways to generate code at runtime, for example you can read on LLVM trampoline intrinsics where you generate a forwarding function that stores additional state, very akin to lambdas but runtime defined.

Unfortunately none of those are standard, and thus you are stranded.


The simplest solution to pass state is... to actually pass state. Ah!

Well defined C callbacks will take two parameters:

  • A pointer to the callback function itself
  • A void*

The latter is unused by the code itself, and simply passed to the callback when it is called. Depending on the interface either the callback is responsible to destroy it, or the supplier, or even a 3rd "destroy" function could be passed.

With such an interface, you can effectively pass state in a thread-safe & re-entrant fashion at the C level, and thus naturally wrap this up in C++ with the same properties.

template <typename Result, typename... Args)
Result wrapper(void* state, Args... args) {
    using FuncWrapper = std::function<Result(Args...)>;
    FuncWrapper& w = *reinterpret_cast<FuncWrapper*>(state);
    return w(args...);
}

template <typename Result, typename... Args)
auto make_wrapper(std::function<Result(Args...)>& func)
    -> std::pair<Result (*)(Args...), void*>
{
    void* state = reinterpret_cast<void*>(&func);
    return std::make_pair(&wrapper<Result, Args...>, state);
}


If the C interface does not provide such facilities, you can hack around a bit, but ultimately you are very limited. As was said, a possible solution is to hold the state externally, using globals, and do your best to avoid contention.

A rough sketch is here:

// The FreeList, Store and Release functions are up to you,
// you can use locks, atomics, whatever...
template <size_t N, typename Result, typename... Args>
class Callbacks {
public:
    using FunctionType = Result (*)(Args...);
    using FuncWrapper = std::function<Result(Args...)>;

    static std::pair<FunctionType, size_t> Generate(FuncWrapper&& func) {
        // 1. Using the free-list, find the index in which to store "func"
        size_t const index = Store(std::move(state));

        // 2. Select the appropriate "Call" function and return it
        assert(index < N);
        return std::make_pair(Select<0, N-1>(index), index);
    } // Generate

    static void Release(size_t);

private:
    static size_t FreeList[N];
    static FuncWrapper State[N];

    static size_t Store(FuncWrapper&& func);

    template <size_t I, typename = typename std::enable_if<(I < N)>::type>
    static Result Call(Args...&& args) {
        return State[I](std::forward<Args>(args)...);
    } // Call

    template <size_t L, size_t H>
    static FunctionType Select(size_t const index) {
        static size_t const Middle = (L+H)/2;

        if (L == H) { return Call<L>; }

        return index <= Middle ? Select<L, Middle>(index)
                               : Select<Middle + 1, H>(index);
    }

}; // class Callbacks

// Static initialization
template <size_t N, typename Result, typename... Args>
static size_t Callbacks<N, Result, Args...>::FreeList[N] = {};

template <size_t N, typename Result, typename... Args>
static Callbacks<N, Result, Args...>::FuncWrapper Callbacks<N, Result, Args...>::State[N] = {};

这篇关于使用C ++ 11接口包装C回调的最好方法是什么?的文章就介绍到这了,希望我们推荐的答案对大家有所帮助,也希望大家多多支持IT屋!

查看全文
登录 关闭
扫码关注1秒登录
发送“验证码”获取 | 15天全站免登陆