I wrote the code below,
std::unordered_map<std::string_view, std::any> symbols_;
symbols_["foo"] = dlsym(handle_), "foo");
When i use any_cast
return (std::any_cast<void(*)()>(symbols_["foo"]))();
, the program will throw a error: bad any_cast.
I found the main reason because of the function .
template<typename _Tp>
void* __any_caster(const any* __any)
It would judge the condition as false and then return 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;
I want to know
1.why __any->_M_manager == &any::_Manager<_Up>::_S_manage
and __any->type() == typeid(_Tp)
were all false,
2.and how can i fix the problem(continue to use std::any).
Here is a simple demo.
#include <any>
void func() { }
auto main() -> int {
std::any a = (void*)func;
std::any_cast<void(*)()>(a)();
return 1;
}
gcc version 10.1.0 (GCC)
CodePudding user response:
Here you store a void*
in the std::any
object:
symbols_["foo"] = dlsym(handle_, "foo");
To instead store a void(*)()
, you need to cast the void*
that's returned by dlsym
:
symbols_["foo"] = reinterpret_cast<void(*)()>(dlsym(handle_, "foo"));
In this case, you may want to just store the void*
and cast when using it instead:
std::unordered_map<std::string_view, void*> symbols_;
symbols_["foo"] = dlsym(handle_, "foo");
//...
return reinterpret_cast<void(*)()>(symbols_["foo"])();
A third option, if you don't need the runtime lookup in the unordered_map
, is to instead store the function pointers in named variables. That makes the usage a little easier. Here's an example:
A generic class
for loading/unloading a shared library:
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 for loading a specific shared library:
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)();
};
Then using it will be as simple as this:
int main() {
YourLib ml;
ml.foo();
}
CodePudding user response:
std::any_cast
will only cast back to the type that was stored in the std::any
. As dlsym
returns void*
, that is what is stored in the std::any
.
You need a separate cast to void(*)()
either before storing in std::any
or after the std::any_cast
:
std::unordered_map<std::string_view, std::any> symbols_;
symbols_["foo"] = reinterpret_cast<void(*)()>(dlsym(handle_), "foo"));
return (std::any_cast<void(*)()>(symbols_["foo"]))();