ccastingffi

"pointer value used where a floating-point was expected", but variable is of type void *


I am confused by following error message. I don't know where the floating-point comes from here.

error: pointer value used where a floating-point was expected
  279 |       ret_value = (void *)((IntFuncWithThreeArg)closure->bound_func)((int)args_values[0], (double)args_values[1], (int)args_values[2]);

The above expression is a call to a ffi closure which returns an int with following type:

typedef int (*IntFuncWithThreeArg)(int, double, int);

The ret_value variable is defined as a void *. And args_values is obviously an array of void * which holds different types.

Is there something wrong with it or the problem most be somewhere else in the code?

EDIT:

I will try to minimize the important parts of the code (omitting the implementation details of unrelated things), but compiling a minimal example for a polymorphic object implementation would be nothing close of "minimal".

The closures are called and stored inside objects:

typedef void (*GeneralFunc)();

typedef struct {
  char *name;
  char *ret_t;
  char **args_t;
  uint8_t args_n;
  void *bound_func;
} ClosureBinding;

ClosureBinding *bind_closure(char *ret_t, char *name, GeneralFunc closure_p, char *args_t[], const uint8_t args_n, void *stream) {
  ffi_cif cif;
  ffi_type *args[args_n];
  ffi_closure *closure;
  void *bound_func;
  _Bool failed;

  failed = 1;
  closure = NULL;

  closure = ffi_closure_alloc(sizeof(ffi_closure), &bound_func);
  if (closure) {
    for (uint8_t i = 0; i < args_n; ++i)
      args[i] = strcmp(args_t[i], "int") == 0 ? &ffi_type_sint : strcmp(args_t[i], "double") == 0 ? &ffi_type_double : &ffi_type_schar;

    if (ffi_prep_cif(&cif, ABI, args_n, strcmp(ret_t, "int") == 0 ? &ffi_type_sint : strcmp(ret_t, "double") == 0 ? &ffi_type_double : &ffi_type_schar, args) == FFI_OK)
      if (ffi_prep_closure_loc(closure, &cif, closure_p, stream, bound_func) == FFI_OK)
        failed = 0;
  }

  return new_closure_binding(name, ret_t, args_t, args_n, bound_func);
}

[...]

typedef struct Object_t {
  ClosureBinding **closures;
  uint8_t closures_n;

  void *(*call)(struct Object_t *self, int index, ...);
} Object;

void *call(Object *self, int index, ...);

Object *new_object(const uint8_t closures_n) {
  Object *self     = (Object *)malloc(sizeof(Object));

  self->call       = call;
  self->closures   = malloc(sizeof(ClosureBinding) *closures_n);
  self->closures_n = closures_n;

  return self;
}

The most important probably are the implementation details of the call function.

typedef int (*IntFuncWithThreeArg)(int, double, int);

void *call(Object *self, int index, ...) {
  va_list args;
  va_start(args, index);

  ClosureBinding *closure = self->closures[index];

  printf("Call the '%s' closure\n", closure->name);

  // Get the closure argument values from variadic arguments
  void *args_values[closure->args_n];

  for (int i = 0; i < closure->args_n; ++i) {
    char *arg;

    printf("Before argument allocation\n");
    arg = closure->args_t[i];
    if (!strcmp(arg, "int")) {
      int *p = malloc(sizeof(int));
      *p = va_arg(args, int);
      args_values[i] = p;
    } else if (!strcmp(arg, "double") || !strcmp(arg, "float")) {
      double *p = malloc(sizeof(double));
      *p = va_arg(args, double);
      args_values[i] = p;
    }
  }

  void *ret_value;

  if (!strcmp(closure->ret_t, "int")) {
    if (closure->args_n == 3) {
      ret_value = (void *)((IntFuncWithThreeArg)closure->bound_func)(*(int *)args_values[0], *(double *)args_values[1], *(int *)args_values[2]);
    } else {
      ret_value = (void *)-1;
      fprintf(stderr, "Warning: Attempt to execute an INT type closure but argument's type is unregistered\n");
    }
  } else {
    ret_value = (void *)-1;
    fprintf(stderr, "Warning: Return type unregistered\n");
  }

  va_end(args);
  return ret_value;
}

Solution

  • And args_values is obviously an array of void * which holds different types.

    You are casting a void * to a double, just like the following:

    int main( void ) {
       void *vp;
       double d = (double)vp;
    }
    
    <source>:3:4: error: pointer value used where a floating-point was expected
        3 |    double d = (double)vp;
          |    ^~~~~~
    

    This is undefined behaviour, thus the warning.


    Is args_values[1] actually a pointer to a double? If so, you should be using

    *(double *)args_values[1]
    

    If not, you should be using a union type rather than void *.

    Similar changes should be adopted for the int values as well.