用于“上下文”指针的正确类型

时间:2017-03-16 03:03:51

标签: c++ c

C风格的API通常将函数指针作为回调来将指针大小的参数作为“上下文”传递给回调,以便可以从调用站点传递信息调用回调。例如,pthread_create

int pthread_create(pthread_t *thread, const pthread_attr_t *attr,
                          void *(*start_routine) (void *), void *arg);

此处,arg是“上下文”。

最近,我遇到了一个我希望将整数传递给这样一个函数的情况。我显然不想实际传递指向整数的指针,因为我必须动态分配它以保证生命周期。

所以我的解决方案是reinterpret_cast intvoid*,然后回到int回调中。但是,我后来才知道这不可移植:Does reinterpret_casting an integral to a pointer type and back yield the same value?

如果是这样的话,解决方案是什么?

要避免此问题,此类API应采用uintptr_t代替void*吗?

1 个答案:

答案 0 :(得分:1)

  

我显然不想实际传递指向整数的指针,因为我必须动态分配它以保证生命周期。

我知道您的问题不仅仅是pthread_create,所以我会尝试从更广泛的意义上回答。但是,您还专注于pthread_create,举个例子,所以我觉得有必要回答这个问题

pthread_create的上下文中,您的C ++代码应该使用C ++习惯用法,例如std::thread。如果你 要使用C语言,那么在pthread_create的上下文中提供可移植性,清洁度和可维护性的良好平衡,你应该动态分配这个对象!但是,还有其他选择。避免动态分配似乎是一种不成熟的优化;它是最简单的解决方案(除了使用C ++习语)。我们可以整天躲避最简单的解决方案,直到我们到达疯狂的边缘,但这不会太有用,不是吗?

  

......解决方案是什么?

其他 API的上下文中,天空是极限。 C ++具有一系列功能,使生活更轻松,但不会引入明显的开销或复杂性。在我们设计API时,我们应该尽量保持可维护性......

pthread_create的背景下,有两个几乎是疯狂的替代方案:

  1. 可以将指针传递给一个结构,该结构具有包含互斥锁和整数的自动存储持续时间,并在使用pthread_create或{{pthread_rwlock_t调用后进行同步1}}以确保对象保持足够长的时间。但是,这似乎比使用具有动态存储持续时间的对象更有效。您是否注意到我们如何慢慢接近疯狂
  2. 或者,您将指针传递给具有静态存储持续时间的对象。这仅适用于创建一个新线程。通过使未来的维护和优化复杂化,这将向不同的方向发展疯狂
  3. 您有什么理由避免使用更合理的选项pthread_mutex_t

      

    ......此类API应该使用std::thread代替uintptr_t吗?

    我认为这取决于API。这是设计阶段的决定。在void*的上下文中,该API是POSIX的一部分。我想明确指出,正如POSIX世界目前所见,pthread_create 可以调用的唯一函数必须pthread_create作为参数和< / em>返回void *。但是,POSIX标准似乎并不要求这些指针指向 任何东西。

    在C ++和POSIX的世界中,void *类型是可选,其中uintptr_t类型是必需。任何想要使用void *的API都应该考虑到这个选项; uintptr_t在POSIX.1-2008世界中似乎不是可选的,这样的改变可能会破坏可移植性,因为我们很快就会探索,因此我不希望对POSIX进行更改<pthread.h> API。

    如果pthread 确实存在,则可以保证从uintptr_tuintptr_t并返回void *的转换会产生相同的值,因此uintptr_t(或类似的使用pthread_create(..., fubar, (void *) 42))可以明确定义,只要reinterpret_cast 存在uintptr_t执行逆转换(即{{{ 1}}将等于42)。

    同样,从fubar(uintptr_t) context(即在您致电void *(*)(uintptr_t)),并返回void *(*)(void *) 的转换会产生一个功能可以调用的指针。 但是,将函数调用为错误类型会产生未定义的行为! C标准(POSIX标准采用)实际上比我解释这个更好,所以here's an extract from C11/6.3.2.3p8

      

    指向一种类型的函数的指针可以转换为指向另一种类型的函数的指针,然后再返回;结果应该等于原始指针。如果转换的指针用于调用类型与引用类型不兼容的函数,则行为未定义。