【问题标题】:Type cast void*(*)(void*) to void(*)(void)将 void*(*)(void*) 类型转换为 void(*)(void)
【发布时间】:2013-01-25 20:46:35
【问题描述】:

作为作业的一部分,我正在尝试创建一个用户级线程库,例如 pthreads。

为了处理线程之间的上下文切换,我使用了“swapcontext”函数。在使用它之前,我必须使用“makecontext”函数创建一个上下文。 'makecontext' 需要一个返回类型为 void 和参数类型为 (void) 的函数指针。

而线程函数的类型必须是void* thread_func (void*)

有没有办法进行类型转换?还是有其他方法可以在用户级别进行上下文切换?

【问题讨论】:

  • 你要找的不是void*(*)(void*)void(*)(void) 演员吗?
  • @Chunk-e-Yamani 我现在已经编辑了问题以正确说明标题中的演员。

标签: c multithreading casting pthreads


【解决方案1】:

通过将函数的地址转换为不同的原型并通过结果指针调用它来调用具有不兼容原型的函数是非法的:

void *my_callback(void *arg) { ... }

void (*broken)(void *) = (void (*)(void *)) my_callback;
broken(some_arg);   // incorrect, my_callback returns a `void *`

您可以做的是将您自己的回调传递给makecontext,该回调将调用thread_func 并忽略其返回值。仅用于调用另一个函数的小函数有时称为trampoline

/* return type is compatible with the prototype of the callback received
   by makecontext; simply calls the real callback */
static void trampoline(int cb, int arg)
{
  void *(*real_cb)(void *) = (void *(*)(void *)) cb;
  void *real_arg = arg;
  real_cb(real_arg);
}

int my_pthread_create(void *(*cb)(void *), void *arg)
{
  ucontext_t *ucp;
  ...
  /* For brevity treating `void *` as the same size as `int` -
     DO NOT USE AS-IS.
     makecontext exposes an annoyingly inconvenient API that only
     accepts int arguments; correct code would deconstruct each
     pointer into two ints (on architectures where pointer is
     larger than int) and reconstruct them in the trampoline. */
  makecontext(ucp, trampoline, 2, (int) cb, (int) arg);
  ...
}

对于奖励积分,您可以修改蹦床以将回调函数返回的 void * 值存储在堆栈中,并让您的等效 pthread_join() 检索它。

【讨论】:

  • 准确地说,铸造完全没问题。但是,通过错误的指针类型调用函数是未定义的行为。
  • 在 C 中,void (*)() 不是没有参数的函数 - 它是一个老式函数,具有固定但未指定数量的参数,不提供原型。因此,无需强制转换 void (*)() 即可使用参数调用它。
  • @caf 你是对的;我认为这在 C99 中已被删除,但它只是被弃用。我会更新答案。
【解决方案2】:

原则上,您始终可以将任何类型的指针强制转换为任何其他类型的指针,但对于函数指针,我会强烈反对

您的thread_func 将期望堆栈上有一个参数,如果在您的错误转换后调用该参数,则不会提供该参数。更糟糕的是,thread_func 会在不应该的地方写入返回值,从而破坏您的堆栈。

一种解决方案是将调用包装在它自己的适当类型的函数中。

【讨论】:

  • +1 用于解释 user4815162342 省略的堆栈
【解决方案3】:

您可以像变量一样对函数指针进行类型转换。语法比较笨拙,但肯定有可能(是否是个好主意完全是另一个讨论)。

不过,在这种情况下,这可能不是您想要做的。来自man page for swapcontext

在调用 makecontext() 之前,调用者必须分配一个新堆栈 对于这个上下文并将其地址分配给 ucp->uc_stack,

你的线程函数接受一个参数。通过您创建的堆栈将该参数传递给新上下文。传递给makecontext() 的函数可以是一个包装函数,它从堆栈中检索值并将其作为参数传递给线程函数。单独的类型转换不会提供将参数中的数据向下传递到新上下文中的函数的方法。

【讨论】:

    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2020-10-24
    • 1970-01-01
    • 1970-01-01
    • 2018-02-24
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多