I wish to accomplish function overloading in C, but I am attempting to run my code on a Unix server that does not have C11 support therefore the _Generic keyword is not available.
(Upgrading the server so it has a newer version of GCC is not an option).
Are there any alternatives to using _Generic to simulate effective function overloading in C?
You can do a limited form of overloading, for some argument types, like so:
void func_int(int);
void func_long(long);
void func_longlong(long long);
#define FUNC(X) \
(sizeof(X) <= sizeof(int) ? func_int(X) \
: sizeof(X) == sizeof(long) ? func_long(X) \
: func_longlong(X))
This will allow you to use FUNC(i) and have it call different functions. It's limited, because you can only distinguish types by their size. That means if sizeof(int) == sizeof(long) then you will never call func_long, and if sizeof(long) == sizeof(long long) then you will never call func_longlong. Also, you can't overload for other types, such as double, if sizeof(double) is the same as one of the integer types you're testing for.
It can be used to overload for e.g. float, double or long double, where you might have different implementations of a function that calculate more or less precisely depending on the precision (i.e. number of bits) in the argument type.
The GCC manual explicitly shows a GNU99 (-std=gnu99) workaround since at least version 3.1.1.
There are limitations, of course: all variants must have the same return type, and all function variants must make syntactic sense. The latter is often the cause of various compile errors (invalid types for function variant parameters). That can be avoided by declaring the functions without parameter prototypes; however, one must then remember that default type promotions will then take place (float are promoted to double, and all integer types smaller than int are promoted to int or unsigned int). Consider this example program:
#define _GNU_SOURCE /* for asprintf() */
#include <stdlib.h>
#include <stdio.h>
typedef struct {
double x;
double y;
double z;
double d;
} plane;
static const char *foo_char_array();
static const char *foo_int();
static const char *foo_long();
static const char *foo_double();
static const char *foo_float();
static const char *foo_short();
static const char *foo_plane();
#define foo(x) \
( __builtin_choose_expr( __builtin_types_compatible_p(typeof(x), int), foo_int(x), \
__builtin_choose_expr( __builtin_types_compatible_p(typeof(x), long), foo_long(x), \
__builtin_choose_expr( __builtin_types_compatible_p(typeof(x), short), foo_short(x), \
__builtin_choose_expr( __builtin_types_compatible_p(typeof(x), float), foo_float(x), \
__builtin_choose_expr( __builtin_types_compatible_p(typeof(x), double), foo_double(x), \
__builtin_choose_expr( __builtin_types_compatible_p(typeof(x), plane), foo_plane(x), \
__builtin_choose_expr( __builtin_types_compatible_p(typeof(x), char []), foo_char_array(x), \
(void)0 ))))))) )
int main(void)
{
double d = 1.0;
float f = 2.0f;
short s = 3;
long n = 4L;
plane p = { 5.0, 6.0, 7.0, 8.0 };
printf("foo(9) = %s\n", foo(9));
printf("foo(10L) = %s\n", foo(10L));
printf("foo(11.0f) = %s\n", foo(11.0f));
printf("foo(12.0) = %s\n", foo(12.0));
printf("foo(\"bar\") = %s\n", foo("bar"));
printf("foo(d) = %s\n", foo(d));
printf("foo(f) = %s\n", foo(f));
printf("foo(s) = %s\n", foo(s));
printf("foo(n) = %s\n", foo(n));
printf("foo(p) = %s\n", foo(p));
return EXIT_SUCCESS;
}
static const char *foo_char_array(char x[]) { return "char []"; }
static const char *foo_int(int x) { static char buffer[40]; snprintf(buffer, sizeof buffer, "(int)%d", x); return (const char *)buffer; }
static const char *foo_long(long x) { static char buffer[40]; snprintf(buffer, sizeof buffer, "(long)%ld", x); return (const char *)buffer; }
static const char *foo_float(double x) { static char buffer[40]; snprintf(buffer, sizeof buffer, "%af", x); return (const char *)buffer; }
static const char *foo_double(double x) { static char buffer[40]; snprintf(buffer, sizeof buffer, "%a", x); return (const char *)buffer; }
static const char *foo_short(int x) { static char buffer[40]; snprintf(buffer, sizeof buffer, "(short)%d", x); return (const char *)buffer; }
static const char *foo_plane(plane p) { static char buffer[120]; snprintf(buffer, sizeof buffer, "(plane){ .x=%g, .y=%g, .z=%g, .d=%g }", p.x, p.y, p.z, p.d); return (const char *)buffer; }
You do not need to determine the type based on a single parameter; you can do e.g. __builtin_types_compatible_p(typeof(x), double) && __builtin_types_compatible_p(typeof(y), double) to verify both x and y are of type double.
When compiled and run, the above program will output
foo(9) = (int)9
foo(10L) = (long)10
foo(11.0f) = 0x1.6p+3f
foo(12.0) = 0x1.8p+3
foo("bar") = char []
foo(d) = 0x1p+0
foo(f) = 0x1p+1f
foo(s) = (short)3
foo(n) = (long)4
foo(p) = (plane){ .x=5, .y=6, .z=7, .d=8 }
tested on 32-bit x86 Linux (ILP32), as well as on x86-64 (LP64). And yes, the above program will leak memory, since it never free()s the dynamically allocated strings returned by the foo_..() function variants.
I found a method that appears to work, however I still get a couple warnings at compile time...
Working code:
#include <stdio.h>
#define print(x) \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), int ), print_int(x) , \
__builtin_choose_expr(__builtin_types_compatible_p(typeof(x), char[]), print_string(x), \
(void)0))
void print_int(int i) {
printf("int: %d\n", i);
}
void print_string(char* s) {
printf("char*: %s\n", s);
}
int main(int argc, char* argv[]) {
print(1);
print("this");
return 0;
}
output:
int: 1
char*: thing
Compiler warnings:
gcc overload.c -o main
overload.c: In function 'main':
overload.c:19: warning: passing argument 1 of 'print_string' makes pointer from integer without a cast
overload.c:20: warning: passing argument 1 of 'print_int' makes integer from pointer without a cast
It is somehow possible using function pointers, and nameless struct inside a union. Here comes an example in which we overload the add and mul functions. There are two unions LIBI, and LIBF containing nameless structures. LIBI contains the function pointers add and mulc which only use integer values. LIBF is the same as LIBI except that add and mul use float variables. In addition, we need to create addi, muli, addf, and mulf functions outside of these unions. Functions pointers in unions will be referred to these 4 functions. For example, add in LIBI is referred to addi because addi uses int values and add in LIBF is referred to addf as it uses only float variables. This example can also be used as a way of emulating namespace in C which is absent in the language. Unions act like the namespace in this example.
#include<stdio.h>
#include<stdlib.h>
union {
struct {
void (*add)(int *, int);
void (*mul)(int *, int);
};
}LIBI;
union {
struct {
void (*add)(float *, float);
void (*mul)(float *, float);
};
}LIBF;
void addi(int *a, int c){
*a += c;
}
void addf(float *a, float c){
*a += c;
}
void muli(int *a, int c){
*a *= c;
}
void mulf(float *a, float c){
*a *= c;
}
int main(void){
LIBI.add = addi;
LIBF.add = addf;
LIBI.mul = muli;
LIBF.mul = mulf;
int ia = 10;
int ib = 2;
float fa = 20.0f;
float fb = 2.0f;
LIBI.add(&ia,ib);
LIBF.add(&fa,fb);
printf("%d\n",ia);
printf("%f\n",fa);
LIBI.mul(&ia,ib);
LIBF.mul(&fa,fb);
printf("%d\n",ia);
printf("%f\n",fa);
return 0;
}
Related
I was reading FFMPEG source code on extracting audio and I found these macros. What do these macros do?
#define REINTERPRET_CAST(type, variable) C_CAST(type, variable)
#define STATIC_CAST(type, variable) C_CAST(type, variable)
#define C_CAST(type, variable) ((type)variable)
//used like this
int value = 0;
int sampleIndex = 0;
uint8_t* buffer = calloc(50, sizeof(uint8_t));
value = REINTERPRET_CAST(uint8_t*, buffer)[sampleIndex];
value = REINTERPRET_CAST(int16_t*, buffer)[sampleIndex];
value = REINTERPRET_CAST(int32_t*, buffer)[sampleIndex];
value = REINTERPRET_CAST(int64_t*, buffer)[sampleIndex];
int ret = STATIC_CAST(float, *REINTERPRET_CAST(double*, &value));
//used like this
Used like in this code they do nothing meaningful - convert pointer to int and assign to int vartable (abstracting from the wrong syntax)
If they are used like this:
uint64_t value = *REINTERPRET_CAST(int64_t*, buffer + sampleIndex);
BTW the macro is wrong as well
#define C_CAST(type, variable) ((type)(variable))
Then it is called pointer punning and it invokes Undefined Behaviour violating strict aliasing rules.
It should be done this way:
#define pune(var, X) _Generic((var), \
uint16_t *: pune16, \
uint32_t *: pune32, \
uint64_t*: pune64 \
)(var, X)
uint16_t pune16(uint16_t *val, const void *ptr)
{
memcpy(val, ptr, sizeof(*val));
return *val;
}
uint32_t pune32(uint32_t *val, const void *ptr)
{
memcpy(val, ptr, sizeof(*val));
return *val;
}
uint64_t pune64(uint64_t *val, const void *ptr)
{
memcpy(val, ptr, sizeof(*val));
return *val;
}
example usage:
void foo(void *v)
{
uint32_t x;
x = pune(&x, v);
printf("%"PRIu32"\n,", x);
}
I would like to send callbacks with different signatures for the same function. Somenthing like this:
#include <stdio.h>
#include <stdarg.h>
void a(int pa) {}
void b(int pb1, float pb2) {}
// exec implementation
int main() {
exec(a, 1);
exec(b, 1, 2.3);
}
I thought of using something like:
void exec(void (*func)(...), ...) {
int arg1;
float arg2;
va_list valist;
va_start(valist, size);
arg1 = va_arg(valist, int);
if (size == 1) {
(*func)(arg1);
va_end(valist);
return;
}
arg2 = va_arg(valist, float);
if (size == 2) {
(*func)(arg1, arg2);
va_end(valist);
return;
}
}
But obviously it doesn't work :(
The usual solution to making callback function interfaces flexible with respect to data provided to the function is to give the callback signature a void * parameter (possibly in addition to other parameters). Arbitrary data can be provided via such a parameter. Something like this:
void exec(void (*func)(void *), void *data) {
func(data);
}
struct s2 {
int i;
float f;
};
void func1(void *data) {
int i = *(int *)data;
// ...
}
void func2(void *data) {
struct s2 s = *(struct s2 *)data;
// ...
}
int main(void) {
int i = 42;
struct s2 s = { .i = 17, .f = 3.14 };
exec(func1, &i);
exec(func2, &s);
}
HOWEVER, It is possible to do something more like you describe, where the callback functions genuinely have different signatures, by specifying the callback type without a prototype. In that case, there are still at least these caveats:
If the callback functions themselves are defined with prototypes (as they should be) then the parameter types should not be any that are altered by the default argument promotions. So, pointers, ints, doubles, but not floats or short ints or chars (not an exhaustive list). If you wanted to support other parameter types then you would need to cast the function pointer before calling the function, as described later.
The callback functions cannot be variadic.
If the front-end is variadic, then it needs to be told at runtime, somehow, what the actual number and types of the arguments are.
Furthermore, there will need to be explicit calls to the callback functions, with correct arguments, so there can be only a fixed set of predetermined callback signatures supported.
For example, that might look something like this:
enum sig { INT, INT_DOUB };
void exec(void (*func)(/* no prototype */), enum sig cb_sig, ...);
void a(int pa) {}
void b(int pb1, double pb2) {}
int main(void) {
exec(a, INT, 1);
exec(b, INT_DOUB, 1, 2.3);
}
void exec(void (*func)(/* no prototype */), enum sig cb_sig, ...) {
va_list valist;
va_start(valist, cb_sig);
switch (cb_sig) {
case INT: {
int i = va_arg(valist, int);
func(i);
break;
}
case INT_DOUB: {
int i = va_arg(valist, int);
double d = va_arg(valist, double);
func(i, d);
break;
}
default:
assert(("Can't be reached", 0));
}
va_end(valist);
}
It is possible that that would elicit a few warnings, such as about a function declaration that does not provide a prototype, and about calling a (declared, but) unprototyped function. Since you know the signatures by the time you execute the calls, however, you could get rid of the latter kind of warning via appropriate casting. For example,
// ...
case INT: {
int i = va_arg(valist, int);
((void (*)(int))func)(i);
break;
}
// ...
You could change the callbacks to take a single va_list argument:
void a(va_list args)
{
int pa = va_arg(args,int);
}
void b(va_list args)
{
int pb1 = va_arg(args,int);
double pb2 = va_arg(args,double);
}
And have your other function pass the va_list along.
void exec(void (*func)(va_list), ...)
{
va_list valist;
va_start(valist, func);
func(valist);
va_end(valist);
}
You can use va_args to solve this.
#include <stdio.h>
#include <stdint.h>
#include <stdarg.h>
#define exec_func(func, ...) func(__VA_ARGS__)
long func(char *a, int b, long c, long d)
{
printf("a: %s, b: %d, c: %ld, d: %ld\n", a, b, c, d);
return c + d;
}
int main()
{
printf("c + d: %ld\n", exec_func(func, "test", 10, 1000, 1000));
}
int* push_back_int(intVector* target, int push)
{
target->length++;
target->val = (int *)realloc(target->val, target->length * sizeof(int));
target->val[target->length - 1] = push;
return &target->val[target->length - 1];
}
float* push_back_float(floatVector* target, float push)
{
target->length++;
target->val = (float *)realloc(target->val, target->length * sizeof(float));
target->val[target->length - 1] = push;
return &target->val[target->length - 1];
}
Is there any way that I can hold a variable to replace the cast to int* or float* so that i can reuse the same code for multiple variable types using void*
No. In C the type is only available at compile-time.
You can use void * to pass data back and forth but you need to retain the element size. This approach is referred to as non-type safe (compiler will not catch the wrong "type", say, switching iq and fq below, which will then blow up most impressively at run-time when you get it wrong). Note how calling code handles the cast.
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
struct queue {
size_t push_length;
void *val;
size_t length;
};
void *push_back(struct queue *target, void *push) {
size_t offset = target->length * target->push_length;
target->length++;
void *tmp = realloc(target->val, target->length * target->push_length);
if(!tmp) {
// error handling
return NULL;
}
target->val = tmp;
return memcpy((char *) target->val + offset, push, target->push_length);
}
int main() {
struct queue fq = { sizeof(float), NULL, 0 };
push_back(&fq, &(float) { 2.718 });
push_back(&fq, &(float) { 3.142 });
for(unsigned i = 0; i < fq.length; i++) {
printf("%u: %f\n", i, ((float *) fq.val)[i]);
}
struct queue iq = { sizeof(int), NULL, 0 };
push_back(&iq, &(int) { 1 });
push_back(&iq, &(int) { 2 });
for(unsigned i = 0; i < iq.length; i++) {
printf("%u: %d\n", i, ((int *) iq.val)[i]);
}
}
and the output:
0: 2.718000
1: 3.142000
0: 1
1: 2
Your platform may require specific alignment for each element of val (i.e. for type T push_length = sizeof(T) % alignof(T) ? (sizeof(T) / alignof(T) + 1) * alignof(T) : sizeof(T)).
Can I cast a variable to a type decided during execution (?)
Yes, in some cases that support variable length array.
(double (*)[r]) is a cast to a type determined at run time. Demonstrative code follows:
int r = rand();
double a[r];
double (*b)[r] = &a;
unsigned char *data = malloc(sizeof a);
b = data; // warning: assignment to 'double (*)[r]' from incompatible pointer type 'unsigned char *' [-Wincompatible-pointer-types]
b = (double (*)[r]) data; // OK
(void) b;
In OP's code, the cast nor the sizeof(type) are not needed.
Use target->val = realloc(target->val, target->length * sizeof(target->val[0])); for both.
Only difference remaining in push_back_...() is the function name and signature.
Is there any way that I can hold a variable to replace the cast to int* or float* so that i can reuse the same code for multiple variable types using void*.
Any object pointer can be held in a void *. Yet that void * does not certainly retain anything to denote the type from which it is assigned. Auxiliary data needed. In OP's case, the size of the type would be enough if the size was consistent per vector.
typedef struct {
size_t object_size;
size_t length;
void *val;
} gVector;
// Pass in the address of the object to push
void* push_back_g(gVector* target, const void *push) {
void *p = realloc(target->val, target->object_size * (target->length + 1u));
if (p) {
target->val = p;
return memcpy((unsigned char *)val + target->object_size * target->length++,
push, target->object_size);
}
// Handle error with TBD code
return NULL;
}
Alternative we could pass in the size per push call and store that too.
Yet in both cases, code loses type checking.
With _Generic, code could handle various pre-determined select types with common code. Yet it seems OP wants any type.
C does not have runtime type information (RTTI) so you would have to hack that out manually by introducing an enum or similar. However, I would not encourage type generic programming with enums + void pointers in modern C programming since it is type unsafe and clunky.
We could use macros to generate type-generic code based on a number of supported types, kind of like a poor man's version of C++ templates. But that creates code which is very hard to read and maintain, so it isn't advisable either. Just for the record, it might look like this:
// NOT RECOMMENDED PRACTICE
#define SUPPORTED_TYPES(X) \
X(int) \
X(float) \
#define vector_define(type) \
typedef struct \
{ \
type* val; \
size_t length; \
}type##Vector;
SUPPORTED_TYPES(vector_define)
#define push_back_define(type) \
type* push_back_##type (type##Vector* target, type push) \
{ \
target->length++; \
target->val = realloc(target->val, target->length * sizeof(type)); \
target->val[target->length - 1] = push; \
return &target->val[target->length - 1]; \
}
SUPPORTED_TYPES(push_back_define)
As you can see it starts to look like a different language and the code is pretty alien to read for anyone who isn't used at seeing "X-macros".
In this case the best solution is perhaps to write a version using void* but to wrap it in a type safe macros utilizing _Generic. Your vector could then be implemented as a single type-generic ADT. Not a separate one for int, float and so on. As for how to do that proper, it's too long an answer for SO - a place to start would be to read up on How to do private encapsulation in C?.
A simple example of how to use _Generic without creating a proper ADT:
#include <stdio.h>
void push_back_int (int* target, int push)
{ puts(__func__); }
void push_back_float (float* target, float push)
{ puts(__func__); }
#define push_back(arr,item) \
_Generic((arr), \
int*: push_back_int, \
float*: push_back_float) \
((arr), (item) )
int main (void)
{
int* i_arr;
int i_item = 5;
push_back(i_arr, i_item);
float* f_arr;
float f_item = 123.0f;
push_back(f_arr, f_item);
}
Previously I asked How to make a function which receive a function as param in C language. I get an answer Link to the question but this solution is based on the parameters of argument's function. I mean:
int functionToPassAsParameter (int arg1, int arg2){
// do something
}
int functionWhichReceiveFunction (int (*f)(), int arg1, int arg2){
// do something
f(arg1, arg2);
// do something
}
// How to call the function
functionWhichReceiveFunction (&functionToPassAsParameter, 1, 2);
I would like something like:
int functionToPassAsParameter (int arg1, int arg2){
// do something
}
int functionWhichReceiveFunction ( f() ){
// do something
f();
// do something
}
// How to call the function
functionWhichReceiveFunction ( functionToPassAsParameter(arg1, arg2) );
So, when I call the function, I pass the correctly params but when I define the function which receive the other function I do not specify which params I will send to it. Is that possible to make?
EDIT 1:
I wanna achieve pass any function to functionWhichReceiveFunction. Something like:
int function_a (int param1, int param2) { /* Do something */ };
void function_b (char *arg1) { /* Do something */ };
// Call the function with two differents functions regardless return type nor params
int x = functionWhichReceiveFunction ( function_a(param1, param2) );
functionWhichReceiveFunction ( function_b(arg1) );
Define the function to be passed to take a void * as a parameter. That way the function that calls the given function doesn't need to know anything specific about the parameters:
struct params1 {
int arg1;
int arg2;
};
struct params2 {
char *arg1;
char *arg2;
};
int functionToPassAsParameter (void *param){
struct params1 *args = params;
// do something
}
int otherFunctionToPassAsParameter (void *param){
struct params2 *args = params;
// do something
}
int functionWhichReceiveFunction (int (*f)(void *), void *args) {
// do something
f(args);
// do something
}
struct params1 p1 = { 1, 2 };
functionWhichReceiveFunction (&functionToPassAsParameter, &p1);
struct params2 p2 = { "abc", "def" };
functionWhichReceiveFunction (&otherFunctionToPassAsParameter, &p2);
To be able to pass a function with unknown parameters to a function, declare the function that will pass the function as follows:
int g(int (*f)());
The function that is actually passed can have any number of parametes, for example:
int f(int x, void *y);
The call is now as follows:
g(f);
The above means that g passes f, which can have zero or more parameters of any type. This is denoted by the empty parameter list.
A particular function that may need to be passed is for example f.
Now g is called with function f, or any other function.
Note that it is up to g to know which parameters must be passed in calling f. So you need a "protocol" that tells g which function/type is passed. For example, besides passing the function, pass an identifier (int) that says what type of function is passed, for example:
#define fS_I_I 1 // f needs String, int, Int
#define fD_I 2 // f needs Double, Int
#define fI_I 3 // f needs Int, Int
int g(int ID, int (*f)());
g(fI_I, f);
Learn more about closures and tagged unions. Notice that C don't have them. You might want to emulate that with callbacks
I wanna achieve pass any function to functionWhichReceiveFunction
You cannot do that simply and portably. Remember that the signature of a function in C is related to its calling conventions (so to the ABI used by your compiler and your code; for examples, look into Linux x86 ABIs; so floating point arguments could be passed in different registers as integral arguments, so your compiler needs to know the signature of all your function pointers). You need to also give to functionWhichReceiveFunction something which describes the signature.
What you might consider doing, assuming your platform have function pointers of the same size and in the same address space as data pointers (this is very often the case), is to pass to functionWhichReceiveFunction a void* pointer (actually, a function pointer casted to void*) and an enumeration describing it.
For example
enum funsig_en {
funsig_void_to_void,
funsig_int_to_void,
funsig_int_to_double,
funsig_int_double_to_void,
};
Then, you'll have corresponding function signatures (types)
typedef void fun_void_to_void(void);
typedef void fun_int_to_void(int);
typedef double fun_int_to_double(int);
typedef void fun_int_double_to_void(int, double);
Suppose you have these static functions
static void statf_void_to_void(void);
static void statf_int_to_void(int);
static double statf_int_to_double(int);
static void statf_int_double_to_void(int, double);
You might declare
void
functionWhichReceiveFunction (void*res, enum funsig_en sigkind, void*fun, ...);
and you could use it as
functionWhichRecieveFunction(NULL, funsig_void_to_void
(void*)statf_void_to_void);
or
functionWhichRecieveFunction(NULL, funsig_int_to_void,
(void*)statf_int_to_void, 123);
or
double r = 0;
functionWhichRecieveFunction(&r, funsig_int_to_double,
(void*)statf_int_to_double, 2345);
I leave you to code that variadic functionWhichRecieveFunction. You need stdarg(3) facilities. It would include code like
va_args arglist;
va_start (arglist, fun);
switch(sigkind) {
case funsig_int_to_void: {
int a = va_arg(arglis, int);
fun_int_to_void* fptr = (fun_int_to_void*)fun;
(*fptr)(a);
return;
} // end case funsig_int_to_void
much later you'll need some va_end(arglis); near the end of your functionWhichRecieveFunction body.
Another possibility is using varargs. In the following example every function being called does its own interpretation of parameters.
#include <stdio.h>
#include <stdarg.h>
// expects 4 arguments (int, int, int, char*)
void f1(va_list args) {
int a, b, c;
char *d;
a = va_arg(args, int);
b = va_arg(args, int);
c = va_arg(args, int);
d = va_arg(args, char *);
printf("%d, %d, %d: %s\n", a, b, c, d);
}
// expects 3 ars (int, int, char*);
void f2(va_list args) {
int a, b;
char *c;
a = va_arg(args, int);
b = va_arg(args, int);
c = va_arg(args, char *);
printf("%d, %d: %s\n", a, b, c);
}
void caller(void (*f)(va_list), ...) {
va_list args;
va_start(args, f);
f(args);
va_end(args);
}
int main() {
caller(&f1, 0, 1, 3, "hello");
caller(&f2, 1, 2, "bye");
return 0;
}
Another possibility is to have caller to interpret parameters based on some type info and call a correct function call. This might be useful if you have a limited number of argument patterns and just regular functions to call:
void f3(int a, int b, int c, char *d) {
printf("%d, %d, %d: %s\n", a, b, c, d);
}
void f4(int a, int b, char *c) {
printf("%d, %d: %s\n", a, b, c);
}
typedef enum {
type1, type2
} Types;
void caller1(Types t, void (*f)(), ...) {
va_list args;
va_start(args, f);
switch (t) {
case type1: {
int a, b, c;
char *d;
a = va_arg(args, int);
b = va_arg(args, int);
c = va_arg(args, int);
d = va_arg(args, char *);
f(a,b,c,d);
break;
}
case type2: {
int a, b;
char *c;
a = va_arg(args, int);
b = va_arg(args, int);
c = va_arg(args, char *);
f(a,b,c);
}
}
va_end(args);
}
int main() {
caller1(type1, &f3, 3,2,1, "hi");
caller1(type2, &f4, 3,2,"take care");
return 0;
#Paul Ogilvie, this is the code:
int f(int x, void *y) {
return x;
};
int g(int (*f)()) {
int x = f(1, NULL); // call f with parameters
printf("X is: %d", x);
return(x); // return result
};
int main()
{
//g( f(1, 2) ); // this passes the result of a call to f, not f
g( f ); // this passes f
return 0;
}
I'm trying to create a system call handler, and I'm not sure how to store it.
I'm using the following typedef to store a (void *) pointer, which should receive the address of the function and an integer arg_no representing the number of arguments. Then, I create an array of this type.
typedef struct
{
void *foo;
int arg_no;
}td_sys_call_handler;
td_sys_call_handler ish[SYSCALL_HANDLER_NUM];
I'm trying to initialize the array in the following manner.
ish[0].foo = void (*halt) (void); ish[0].arg_no = 0;
ish[1].foo = void (*exit) (int status) NO_RETURN; ish[1].arg_no = 1;
ish[2].foo = pid_t (*exec) (const char *file); ish[2].arg_no = 1;
ish[3].foo = int (*wait) (pid_t); ish[3].arg_no = 1;
ish[4].foo = bool (*create) (const char *file, unsigned initial_size);
ish[4].arg_no = 2;
ish[5].foo = bool (*remove) (const char *file); ish[5].arg_no = 1;
ish[6].foo = int (*open) (const char *file); ish[6].arg_no = 1;
ish[7].foo = int (*filesize) (int fd); ish[7].arg_no = 1;
ish[8].foo = int (*read) (int fd, void *buffer, unsigned length);
ish[8].arg_no = 3;
ish[9].foo = int (*write) (int fd, const void *buffer, unsigned length);
ish[9].arg_no = 3;
ish[10].foo = void (*seek) (int fd, unsigned position);
ish[10].arg_no = 2;
ish[11].foo = unsigned (*tell) (int fd); ish[11].arg_no = 1;
But all the assignments from the function pointer to the void pointer produce the following error:
../../userprog/syscall.c: In function ‘syscall_init’:
../../userprog/syscall.c:76:17: error: expected expression before ‘void’
../../userprog/syscall.c:77:17: error: expected expression before ‘void’
../../userprog/syscall.c:78:17: error: expected expression before ‘pid_t’
../../userprog/syscall.c:79:17: error: expected expression before ‘int’
../../userprog/syscall.c:80:17: error: expected expression before ‘_Bool’
../../userprog/syscall.c:82:17: error: expected expression before ‘_Bool’
../../userprog/syscall.c:83:17: error: expected expression before ‘int’
../../userprog/syscall.c:84:17: error: expected expression before ‘int’
../../userprog/syscall.c:85:17: error: expected expression before ‘int’
../../userprog/syscall.c:87:17: error: expected expression before ‘int’
../../userprog/syscall.c:89:17: error: expected expression before ‘void’
../../userprog/syscall.c:91:17: error: expected expression before ‘unsigned’
I was under the impression that void* is the only instance of polymorphism in the language and that it can point to anything.
However, it appears that I'm wrong.
So which is the type of the pointer which can store the address of any function type?
Also, can you give me a good reference about C polymorphism? I've looked in many books but as far as I've seen the polymorphism chapter is very thin.
Thank you.
Yes, you are wrong.
void * pointers can point at any kind of data, but in C code (functions) are not data.
It's not valid to even cast between void * and function pointers: even though on most contemporary computers it will work as expected, the language does not guarantee that.
I don't understand from your code how you intended the "overloading" to be used in practice, how do you expect to call through the foo pointer? Just having the expected number of arguments is not enough, arguments have types and thus are handled differently in the function call.
The notation you need casts the system call function pointer to void *:
ish[0].foo = (void *)halt;
The C standard does not guarantee that pointers to functions will fit into pointers to data such as void *; fortunately for you, POSIX steps in and does guarantee that pointers to functions are the same size as pointers to data.
Your syntax is wrong. You should declare your function pointer first. Then you can use the address of the function pointer to assign to the pointer.
void (*halt) (void) = halt_sys_call_function;
ish[0].foo = &halt; ish[0].arg_no = 0;
C doesn't support traditional inheritance relationships in a direct way, but it does guarantee that the address of a structure is also the address of the first member of the structure. This can be used to emulate polymorphism in C. I described a similar approach in an answer I wrote about dynamic dispatch in C.
Consider a struct formatted to hold each function specifically:
typedef struct
{
void (*halt) (void);
void (*exit) (int status);
pid_t (*exec) (const char *file);
int (*wait) (pid_t);
bool (*create) (const char *file, unsigned initial_size);
bool (*remove) (const char *file);
int (*open) (const char *file);
int (*filesize) (int fd);
int (*read) (int fd, void *buffer, unsigned length);
int (*write) (int fd, const void *buffer, unsigned length);
void (*seek) (int fd, unsigned position);
unsigned (*tell) (int fd);
} myFuncs;
OR
This is messy and VERY unmaintable, but if you did cast each pointer to a void*, using void *addressOfWait = (void*)&wait;, then you could re-cast to the correct function pointer type before calling:
int (*waitFunctionPointer)(pid_t) = addressOfWait;
Then you could call that pointer:
waitFunctionPointer((pid_t) 1111); //wait for process with pid of 1111
I'll ask for #problemPotato's forgiveness for filching his structure definition:
typedef struct
{
void (*halt) (void);
void (*exit) (int status);
pid_t (*exec) (const char *file);
int (*wait) (pid_t);
bool (*create) (const char *file, unsigned initial_size);
bool (*remove) (const char *file);
int (*open) (const char *file);
int (*filesize) (int fd);
int (*read) (int fd, void *buffer, unsigned length);
int (*write) (int fd, const void *buffer, unsigned length);
void (*seek) (int fd, unsigned position);
unsigned (*tell) (int fd);
} fs_ops;
Say you have matching functions, declared like:
int ext5_open(const char * file);
unsigned ext5_tell (int fd);
then you can define and initialize a variable like (the bare name of the function is a pointer to it):
fs_ops ext5_ops = {
.open = ext5_open,
.tell = ext5_tell,
};
Fields that aren't initialized get NULL (i.e., pointer to no function). You can change the value of a field, ask if it is set (if(ext5_ops.seek == NULL) ...), and call the function:
retval = ext5_ops.(*ext5_open)("/tmp/junk");
(the parenteses around (*ext5_open) are because * (pointer indirection) binds less strongly than function call).
A function pointer can be converted into a void *, but it's a little trickier to convert it back to correct function-pointer type in order to call it. It should be possible by using a union. You'll need a separate union-member of the correct type for type of function that you want to store. And, as user4815162342 notes in a comment, you'll need to manage all the various combinations, probably with an enum.
typedef struct
{
union {
void *vp;
void (*v__v)(void);
void (*v__i)(int);
pid_t (*pid__ccp)(const char *);
int (*i__pid)(pid_t);
bool (*b__ccp_u)(const char *, unsigned);
bool (*b__ccp)(const char *);
int (*i__ccp)(const char *);
int (*i__i)(int);
int (*i__i_vp_u)(int, void *, unsigned);
int (*i__i_cvp_u)(int, const void *, unsigned);
void (*v__i_u)(int, unsigned);
unsigned (*u__i)(int);
} fp;
int arg_no;
}td_sys_call_handler;
The idea here is to try to encode the types into the identifiers, as a kind of "apps-Hungarian". This way, the meaning of any of these identifiers is directly visible.
It may be easier to generate these pointers and the associated enum at the same time. I think the easiest way to manage this part is with my favorite trick, X-Macros. Warning: it just gets more and more weird.
#define function_types(_) \
_(v__v, void, void) \
_(v__i, void, int) \
_(pid_ccp, pid_t, const char *) \
_(i__pid, int, pid_t) \
_(b__ccp_u, const char *, unsigned) \
_(b__ccp, const char *) \
_(i__ccp, const char *) \
_(i__i, int) \
_(i__i_vp_u, int, void *, unsigned) \
_(i__i_cvp_u, int, const void *, unsigned) \
_(v__i_u, int, unsigned) \
_(u__i, unsigned, int) \
/* end function_types */
This "master"-macro is a comma separated table of tokens which is passed, row by row, to the _ underscore macro, which is passed-in.
Now the struct type can be constructed by writing additional macros to use the rows, these are passed-in as _ to the table macro to instantiate the template:
#define create_function_pointer(id, ret, ...) \
ret (*id)(__VA_ARGS__);
#define create_function_type_id(id, ret, ...) \
f__ ## id
typedef struct {
union {
void *vp;
function_types(create_function_pointer)
} fp;
int arg_no;
enum {
function_types(create_function_type_id)
} type;
} td_sys_call_handler;
Now an array of these structs can be populated:
td_sys_call_handler ish[SYSCALL_HANDLER_NUM];
int i=0;
ish[i++] = (td_sys_call_handler){ halt, 0, f__v__v };
ish[i++] = (td_sys_call_handler){ exit, 1, f__v__i };
ish[i++] = (td_sys_call_handler){ exec, 1, f__pid__ccp };
ish[i++] = (td_sys_call_handler){ wait, 1, f__i__pid };
ish[i++] = (td_sys_call_handler){ create, 2, f__b__ccp_u };
ish[i++] = (td_sys_call_handler){ remove, 1, f__b__ccp };
ish[i++] = (td_sys_call_handler){ open, 1, f__i__ccp };
ish[i++] = (td_sys_call_handler){ filesize, 1, f__i__i };
ish[i++] = (td_sys_call_handler){ read, 3, f__i__i_vp_u };
ish[i++] = (td_sys_call_handler){ write, 3, f__i__i_cvp_u };
ish[i++] = (td_sys_call_handler){ seek, 2, f__v__i_u };
ish[i++] = (td_sys_call_handler){ tell, 1, f__u__i };
Now, calling a function given one of these structs will require (as you surmised) a switch, with a separate case for each signature. It needs to crack the arguments using stdarg and the call with the appropriate union member function pointer.
void make_sys_call(td_sys_call_handler ish, ...){
va_list ap;
int i;
const char *ccp;
pid_t pid;
bool b;
void *vp;
unsigned u;
const void *cvp;
va_start(ap, ish);
switch(ish.type) {
case f__v__f: ish.fp.v__v();
break;
case f__v__i: i = va_arg(int);
ish.fp.v__i(i);
break;
case f__pid__ccp: ccp = va_arg(const char *);
ish.fp.pid__ccp(ccp);
break;
// etc.
}
va_end(ap);
}
It will not be possible to return different types directly. You will either need to allocate a union type variable to hold the return value and return that, or something even more insane. An external stack data type could hold unions of the various return types. Depending on profiling results, it may be appropriate to consider this instead of returning the unions.
HTH.