2016-09-18 687 views
5

我试图写在c库接口,使用这个熟悉的模式:如何将std :: function对象传递给函数指针的函数?

void some_c_handler(void(*func)(void*), void* data); 

现在,我想写一个C++包装这个功能看起来像这样:

void my_new_cpp_handler(std::function<void()>&& func) 
{ 
    void (*p)() = foo(func); 
    void* data = bar(func); 
    some_c_handler(p, data); 
} 

some_c_handlermy_new_cpp_handler都解决了同样的问题;他们正在接受某种功能以及某种状态。但后者是首选,因为它从用户中抽象出大部分实现细节,并允许简单地传入一个lambda对象。

因此my_new_cpp_handler应该参照func参数,将其转换为函数指针并将其状态转换为data

我对标准知之甚少,或者执行std::function知道这是否是一个合理的要求。 foobar是否存在?

换句话说,我想要的是能够将有状态函数传递给c回调处理程序,而无需手动实例化我自己的struct类型以传递它。显然std::function已经为我们做了这个,所以我很想能够以某种方式将函数指针从状态中分离出来,并将它传递到c样式处理程序。这可能吗?

回答

7

这可能吗?

号你可以用C风格的回调到std::function<>...,编译器会发出代码提取datahandler并调用它。但是,确切的代码将取决于编译器,即ABI和正在使用的标准C++库。你只能在std::function包装下才能神奇地重建那些代码。此外,任意std::function...(或lambda)可以将代码其他包装成比对C风格处理程序的调用。很明显,应用“神奇重构”代码从这样一个std::function...实例中提取C风格的处理程序是不可能成功的。

P.S.

换句话说,我想要的是能够将有状态函数传递给c回调处理程序,而无需手动实例化自己的结构类型以传递它。显然std::function已经做到了这一点对我们来说

那么,为什么你刚才用什么std::function已经为你做:

void my_new_cpp_handler(std::function<void()>&& func) 
{ 
    func(); // calls some_c_handler(p, data) IFF that is what "func" encodes. 
} 
-2

根据this链接,std::function对象没有可访问的成员可以提供对指针的原始访问。您应该定义一个struct,它包含一个指向函数指针和对象的指针,以及一个构造函数包装器,它在构造std::struct之前将指针的地址存储到结构中,以便分配存储在指向它的指针中的地址到你的C处理程序的参数。

4

你可以做一个包装函数,其目的是简单地执行std::function回调。

void some_c_handler(void(*)(void*), void*) {} 

void std_function_caller(void* fn) { 
    (*static_cast<std::function<void()>*>(fn))(); 
}; 

auto make_std_function_caller(std::function<void()>& fn) { 
    return std::make_pair(std_function_caller, static_cast<void*>(&fn)); 
} 

void my_new_cpp_handler(std::function<void()>&& func) { 
    const auto p = make_std_function_caller(func); 
    some_c_handler(p.first, p.second); 
} 
4

如果你仔细查看这个库中的文档,你会发现,

void some_c_handler(void(*func)(void*), void* data); 

调用func,其传递data说法。

对于采用回调函数的C库,这是一种非常常见的设计模式。除了回调函数之外,它们还带有一个额外的不透明指针,该指针不是由库解释的,而是被盲目地转发到func。换句话说,C库调用

func(data); 

您可以在C++代码中使用它来将普通指针传递给任何类。

这也包括std::function

诀窍是,在大多数情况下,有必要使用new

auto *pointer=new std::function<function_type>... 

最终的结果是,可以传递到C库的指针,连同一个指向“蹦床功能“:

some_c_handler(&cpp_trampoline, reinterpret_cast<void *>(pointer)); 

而且蹦床重铸不透明指针:

void cpp_trampoline(void *pointer) 
{ 
    auto real_pointer=reinterpret_cast<std::function<...>*>(pointer); 

    // At this point, you have a pointer to the std::function here. 
    // Do with it as you wish. 

您需要在这里排除的唯一细节是找出动态分配的函数指针的正确范围,以避免内存泄漏。