简体   繁体   English

我可以检查 void* 的实际类型吗?

[英]Can I check the actual type of void*?

I have to use a legacy C library in my C++ code.我必须在我的 C++ 代码中使用旧版 C 库。 One of the functions of that library looks like this:该库的功能之一如下所示:

int legacyFunction(int (*userDefinedPredicateFunction)(void*), void* structure, otherArgs...);

This legacyFunction() calls userDefinedPredicateFunction() inside itself passing structure as argument to it.这个legacyFunction() ) 在自身内部调用userDefinedPredicateFunction()并将structure作为参数传递给它。 I have multiple custom predicate functions to be used with the above function.我有多个自定义谓词函数可与上述函数一起使用。 Each of these functions works different from the others and expects an argument to be of a strictly defined type to work properly:这些函数中的每一个都与其他函数的工作方式不同,并且期望参数是严格定义的类型才能正常工作:

int userDefinedPredicateFunction1(void* structure)
{
    const expectedType1* const s = reinterpret_cast<expectedType1*>(structure);
    // Check s conditions...
}

int userDefinedPredicateFunction2(void* structure)
{
    const expectedType2* const s = reinterpret_cast<expectedType2*>(structure);
    // Check s conditions...
}

int userDefinedPredicateFunction3(void* structure)
{
    const expectedType3* const s = reinterpret_cast<expectedType3*>(structure);
    // Check s conditions...
}

The problem is that these predicate functions are not safe to use - passing an argument of not the expected type to them will lead to an undefined mess.问题是这些谓词函数使用起来不安全 - 将非预期类型的​​参数传递给它们将导致未定义的混乱。 I need to somehow check the type of the argument and throw if it is not the expected type.我需要以某种方式检查参数的类型,如果它不是预期的类型,则抛出。 structure is not polymorphic, so I cannot use dynamic_cast here. structure不是多态的,所以我不能在这里使用dynamic_cast

The first thing comes to mind is to use a wrapper function like this one:首先想到的是使用这样的包装函数:

int legacyFunctionWrapper1(const Type1& structure, otherArgs...)
{
    return legacyFunction(
        userDefinedPredicateFunction1, &structure, otherArgs...);
}

This will let structure only to be of a one certain type expected by the predicate function, but a dedicated wrapper function is needed to be written to be used with each predicate function, which is undesirable.这将使structure仅属于谓词函数所期望的一种特定类型,但是需要编写专用的包装函数以与每个谓词函数一起使用,这是不可取的。

Is there more elegant way to check the actual type of void* pointer?有没有更优雅的方法来检查void*指针的实际类型?

I think you are on the right path with that wrapper function.我认为您使用该包装器功能走在正确的道路上。 You can save yourself some work by making it a template.您可以通过将其设为模板来节省一些工作。 This should work:这应该有效:

using legacy_predicate = int (*)(void*);

template<class T>
void call_legacy(int (*predicate)(T*), T* obj, int otherargs)
{
    using pair_type = std::pair<int(*)(T*), T*>;
    pair_type realargs = std::make_pair(predicate, obj);
    legacy_predicate wrapper = +[](void* unsafe) -> int {
        pair_type& realargs = *static_cast<pair_type*>(unsafe);
        return realargs.first(realargs.second);
    };
    legacyFunction(wrapper, &realargs, otherargs);
}

Two things of note:有两点需要注意:

  1. I avoided reinterpret-casting the function pointer since that is technically undefined behavior (though it should work in practice).我避免重新解释转换函数指针,因为这在技术上是未定义的行为(尽管它应该在实践中起作用)。 This introduces some indirection and may be a bit slower这引入了一些间接性,可能会慢一些

  2. The trick is to turn a stateless lambda into a function pointer with the unary + operator诀窍是使用一元+运算符将无状态 lambda 转换为函数指针

To make up for the indirection and maybe modernize the whole thing a bit, consider this:为了弥补间接性并可能使整个事物现代化一点,请考虑以下几点:

template<class Functor>
void call_legacy(Functor predicate, int otherargs)
{
    legacy_predicate wrapper = +[](void* unsafe) -> int {
        Functor* predicate = static_cast<Functor*>(unsafe);
        return (*predicate)();
    };
    legacyFunction(wrapper, &predicate, otherargs);
}

This allows you to pass arbitrary functors, not just function pointers.这允许您传递任意函子,而不仅仅是函数指针。 So you can use it with lambdas or whatever you want.所以你可以将它与 lambdas 或任何你想要的东西一起使用。 To get the old pattern, you wrap the object at the call site, maybe make it into a overload.要获得旧模式,您可以在调用站点包装对象,可能会使其成为重载。 Like this:像这样:

template<class T>
void call_legacy(int (*predicate)(T*), T* obj, int otherargs)
{
    // dispatch to overloaded interface described above
    return call_legacy([predicate, obj]() -> int {
          return predicate(obj);
    }, otherargs);
}

Warning警告

This only works if legacyFunction does not store the passed void* beyond the runtime of the function.这仅在 legacyFunction 不存储传递的 void* 超出函数运行时才有效。 Beware of dangling pointers.当心悬空指针。

Alternative选择

As I've mentioned, casting function pointers is undefined behavior, but it tends to work in simple cases.正如我所提到的,转换函数指针是未定义的行为,但它往往适用于简单的情况。 Rule of thumb: If your platform supports GTK+, it supports simple function pointer casts (because Gtk even does horrible things like changing the number of function arguments).经验法则:如果你的平台支持 GTK+,它支持简单的函数指针转换(因为 Gtk 甚至会做一些可怕的事情,比如改变函数参数的数量)。

So this is the zero-overhead version:所以这是零开销版本:

using legacy_predicate = int (*)(void*);
template<class T>
void call_legacy(int (*predicate)(T*), T* obj, int otherargs)
{
    legacy_predicate unsafe = reinterpret_cast<legacy_predicate>(predicate);
    legacyFunction(unsafe, obj, otherargs);
}

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

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