Назначение указателей функций на С++
У меня есть указатель на void, возвращаемый dlsym(), я хочу вызвать функцию, указанную указателем void.
Поэтому я делаю преобразование типа путем кастинга:
void *gptr = dlsym(some symbol..) ;
typedef void (*fptr)();
fptr my_fptr = static_cast<fptr>(gptr) ;
Я также пробовал reinterpret_cast
, но не повезло, хотя, похоже, работает оператор C cast.
Ответы
Ответ 1
Преобразование void*
в указатель на функцию напрямую не допускается (не должно компилироваться с использованием любых приведений) в С++ 98/03. Он условно поддерживается в С++ 0x (реализация может выбрать определение поведения, и если оно его определяет, то оно должно делать то, что в стандарте сказано, что должно. A void*
, как определено в С++ 98/03 стандарт, должен был указывать на объекты, а не содержать указатели на функции или указатели на элементы.
Зная, что то, что вы делаете, сильно зависит от реализации, вот один вариант, который должен компилироваться и работать (предполагая 32-битные указатели, использовать long long
для 64-битных) на большинстве платформ, даже если это явно неопределенное поведение согласно стандарту:
void *gptr = dlsym(some symbol..) ;
typedef void (*fptr)();
fptr my_fptr = reinterpret_cast<fptr>(reinterpret_cast<long>(gptr)) ;
А вот еще один вариант, который должен компилироваться и работать, но с теми же оговорками, что и выше:
fptr my_ptr = 0;
reinterpret_cast<void*&>(my_ptr) = gptr;
Или в замедленном режиме...
// get the address which is an object pointer
void (**object_ptr)() = &my_ptr;
// convert it to void** which is also an object pointer
void ** ppv = reinterpret_cast<void**>(object_ptr);
// assign the address in the memory cell named by 'gptr'
// to the memory cell that is named by 'my_ptr' which is
// the same memory cell that is pointed to
// by the memory cell that is named by 'ppv'
*ppv = gptr;
Он по существу использует тот факт, что адрес указателя функции является указателем объекта (void (**object_ptr)()
) - поэтому мы можем использовать reinterpret_cast
для преобразования его в любой другой указатель объекта: такой как void**
. Затем мы можем следовать по адресу назад (разыменовав void**
) к фактическому указателю функции и сохранить там значение gptr.
Юк - отнюдь не четко определенный код - но он должен делать то, что вы ожидаете, что он делает в большинстве реализаций.
Ответ 2
Обратите внимание, что C++ 11 разрешает такое преобразование, и из gcc 4.9 и выше это преобразование не генерирует предупреждение: https://gcc.gnu.org/bugzilla/show_bug.cgi?id=57869.
Смотрите ТАКИЕ обсуждения:
Ответ 3
Я нашел это (немного уродливое) решение.
gcc с максимальным уровнем предупреждения не жалуется.
В этом примере вызывается dlsym() (который возвращает void *) и возвращает результат указателю функции.
typedef void (*FUNPTR)();
FUNPTR fun_dlsym(void* handle, const char* name) {
union {
void* ptr;
FUNPTR fptr;
} u;
u.ptr = dlsym(handle, name);
return u.fptr;
}
Ответ 4
Это компилируется в Visual Studio без использования реинтерпрета:
void *ptr;
int (*func)(void) = (int(*)(void))ptr;
int num = func();
Ответ 5
Можно использовать следующий метод:
int (*fn)(int);
*(void **)(&fn) = dlsym(lib1, "function");
int result = (*fn)(3);
Или
fn = (int (*)(int))dlsym(lib1, "function");
Составлено:
g++ -Wall -pedantic -std=c++11
Ответ 6
Вы можете привести dlsym
к функции, которая возвращает требуемый указатель, а затем вызвать его следующим образом:
typedef void (*fptr)();
fptr my_fptr = reinterpret_cast<fptr (*)(void*, const char*)>(dlsym)(RTLD_DEFAULT, name);
PS. Приведение указателя функции к другому указателю на функцию, а затем его вызов - это неопределенное поведение (см. Пункт 7 в https://en.cppreference.com/w/cpp/language/reinterpret_cast), поэтому лучше привести результат dlsym
к uintptr_t
а затем к требуемому типу:
fptr my_fptr = reinterpret_cast<fptr>(reinterpret_cast<uintptr_t>(dlsym(RTLD_DEFAULT, name)));
Ответ 7
Это может вам помочь. Он печатает "Привет".
#include <iostream>
void hello()
{
std::cout << "Hello" << std::endl;
}
int main() {
typedef void (*fptr)();
fptr gptr = (fptr) (void *) &hello;
gptr();
}
ИЛИ вы можете сделать:
fptr gptr = reinterpret_cast<fptr>( (void *) &hello);
где & hello заменяется командой dlsym.