std::any_cast 在将 void* 转换为函数指针时抛出错误 any_cast

std::any_cast throws a bad any_cast error when converting void* to function pointer

下面是我写的代码,

std::unordered_map<std::string_view, std::any> symbols_;
symbols_["foo"] = dlsym(handle_), "foo");

当我使用 any_cast return (std::any_cast<void(*)()>(symbols_["foo"]))();,程序会报错:bad any_cast.

我找到主要原因是因为函数.

template<typename _Tp>
void* __any_caster(const any* __any)

判断条件为false然后returnnullptr.

else if (__any->_M_manager == &any::_Manager<_Up>::_S_manage
#if __cpp_rtti
      || __any->type() == typeid(_Tp)
#endif
      ){
      any::_Arg __arg;
      __any->_M_manager(any::_Op_access, __any, &__arg);
      return __arg._M_obj;
}
return nullptr;

我想知道

1.why __any->_M_manager == &any::_Manager<_Up>::_S_manage__any->type() == typeid(_Tp) 都是假的,

2.and我该如何解决这个问题(继续使用std::any)。

这是一个简单的演示。

#include <any>

void func() { }

auto main() -> int {
    std::any a = (void*)func;
    std::any_cast<void(*)()>(a)();
    return 1;
}

gcc 版本 10.1.0 (GCC)

std::any_cast 只会转换回 std::any 中存储的类型。如dlsymreturnsvoid*,就是存储在std::any.

中的内容

在存储到 std::any 之前或在 std::any_cast:

之后,您需要单独转换为 void(*)()
std::unordered_map<std::string_view, std::any> symbols_;
symbols_["foo"] = reinterpret_cast<void(*)()>(dlsym(handle_), "foo"));
return (std::any_cast<void(*)()>(symbols_["foo"]))();

这里你在std::any对象中存储了一个void*

symbols_["foo"] = dlsym(handle_, "foo");

要改为存储 void(*)(),您需要转换 dlsym 返回的 void*

symbols_["foo"] = reinterpret_cast<void(*)()>(dlsym(handle_, "foo"));

在这种情况下,您可能只想存储 void* 并在使用它时进行转换:

std::unordered_map<std::string_view, void*> symbols_;
symbols_["foo"] = dlsym(handle_, "foo");
//...
return reinterpret_cast<void(*)()>(symbols_["foo"])();

如果您不需要在 unordered_map 中进行运行时查找,第三种选择是将函数指针存储在命名变量中。这使得使用起来更容易一些。这是一个例子:

loading/unloading 共享库的通用 class

class Lib { 
public:
    explicit Lib(const char* filename, int flags = RTLD_LAZY) :
        lib(dlopen(filename, flags)) 
    {
        if(!lib) throw std::runtime_error(dlerror());
    }

    Lib(const Lib&) = delete;
    Lib(Lib&& rhs) = delete;
    Lib& operator=(const Lib&) = delete;
    Lib& operator=(Lib&& rhs) = delete;
    virtual ~Lib() { dlclose(lib); }

private:
    struct cast_proxy { // a class to cast to the proper pointer
        // cast to whatever that is needed:
        template<class Func>
        operator Func () { return reinterpret_cast<Func>(sym); }
        void* sym;
    };

protected:
    cast_proxy sym(const char* symbol) const {
        void* rv = dlsym(lib, symbol);
        if(rv) return {rv};                   // put it in the cast_proxy
        throw std::runtime_error(dlerror());
    }

private:
    void* lib;
};

A class 用于加载特定的共享库:

class YourLib : public Lib {
public:
    YourLib() : Lib("./libyour_library.so"),
        // load all symbols here:
        foo(sym("foo"))   // the cast proxy will return the correct pointer type
    {}

    // Definitions of all the symbols you want to load:
    void(*const foo)();
};

那么使用起来就这么简单:

int main() {
    YourLib ml;
    ml.foo();
}