#c #dlopen #any
Вопрос:
Я написал код ниже,
std::unordered_maplt;std::string_view, std::anygt; symbols_; symbols_["foo"] = dlsym(handle_), "foo");
Когда я использую any_cast return (std::any_castlt;void(*)()gt;(symbols_["foo"]))();
, программа выдаст ошибку: bad any_cast.
Я нашел главную причину из-за функции .
templatelt;typename _Tpgt; void* __any_caster(const any* __any)
Он будет оценивать условие как ложное, а затем возвращать значение nullptr.
else if (__any-gt;_M_manager == amp;any::_Managerlt;_Upgt;::_S_manage #if __cpp_rtti || __any-gt;type() == typeid(_Tp) #endif ){ any::_Arg __arg; __any-gt;_M_manager(any::_Op_access, __any, amp;__arg); return __arg._M_obj; } return nullptr;
Я хочу знать
1.почему __any-gt;_M_manager == amp;any::_Managerlt;_Upgt;::_S_manage
и __any-gt;type() == typeid(_Tp)
были ли все ложными,
2.и как я могу устранить проблему(продолжайте использовать std::любой).
Вот простая демонстрация.
#include lt;anygt; void func() { } auto main() -gt; int { std::any a = (void*)func; std::any_castlt;void(*)()gt;(a)(); return 1; }
gcc версия 10.1.0 (GCC)
Комментарии:
1. Потому что тип
dlsym(handle_, "foo")
— это неvoid(*)()
«ноvoid*
«.2. Вы сохранили
void*
, это за пределами разумного, что он выбросит, если вы попытаетесь получить другой тип, такой какchar*
или(void*)()
. Серьезно, почему ты вообще спрашиваешь?3. Могу ли я предложить просто сделать карту типа:
unordered_maplt;string, void*gt;
. В любом случае вам придется использовать функцию reinterpret_cast со значением void* для функции, которую вы ожидаете.4. @selbie ну, с
std::any
его помощью он может сохранить правильную подпись с самого начала, а затем убедиться, что при каждом использовании используется одна и та же подпись. Ничегоvoid*
не поделаешь.5.
symbols_["foo"] = reinterpret_castlt;void(*)()gt;(dlsym(handle_, "foo"));
казалось бы, более уместно
Ответ №1:
Здесь вы сохраняете a void*
в std::any
объекте:
symbols_["foo"] = dlsym(handle_, "foo");
Чтобы вместо этого сохранить a void(*)()
, вам нужно привести значение void*
, возвращаемое dlsym
:
symbols_["foo"] = reinterpret_castlt;void(*)()gt;(dlsym(handle_, "foo"));
В этом случае вы можете просто сохранить void*
и привести при его использовании вместо этого:
std::unordered_maplt;std::string_view, void*gt; symbols_; symbols_["foo"] = dlsym(handle_, "foo"); //... return reinterpret_castlt;void(*)()gt;(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 Libamp;) = delete; Lib(Libamp;amp; rhs) = delete; Libamp; operator=(const Libamp;) = delete; Libamp; operator=(Libamp;amp; rhs) = delete; virtual ~Lib() { dlclose(lib); } private: struct cast_proxy { // a class to cast to the proper pointer // cast to whatever that is needed: templatelt;class Funcgt; operator Func () { return reinterpret_castlt;Funcgt;(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(); }
Ответ №2:
std::any_cast
приведет только к возвращению к типу, который был сохранен в std::any
. Как dlsym
возвращает void*
, это то, что хранится в std::any
.
Вам нужно отдельное приведение void(*)()
либо перед хранением, std::any
либо после std::any_cast
:
std::unordered_maplt;std::string_view, std::anygt; symbols_; symbols_["foo"] = reinterpret_castlt;void(*)()gt;(dlsym(handle_), "foo")); return (std::any_castlt;void(*)()gt;(symbols_["foo"]))();