std::any_cast выдает плохую ошибку any_cast при преобразовании void* в указатель функции

#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"]))();