"pointer value used where a floating-point was expected", but va

ghz 8months ago ⋅ 169 views

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

I know the answer should not be complex but I am confused by the error message. I don't know where the floating-point comes from.

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.

Am I missing something? Or the problem most be somewhere 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, name);

  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;
}

Answers

The error message you're encountering, "pointer value used where a floating-point was expected," indicates that there's a mismatch between the expected data types in the function call and the actual data types being passed. In the context of your code, this occurs when you attempt to call a closure that expects a double argument, but the value passed to it is interpreted as a pointer instead of a floating-point number.

Based on the code you provided, the issue seems to be related to how you're handling the arguments passed to the closures. Specifically, in the call function, you're allocating memory for each argument dynamically, which results in a pointer to the argument being passed instead of the argument value itself. Consequently, when you attempt to call the closure, it receives pointers instead of actual floating-point values.

To resolve this issue, you need to ensure that you pass the actual argument values to the closures instead of pointers to those values. Here's how you can modify the call function to achieve this:

void *call(Object *self, int index, ...) {
    va_list args;
    va_start(args, index); // Use 'index' instead of 'name'

    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 value = va_arg(args, int); // Retrieve the argument value directly
            args_values[i] = (void *)value; // Cast the value to void pointer
        } else if (!strcmp(arg, "double") || !strcmp(arg, "float")) {
            double value = va_arg(args, double); // Retrieve the argument value directly
            args_values[i] = (void *)value; // Cast the value to void pointer
        }
    }

    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;
}

With these modifications, the call function retrieves the argument values directly from the variadic arguments and passes them to the closures, ensuring that the closures receive the correct data types. This should resolve the error you're encountering.