简体   繁体   English

我怎样摆脱这个reinterpret_cast,或者这个用法好吗?

[英]How can I get rid of this reinterpret_cast, or is this usage OK?

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* . 可以使用指向接受类型为T*的参数的函数的指针来调用它。 context is passed to that function. context被传递给该函数。 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. 它使用reinterpret_cast<>并且它可以工作。 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但这给了我一个编译器错误:

static_cast from void (*)(std::__1::basic_string<char> *) to dispatch_function_t (aka void (*)(void *) ) is not allowed. static_castvoid (*)(std::__1::basic_string<char> *)dispatch_function_t (又名void (*)(void *) )。

dispatch_function_t is a C type and is the same as void (*)(void*) . dispatch_function_t是C类型,与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. dispatch_sync_f作用是调用给定的回调函数并将给定的上下文参数传递给该回调函数。 (It does that on another thread, although that is out of the scope of this question.) (它在另一个线程上执行此操作,但这超出了此问题的范围。)

The reason this is not supported by static_cast is because it is potentially unsafe. static_cast不支持这个原因是因为它可能不安全。 While a std::string* will convert implicitely to a void* , the two are not the same thing. 虽然std::string*会隐含地转换为void* ,但两者并不相同。 The correct solution is to provide 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 of this wrapper function to your function. 正确的解决方案是为您的函数提供一个简单的包装类,它将void*static_cast返回到所需类型,并将此包装函数的地址传递给您的函数。 (In practice, on modern machines, you'll get away with the reinterpret_cast , since all pointers 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.) (实际上,在现代的机器,你会得到摒弃了reinterpret_cast ,因为所有的数据指针具有相同的大小和格式。无论您想走捷径这样是给你,但也在那里的理由的情况下。考虑到简单的解决方法,我只是不相信这是其中之一。)

EDIT: One additional point: you say that dispatch_function_t is a C type. 编辑:还有一点:你说dispatch_function_t是C类型。 If this is the case, the actual type if probably extern "C" void (*)(void*) , and you can only initialize it with functions that have "C" linkage. 如果是这种情况,实际类型如果可能是extern "C" void (*)(void*) ,并且只能用具有"C"链接的函数初始化它。 (Again, you're likely to get away with it, but I've used compilers where the calling conventions were different for "C" and "C++" .) (同样,你可能会逃脱它,但我使用的编译器的调用约定对于"C""C++"是不同的。)

I guess, you are not only casting work to dispatch_function_t , but calling it through dispatch_function_t pointer, aren't you? 我想,你不仅要将work转移到dispatch_function_t ,而是通过dispatch_function_t指针调用它,不是吗? 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 your context and work function like this: 如果您想实现它,那么它更符合标准,您可以为您的contextwork函数创建一个包装器,如下所示:


template <typename T>
struct meta_context_t
{
  T *context;
  void (*work)(T*);
};

template <typename T>
void thunk(void *context)
{
  meta_context_t<T> *meta_context = static_cast<meta_context_t<T> *>(context);
  meta_context->work(meta_context->context);
}

template<typename T> void queue::sync(void (*work)(T*), T context) {
  meta_context_t<T> meta_context =
  {
    &context,
    work
  };

  dispatch_sync_f(_c_queue, static_cast<void*>(&meta_context),
                thunk<T>);
}

I can't believe this works or you have a rather narrow definition of "this works" (eg 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 variable context as parameter. 我不清楚dispatch_sync_f()做了什么,但我认为它获得一个指向本地变量context作为参数的指针是可疑的。 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. C和C ++调用约定可以不同。 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. 也就是说,您不能将指向C ++函数的指针强制转换为指向C函数的指针,并希望它可以调用。 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 (ie 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. 解决这个问题的方法 - 以及你原来的问题 - 当然是一个额外的间接级别:不要调度你作为参数获得的函数,而是调度到C函数(即声明为extern "C"的C ++函数extern "C" )它拥有自己的上下文,同时保存原始上下文和原始函数,并调用原始函数。 The only [explicit] cast needed is the static_cast<>() restoring a pointer to your internal context from the void* . 唯一需要的[explicit] static_cast<>()static_cast<>()void*恢复指向内部上下文的指针。

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" . 由于您似乎实现了一个模板,您可能需要使用另一个间接来摆脱这种类型:我不会将函数模板声明为extern "C" So you would need to restore the original type somehow eg using a base class and a virtual function or something like std::function<void()> holding a readily callable function object doing this conversion (a pointer to this object would be your context). 因此,您需要以某种方式恢复原始类型,例如使用基类和虚函数或类似std::function<void()>保存一个可以调用的函数对象进行此转换(指向此对象的指针将是您的上下文) )。

I believe the cast to/from these two function pointer types is fine: 我相信这两个函数指针类型的转换很好:

void(*)(void*)
void(*)(T*)

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). 仅返回原始类型是合法的(这些转换是reinterpret_cast ,因为它们是不相关的类型)。 From your code, I can't see how your actual callback function is defined. 从您的代码中,我看不到您的实际回调函数是如何定义的。 Why can't you accept a dispatch_function_t as your parameter for queue::sync , rather than casting it? 为什么不能接受dispatch_function_t作为queue::sync参数,而不是将其转换为?

reinterpret_cast is guaranteed to work when converting from a type T * to void * and back. 当从类型T *转换为void *并返回时, reinterpret_cast保证可以正常工作。 It is, however, not acceptable to cast from or to a pointer to a base or derived class of T. 但是,从指向T或T的基类或派生类的指针进行强制转换是不可接受的。

The type of work needs to be dispatch_function_t in this case, and the first order of business in that function needs to be the cast from void * to T * . 在这种情况下, work类型需要是dispatch_function_t ,并且该函数中的第一个业务顺序需要是从void *T * 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, so void * is the "most precise" pointer type. 基本原理:标准允许不同类型的不同指针表示,只要所有指针类型都可以转换为void *和back,因此void *是“最精确”的指针类型。 A conforming implementation is allowed to clear the bottom-order bits of an uint32_t * if sizeof(uint32_t) > sizeof(char) (ie sizeof(uint32_t) > 1 ) or even shift the pointer value if the machine instructions can utilize these pointers more effectively; 如果sizeof(uint32_t) > sizeof(char) (即sizeof(uint32_t) > 1 ),则允许一致的实现清除uint32_t *的底部位,或者如果机器指令可以使用这些指针,则甚至移位指针值有效; on a machine with tagged or shifted pointer values the reinterpret_cast is not necessarily a no-op and needs to be written explicitly. 在具有标记或移位指针值的机器上, reinterpret_cast不一定是无操作,需要明确写入。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM