我怎样才能摆脱这个reinterpret_cast,或者这个用法可以吗?
我有一个具有以下签名的模板成员函数:
template<typename T> void sync(void (*work)(T*), T context);
可以使用指向接受 T*
类型参数的函数的指针来调用它。 context
被传递给该函数。实现是这样的:
template<typename T> void queue::sync(void (*work)(T*), T context) {
dispatch_sync_f(_c_queue, static_cast<void*>(&context),
reinterpret_cast<dispatch_function_t>(work));
}
它使用reinterpret_cast<>
并且它可以工作。问题是标准没有很好地定义它,而且非常危险。我怎样才能摆脱这个?我尝试了 static_cast
但这给了我一个编译器错误:
static_cast
从void (*)(std::__1::basic_string
到*) dispatch_function_t
(又名不允许 void (*)(void *)
)。
dispatch_function_t
是 C 类型,与 void (*)(void*)
相同。
我不确定我说得足够清楚。 dispatch_sync_f 的作用是调用给定的回调函数并将给定的上下文参数传递给该回调函数。 (它在另一个线程上执行此操作,尽管这超出了本问题的范围。)
I have a template member function with this signature:
template<typename T> void sync(void (*work)(T*), T context);
It can be called with a pointer to a function that accepts an argument of type T*
. context
is passed to that function. The implementation is this:
template<typename T> void queue::sync(void (*work)(T*), T context) {
dispatch_sync_f(_c_queue, static_cast<void*>(&context),
reinterpret_cast<dispatch_function_t>(work));
}
It uses reinterpret_cast<>
and it works. The problem is that the standard doesn't define it very well and it is very dangerous. How can I get rid of this? I tried static_cast
but that gave me a compiler error:
static_cast
fromvoid (*)(std::__1::basic_string<char> *)
todispatch_function_t
(akavoid (*)(void *)
) is not allowed.
dispatch_function_t
is a C type and is the same as void (*)(void*)
.
I'm not sure I was clear enough. What dispatch_sync_f
does is it calls a given callback function and passes the given context parameter to that callback function. (It does that on another thread, although that is out of the scope of this question.)
如果你对这篇内容有疑问,欢迎到本站社区发帖提问 参与讨论,获取更多帮助,或者扫码二维码加入 Web 技术交流群。
绑定邮箱获取回复消息
由于您还没有绑定你的真实邮箱,如果其他用户或者作者回复了您的评论,将不能在第一时间通知您!
发布评论
评论(5)
static_cast
不支持这一点的原因是它是可能不安全。而
std::string*
将隐式转换为a
void*
,两者不是一回事。正确的解决方案是为您的函数提供一个简单的包装类,该类采用
void*
,并
static_cast
将其返回到所需的类型,并传递地址这个包装函数到你的函数。 (实际上,在现代
机器,你将摆脱
reinterpret_cast
,因为所有指向数据的指针具有相同的大小和格式。无论你想剪吗
像这样的角落取决于你——但在某些情况下,它是
有理有据。我只是不相信这是其中之一,因为
简单的解决方法。)
编辑:还有一点:你说
dispatch_function_t
是一种 C 类型。如果是这种情况,实际类型可能是extern "C" void (*)(void*)
,并且您只能使用具有"C"
的函数来初始化它> 联动。 (同样,您可能会侥幸逃脱,但我使用的编译器的调用约定对于"C"
和"C++"
是不同的。)The reason this is not supported by
static_cast
is because it ispotentially unsafe. While a
std::string*
will convert implicitely toa
void*
, the two are not the same thing. The correct solution is toprovide a simple wrapper class to your function, which takes a
void*
,and
static_cast
s it back to the desired type, and pass the address ofthis wrapper function to your function. (In practice, on modern
machines, you'll get away with the
reinterpret_cast
, since allpointers to data have the same size and format. Whether you want to cut
corners like this is up to you—but there are cases where it's
justified. I'm just not convinced that this is one of them, given the
simple work-around.)
EDIT: One additional point: you say that
dispatch_function_t
is a C type. If this is the case, the actual type if probablyextern "C" void (*)(void*)
, and you can only initialize it with functions that have"C"
linkage. (Again, you're likely to get away with it, but I've used compilers where the calling conventions were different for"C"
and"C++"
.)我想,您不仅将
work
转换为dispatch_function_t
,而是通过dispatch_function_t
指针调用它,不是吗?根据标准,这种强制转换本身是有效的,但您对强制转换指针所能做的就是将其强制转换回原始类型。不过,您的方法应该适用于大多数编译器和平台。如果您想实现它,使其更符合标准,您可以为您的context
和work
函数制作一个包装器,如下所示:I guess, you are not only casting
work
todispatch_function_t
, but calling it throughdispatch_function_t
pointer, aren't you? Such cast itself is valid according to standard, but all you can do with a casted pointer is cast it back to original type. Still your approach should work with most compilers and platforms. If you'd like to implement it so it's more standard conforming you can make a wrapper for yourcontext
andwork
function like this:我不敢相信这有效,或者你对“这有效”的定义相当狭窄(例如,你发现了一个特定的设置,它似乎做了你认为应该做的事情)。我不清楚 dispatch_sync_f() 的作用,但我认为它获取指向局部变量 context 的指针作为参数是可疑的。假设此变量的寿命超过了此指针的使用期限,则仍然存在一个微妙的问题,该问题不会让您在大多数平台上使用,但会在某些平台上使用:
C 和 C++ 调用约定可能不同。也就是说,您不能将指向 C++ 函数的指针强制转换为指向 C 函数的指针并希望它是可调用的。当然,这个问题以及您最初的问题的解决方案是额外的间接级别:不要分派到作为参数获得的函数,而是分派到 C 函数(即声明为
的 C++ 函数) extern "C"
) 它采用自己的上下文来保存原始上下文和原始函数并调用原始函数。唯一需要的[显式]转换是static_cast<>()
从void*
恢复指向内部上下文的指针。由于您似乎实现了一个模板,因此您可能需要使用另一个间接寻址来摆脱这种类型:我不认为函数模板可以声明为 extern "C" 。因此,您需要以某种方式恢复原始类型,例如使用基类和虚拟函数或类似
std::function
的东西,持有一个易于调用的函数对象来执行此转换(a指向该对象的指针将是您的上下文)。I can't believe this works or you have a rather narrow definition of "this works" (e.g. you found one particular setup where it seems to do what you think it should do). I'm not clear what
dispatch_sync_f()
does but I think it is suspicious that it gets a pointer to the local variablecontext
as parameter. Assuming this variable outlives the use of this pointer, there is still a subtle problem which won't get you on most platforms but does get you on some:C and C++ calling conventions can be different. That is, you cannot cast a pointer to a C++ function to a pointer to a C function and hope for this to be callable. The fix to this problem - and your original question - is, of course, an extra level of indirection: don't dispatch to the function you get as argument but rather dispatch to a C function (i.e. a C++ function declared as
extern "C"
) which takes its own context holding both the original context and the original function and calls the original function. The only [explicit] cast needed is thestatic_cast<>()
restoring a pointer to your internal context from thevoid*
.Since you seem to implement a template you might need to use another indirection to get rid of this type: I don't thing function templates can be declared
extern "C"
. So you would need to restore the original type somehow e.g. using a base class and a virtual function or something likestd::function<void()>
holding a readily callable function object doing this conversion (a pointer to this object would be your context).我相信这两种函数指针类型之间的转换很好:
问题是您实际上无法使用这样转换的指针。只有强制转换回原始类型才是合法的(这些强制转换是
reinterpret_cast
,因为这些是不相关的类型)。从您的代码中,我看不到您的实际回调函数是如何定义的。为什么不能接受dispatch_function_t
作为queue::sync
的参数,而不是强制转换它?I believe the cast to/from these two function pointer types is fine:
The problem is that you can't actually use the pointer that you have so cast. It's legal only to cast back to the original type (and those casts are
reinterpret_cast
, because these are unrelated types). From your code, I can't see how your actual callback function is defined. Why can't you accept adispatch_function_t
as your parameter forqueue::sync
, rather than casting it?在从类型
T *
到void *
类型相互转换时,reinterpret_cast
保证正常工作。但是,从指向 T 的基类或派生类的指针进行转换是不可接受的。在这种情况下,
work
的类型需要为dispatch_function_t
,并且该函数中的首要任务是从void *
到T *
的转换。不允许使用不同的参数类型隐式转换参数和转换函数类型。理由:标准允许不同类型有不同的指针表示,只要所有指针类型都可以转换为
void *
并返回,因此void *
是“最精确的”指针类型。如果 sizeof(uint32_t) > 则允许一致的实现清除 uint32_t * 的低位。 sizeof(char)(即sizeof(uint32_t) > 1
),或者如果机器指令可以更有效地利用这些指针,甚至可以移动指针值;在带有标记或移位指针值的机器上,reinterpret_cast 不一定是空操作,需要显式编写。reinterpret_cast
is guaranteed to work when converting from a typeT *
tovoid *
and back. It is, however, not acceptable to cast from or to a pointer to a base or derived class of T.The type of
work
needs to bedispatch_function_t
in this case, and the first order of business in that function needs to be the cast fromvoid *
toT *
. Implicitly casting the argument by using a different argument type and casting the function type is not allowed.Rationale: the standard allows different pointer representations for different types, as long as all pointer types can be converted to
void *
and back, sovoid *
is the "most precise" pointer type. A conforming implementation is allowed to clear the bottom-order bits of anuint32_t *
ifsizeof(uint32_t) > sizeof(char)
(i.e.sizeof(uint32_t) > 1
) or even shift the pointer value if the machine instructions can utilize these pointers more effectively; on a machine with tagged or shifted pointer values thereinterpret_cast
is not necessarily a no-op and needs to be written explicitly.