Неправильное значение с функцией, которая возвращает двойной (libffi) - PullRequest
0 голосов
/ 03 мая 2020

Кажется, что функция, возвращающая двойной тип данных из libffi, неправильно приводится, когда функция возвращает свое значение, вот код, который я использовал:

#include <stdio.h>
#include <stdlib.h>
#include <ffi.h>
#include <math.h>  // The make call the function `cos` by the FFI.

int main()
{
  ffi_cif cif;
  ffi_type *args[1];
  void *values[1];
  ffi_arg rc;

  args[0] = &ffi_type_double;

  void *ptr = malloc(sizeof(double));
  *((double *)ptr) = 3.0;
  values[0] = ptr;

  if (ffi_prep_cif(&cif, FFI_DEFAULT_ABI, 1, &ffi_type_double, args) == FFI_OK)
      ffi_call(&cif, cos, &rc, values);

  printf("%f\n", (double)rc);

  return 0;
}

Результат выглядит следующим образом: 13830464316077631000.000000.

Тип ffi_arg является псевдонимом для unsigned long long. В документации говорится, что аргумент типа ffi_arg, передаваемый в функцию ffi_call, используется для хранения данных, если они меньше sizeof(ffi_arg), в противном случае это указатель на данные в памяти:

rvalue должно указывать на хранилище, которое sizeof(ffi_arg) или больше для типов без плавающей запятой. Для типов возвращаемых значений меньшего размера для хранения возвращаемого значения должен использоваться интегральный тип ffi_arg или ffi_sarg.

(https://manpages.debian.org/testing/libffi-dev/ffi_call.3.en.html)

Итак, я попробовал это с разыменованием указателя:

void* rc;
// The code to call the function and initialize the CIF...
double my_value = *(double *)((void *)rc);

, что привело к сбою программы.

Как мне получить доступ к значению, хранящемуся с помощью переменной rc?

РЕДАКТИРОВАТЬ 1

Командная строка, используемая для компиляции программы:

gcc source.c -lffi -o source

Нет ошибок или предупреждений во время компиляции.

РЕДАКТИРОВАТЬ 2

После добавления опции сборки `-Wall 'я получаю:

warning: passing argument 2 of 'ffi_call' from incompatible pointer type [-Wincompatible-pointer-types]
ffi_call(&cif, cos, &rc, values);
               ^~~

Это предупреждение, похоже, игнорируется в libffi пример . Приведенный пример работает очень хорошо для меня (с этим предупреждением).

Ответы [ 2 ]

3 голосов
/ 03 мая 2020

Проблема простая . ffi_arg не тот тип, в который вы должны поместить возвращаемое значение. Вместо этого 3-й аргумент определяется как указатель на void , который должен указывать на объект , который достаточно большой , чтобы содержать возвращаемое значение и правильное выравнивание, и правильного типа или интерпретируется как таковой, например:

double rv;
...
ffi_call(&cif, cos, &rv, values);

или

void *rv = malloc(sizeof (double));
...
ffi_call(&cif, cos, rv, values);
double value = *(double *)rv;

Что касается предупреждения, это происходит потому, что код libffi не является строго портативный. Указатель функции не может быть автоматически преобразован в void *. Вы можете отключить предупреждение с явным приведением:

ffi_call(&cif, (void *)cos, rv, values);

По крайней мере, оно не делает его более неправильным, чем уже есть.

1 голос
/ 03 мая 2020

РЕДАКТИРОВАТЬ - Согласно документации

FFI - значение должно указывать на хранилище, размер которого sizeof (ffi_arg) или больше для типов без плавающей запятой. Для типов возвращаемых значений меньшего размера для хранения возвращаемого значения должен использоваться целочисленный тип ffi_arg или ffi_sarg.

Определение функции для ffi_call:

void ffi_call(ffi_cif *cif, void (*fn)(void), void *rvalue, void **avalue);

Вместо передачи ffi_arg для функции, которую вам нужно передать void *, который выделен двойным пространством.

void* rc = malloc(sizeof(double));
ffi_call(&cif, cos, rc, values);
printf("%f\n", *(double *)rc);

Это приведет к правильному ответу без каких-либо предположений о вызываемой функции.

...