Подтвердить что ты не робот

Назначение указателей функций на С++

У меня есть указатель на void, возвращаемый dlsym(), я хочу вызвать функцию, указанную указателем void. Поэтому я делаю преобразование типа путем кастинга:

void *gptr = dlsym(some symbol..) ;
typedef void (*fptr)();
fptr my_fptr = static_cast<fptr>(gptr) ;

Я также пробовал reinterpret_cast, но не повезло, хотя, похоже, работает оператор C cast.

4b9b3361

Ответ 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.