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