【问题标题】:std::any_cast throws a bad any_cast error when converting void* to function pointer将 void* 转换为函数指针时,std::any_cast 抛出错误的 any_cast 错误
【发布时间】:2021-12-02 13:33:02
【问题描述】:

我写了下面的代码,

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

当我使用 any_cast return (std::any_cast&lt;void(*)()&gt;(symbols_["foo"]))();,程序会抛出错误:bad any_cast。

因为功能,我找到了主要原因。

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

判断条件为假,然后返回nullptr。

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.为什么__any-&gt;_M_manager == &amp;any::_Manager&lt;_Up&gt;::_S_manage__any-&gt;type() == typeid(_Tp)都是假的,

2.我该如何解决这个问题(继续使用 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)

【问题讨论】:

  • 因为dlsym(handle_, "foo")的类型不是void(*)()而是void*
  • 您存储了void*,如果您尝试获取char*(void*)() 之类的不同类型,它会抛出它是不合理的。说真的,你为什么还要问?
  • 我建议只制作以下类型的地图:unordered_map&lt;string, void*&gt;。无论如何,您都必须将 reinterpret_cast 用于您期望的函数的 void* 值。
  • @selbie 好吧,std::any 他可以从一开始就存储正确的签名,然后确保每次使用都使用相同的签名。 void* 做不到。
  • symbols_["foo"] = reinterpret_cast&lt;void(*)()&gt;(dlsym(handle_, "foo")); 似乎更合适

标签: c++ dlopen any


【解决方案1】:

std::any_cast 只会转换回存储在std::any 中的类型。由于dlsym 返回void*,这就是存储在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"]))();

【讨论】:

    【解决方案2】:

    在这里,您将void* 存储在std::any 对象中:

    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 中进行运行时查找,第三种选择是将函数指针存储在命名变量中。这使得使用更容易一些。这是一个例子:

    用于加载/卸载共享库的通用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;
    };
    

    用于加载特定共享库的类:

    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();
    }
    

    【讨论】:

      猜你喜欢
      • 2019-12-17
      • 1970-01-01
      • 2012-11-02
      • 1970-01-01
      • 1970-01-01
      • 2016-12-01
      • 2017-07-27
      • 1970-01-01
      • 2020-01-22
      相关资源
      最近更新 更多