【问题标题】:Casting the std::function to the C function pointer将 std::function 转换为 C 函数指针
【发布时间】:2021-12-22 05:40:23
【问题描述】:

我在转换时遇到问题: std::function <int32_t (std::string &, uint32_t)>typedef int32_t (*callback_c_type) (std::string &, uint32_t(C 函数指针)。

我的完整示例https://onlinegdb.com/5KI36oPlQ

#include <iostream>
#include <functional>
using namespace std;

typedef int32_t (*callback_c_type) (std::string &, uint32_t);
static int counter = 0;

int32_t my_callback (std::string & index, uint32_t var_id)
{
  std::cout << "my_callback => index: '" << index 
  << " var_id: '" << var_id 
  << "'" << " counter = " << counter << std::endl;
  counter++;
  return 42;
}

void execute_c_callback(callback_c_type cb)
{
  std::cout << "execute_c_callback" << std::endl;
  std::string text = "foo";
  cb(text, 777);
}

int main ()
{
  callback_c_type cb = &my_callback;
  execute_c_callback(cb);

  std::function <int32_t (std::string &, uint32_t)> cb_2 = cb;
  // execute_c_callback((callback_c_type)cb_2); 
  // PROBLEM: convert std::function<int32_t(std::string&, uint32_t)> -> callback_c_type
    
  return 0;
}

【问题讨论】:

  • 投射到const char* 并返回有什么意义?除了问题,它不会增加任何东西。
  • 请比“我有问题”更具体。并且不要发布代码链接,发布代码。
  • 我认为minimal reproducible example 会有所帮助。但据我所见,我不相信这可以通过演员表来完成。您可能需要某种适配器函数/对象。
  • 那是什么图书馆?那个图书馆是谁建的?您确定库将const char * 指针转换为函数指针以调用该指针吗?如果库这样做,请不要使用该库 - 肯定它是一个非常糟糕的库。 ||提供的代码不起作用,因为)&gt;*&gt;(&amp;cb); - &amp;cbcb 指针的地址,而不是它的值。也许你想reinterpret_cast&lt;....&gt;(cb)。不过,这取决于一些假设 - 考虑至少添加 static_assert(alignof(const char *) == alignof(std::function&lt;....&gt;)
  • @MichałHanusek 但是 (a) 为什么 Rust 回调的类型会是 char const*?并且 (b) 你仍然不能将 std::function 传递给 Rust 库。它需要一个 C 函数指针,而不是指向 std::function 对象的指针。

标签: c++ function-pointers std-function


【解决方案1】:

std::function 是一个具有误导性名称的类(模板):它不是函数。它是一个多态函数(对象)包装器。让我们把它分开:它是函数和函数对象的包装类,在类型擦除的意义上它是多态的。

std::function的主要工作是存储任意可调用对象(函数对象)并为它们提供单一接口:

double myFunc(int);

struct MyStruct {
    double operator()(int);
};

std::function<int(double)> f = myFunc; // can store function pointers
f = MyStruct{}; // or class instances
// ... and many more

f 的类型相同,而里面存储的东西的类型可以不同。这称为类型擦除


带有签名int32_t (*) (std::string &amp;, uint32_t) 的C 回调需要一个函数指针。换句话说,一个指向一段可执行代码的指针。

你不能将类对象作为函数指针传递,因为类对象不是一段代码而是一段数据,与相关段代码(成员函数)。 p>

从状态的角度考虑,函数是无状态的。它始终是同一段代码。 类对象可以是有状态的(例如,如果它有任何成员)。

函数指针是指向可执行代码的单个指针,它不能直接传递状态。因此,很多 C 回调都有一个额外的参数来传递状态:

typedef int32_t (*callback_c_type) (void* state, std::string &, uint32_t);

有了这样的签名,你可以通过回调“瓶颈”将函数对象作为指针传递并在另一端恢复它,但需要注意的是:

typedef int32_t (*callback_c_type) (void*, std::string &, uint32_t);

void execute_c_callback(callback_c_type cb, void* state)
{
  std::cout << "execute_c_callback" << std::endl;
  std::string text = "foo";
  cb(state, text, 777);
}

using plain_cb_signature = int32(std::string&, uint32_t);

int32_t callback_wrapper(void* state, std::string &s, uint32_t i)
{
    auto* f = static_cast<std::function<plain_cb_signature>*>(state);
    (*f)(s, i);
}

int main ()
{
  std::function <int32_t (std::string &, uint32_t)> cb_2 = cb;
  auto* f = &cb;
  execute_c_callback(callback_wrapper, f); 
    
  return 0;
}

这里的麻烦在于维护cb 的生命周期,换句话说,保持状态的有效时间足够长,以供execute_c_callback 的所有调用使用。

通常将callback_wrapper 写为通用函数模板。例如,请参阅this version by Yakk

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2012-10-25
    • 1970-01-01
    • 1970-01-01
    • 2012-11-21
    • 2011-07-31
    • 1970-01-01
    相关资源
    最近更新 更多