I have a struct which looks somewhat like this:
struct Data
{
int a;
float b;
char *c;
int (*read)(struct Data *data, int arg1);
int (*write)(struct Data *data, int arg1, int arg2);
int (*update)(struct Data *data, int arg1, int arg2, int arg3);
int (*erase)(struct Data *data, int arg1);
/* ... */
}
The ... means that there is bunch of other function pointers smiliar to above (that is, they all return an int and take pointer to Data as first argument, but other arguments may differ).
Let's say there are 20 function pointers total. In a special function DataInit(), I assign functions to them, like this:
Data->read = readA;
Data->write = writeA;
/* readA() and writeA() are functions defined elsewhere in the code, with argument lists same as corresponding function pointers */
Now I have to do the same for another object of type Data, which differs in a way that it's "read-only"; it basically means that from those 20 function pointers 15 has to be assigned such that after invoking them they should return error code NOT_SUPPORTED. The rest stay the same (for example, readA() is assigned to function pointer (*read) like above).
I was wondering if there's a way to do it without implementing a function for each pointer (for example, updateB() that takes three arguments and its body is just return NOT_SUPPORTED). Unfortunately, I cannot just set them to NULL.
I was thinking about preprocessor macros but it's black magic to me, honestly.
No, you may not cast a function pointer to a function pointer of different type (or even worse, to a different pointer type). This causes undefined behavior in the C standard for a good reason.
There are currently architectures out there where this isn't just a theoretical problem that everyone gets away with, but it can actually crash your program in unexpected ways. Read this blog post if you want details.
I don't know whether my suggestion is legal or not, but I want to suggest this:
int data_not_supported_(struct Data *thiz, ...)
{
return NOT_SUPPORTED;
}
And there might be no problem if your compiler uses cdecl calling convention, where the number of argument doesn't affect on the caller.
Yes, you can use a single function
int unsupported() {
return NOT_SUPPORTED;
}
and cast to correct the function pointer type when initializing your struct:
Data->write = (int (*)(struct Data *, int, int))unsupported;
These casts are ugly, so it's more readable to have a typedef for each function:
typedef int
(*write_t)(struct Data *, int, int);
And then:
Data->write = (write_t)unsupported;
As mentioned, function pointer casts will most likely result in undefined behavior on most systems.
A feasible solution to the problem is this:
typedef int func_t (struct Data* this, void* arg);
struct Data
{
int a;
float b;
char* c;
func_t* read;
func_t* write;
...
};
// later on in the code:
int update_function (struct Data* this, void* arg)
{
struct my_type* m = (struct my_type*)arg;
// use m
}
Related
I declared a struct like this one :
typedef struct s_data {
char buff[2048];
int len;
void *func[10];
struct data *next;
} t_data;
In my code, when passing a *data, I assigned some functions (just giving one so it is more understandable)
void init_data(t_data *data)
{
data->len = 0;
data->func[0] = &myfirstfunctions;
//doing the same for 9 others
}
My first function would be something taking as argument *data, and an int.
Then, I try to use this function in another function, doing
data->func[0](data, var);
I tried this and a couple of other syntaxes involving trying to adress (*func[0]) but none of them work. I kind of understood from other much more complex questions over there that I shouldn't store my function like this, or should cast it in another typedef, but I did not really understand everything as I am kind of new in programming.
void* can only be used reliably as a generic object pointer ("pointer to variables"). Not as a generic function pointer.
You can however convert between different function pointer types safely, as long as you only call the actual function with the correct type. So it is possible to do just use any function pointer type as the generic one, like this:
void (*func[10])(void);
...
func[0] = ((void)(*)(void))&myfirstfunction;
...
((whatever)func[0]) (arguments); // call the function
As you might note, the function pointer syntax in C is horrible. So I'd recommend using typedefs:
typedef void genfunc_t (void);
typedef int somefunc_t (whatever*); // assuming this is the type of myfirstfunction
Then the code turns far easier to read and write:
genfunc_t* func [10];
...
func[0] = (genfunc_t*)&myfirstfunction;
...
((somefunc_t*)func[0]) (arguments);
If all of your functions will have the same signature, you can do this like:
#include <stdio.h>
typedef void (*func)(void *, int);
struct s_data {
char buff[2048];
int len;
func f[10];
struct s_data *next;
};
static void
my_first_function(void *d, int x)
{
(void)d;
printf("%d\n", x + 2);
}
static void
init_data(struct s_data *data)
{
data->len = 1;
data->f[0] = my_first_function;
}
int
main(void)
{
struct s_data d;
init_data(&d);
d.f[0](NULL, 5);
return 0;
}
If your functions have different signatures, you will want to either use a union, or perhaps you will need several different members of the struct to store the function pointers.
The problem is that you haven't actually declared an array of function pointers. What you actually did is an array of pointers to void.
The syntax of declaring a pointer to function is as following:
function_return_type (*pointer_name)(arg1_type,arg2_type,...);
Then you can create an array of pointers to functions:
function_return_type (*arr_name[])(arg1_type, arg2_type,...)
Therefore, the declaration of your structure should look like this:
typedef void (*pointer_to_function)(void *, int);
struct s_data {
char buff[2048];
int len;
pointer_to_function array_of_pointeters[10];
struct s_data *next;
};
Good luck:)
I want to manage various entities with the same "universal" functions. The entities are schematically declared as:
typedef struct {
prefix_t pfx;
int i1;
int i2;
int i3;
} entity_t;
i.e. a typedef'd struct prefix for housekeeping (containing chaining pointers, flags, etc.) and a payload. There is one such type declaration per entity type.
The housekeeping functions need only to get a pointer to the prefix, which is easy, and a function which will "explore" the payload to return a meaningful quantity for the housekeeping.
With the example of binary tree management:
void TreeInsert (prefix_t *a, prefix_t *b, int (*compare) (prefix_t *, prefix_t *));
And inside the procedure, I have a call like this:
if (0 > compare(a, b)) {
// a comes before b
} else {
// a is the same as b or comes after
};
That's fine. The library function compiles without error nor warning.
But it is obvious that the compare function can't only reference the prefix. It needs to probe the payload to be useful:
int comp_entity (entity_t *a, entity_t *b) {
return a->i1 - b->i1;
}
The compiler gives warning for comp_entity on the following line:
TreeInsert (&a->pfx, &b->pfx, comp_entity);
Since the library function is used for many different "entities", the compare function cannot be typecast at time of call. The arguments to the compare function cannot be typed for the prefix otherwise there is no means to access the payload.
Should I define a specific function type only for the purpose of passing the compare to the library function? Something like:
typedef int (func_cast *) (prefix_t *, prefix_t*);
and
TreeInsert (&a->pfx, &b->pfx, (func_cast)comp_entity);
I'd rather avoid this. Is this possible?
Nota:
I have found create universal function pointer to any type C language and How do I quiet the C compiler about a function pointer takes any number of arguments?, but they don't provide a solution.
Your compare function knows what the true type should be, so you would declare the arguments with type prefix_t * and cast the arguments inside of the function:
int comp_entity (prefix_t *a, prefix_t *b) {
entity_t *ea = (entity_t *)a;
entity_t *eb = (entity_t *)b;
return ea->i1 - eb->i1;
}
I came across this code.
typedef __mpz_struct MP_INT;
typedef __mpz_struct mpz_t[1];
Here the struct __mpz_struct is a struct that is typedefed to an array of single element. I understand that this is a trick to pass by reference in C. Then mpz_t has been used as a type to declare variables and pass them to function as parameters. Also, There was one more comment
/*
MP_INT*, MP_RAT* and MP_FLOAT* are used because they don't have side-effects
of single-element arrays mp*_t
*/
What kind of side effects are they talking about?
Passing an array to a function let's the array decay to a pointer to it's 1st element.
One can achieve the same effect by applying the Address-Of operator & to a simple variable of the same type as the array's elements.
Examples:
struct S
{
int i;
float f;
};
This
void set_S(struct S * ps)
{
ps->i = 40;
ps->f = 2.;
}
is equivalent to
void set_S(struct S ps[1])
{
ps->i = 40;
ps->f = 2.;
}
is equivalent to
void set_S(struct S * ps)
{
ps[0].i = 40;
ps[0].f = 2.;
}
is equivalent to
void set_S(struct S ps[1])
{
ps[0].i = 40;
ps[0].f = 2.;
}
One-Element-Array approach:
typedef struct S Array_of_S_with_size_1[1];
int main(void)
{
Array_of_S_with_size_1 array_of_S_with_size_1;
array_of_S_with_size_1[0].i = 0;
array_of_S_with_size_1[0].f = 0.;
set_S(array_of_S_with_size_1);
...
}
The above main() provides the same functionality as the following:
int main(void)
{
struct S s;
s.i = 0;
s.f = 0.;
set_S(&s);
...
}
I do not see any gain using the "One-Element-Array" approach. An expection might be if the &-key is broken on ones keyboard ... ;-)
I see two parts to your question. The first part, how the typedef works for passing arguments to functions, would better be illustrated with an example. Without it, I'll have to guess a bit.
In C function declarations, an array parameter is equivalent to a pointer. That's why you see (for example) equivalently for the main function,
int main(int argc, char **argv)
and
int main(int argc, char *argv[])
Similarly, if a function in your program would be declared
int func(__mpz_struct *arg)
it would be equivalent to
int func(__mpz_struct arg[])
and hence to
int func(mpz_t arg)
Also, on the calling side, if you have a variable of type mpz_t, hence the array, and you pass it to a function, the "pointer decay" takes effect: in an expression, if you use (the name of) an array it "decays" into a pointer to its first element.
This way you can call the function:
mpz_t value;
func(value);
Of course, to modify these mpz_t objects outside of the API functions, you still have to be aware of their true nature.
The side effects that you mention, I would also have to guess about them. Possibly it is meant that you have to be aware you're working with pointers inside the functions. It might be considered better to make that explicit by using the pointer syntax.
You can assign an MP_INT to another but you can not assign an mpz_t to another since assignment is not defined for arrays. If you do not want your clients to assign variables other than by your methods (which might do memory managements and stuff) this is the trick for you.
Look at this sample code
typedef char type24[3];
Same as your, but well known data type 'char' insted of your struct __mpz_struct type.
above type def means, I am using above typedef to represent char[3].
So in your sample code,
typedef __mpz_struct mpz_t[1];
mpz_t should be __mpz_struct type.
I'm receiving an incompatible pointer type error warning although my code functions properly. First of all, here's a simplified version of my code:
typedef struct {
int field1;
int field2;
} my_struct;
void some_function(void **data){
// do something with *data
}
int main(int argc, char *argv[]){
my_struct *ptr = malloc(sizeof(my_struct));
ptr->field1 = 5;
ptr->field2 = 17;
some_function(&ptr);
}
The my_struct type is one example, but I'm actually using multiple types, so some_func must accept void**, rather than my_struct**. Each time I call some_func in the manner above, I recieve the following warning, and yet my code functions properly.
warning: passing argument 1 of ‘my_func’ from incompatible pointer type
I'm unsure of why these types aren't compatible, and would like to remove the warning in some way. For answers, feel free to explain why this is happening and, if possible, how to remove the warning (cpp directive, maybe?). Thanks!
Use a void * parameter type.
void ** is not a generic pointer type. void * is.
void* means (effectively) "a pointer to an untyped region of memory".
void** means (effectively) "a pointer to a region of memory, which contains pointers to untyped regions of memory".
Notice how the block in the middle has a type -- an array of void*. You're passing in effectively an array of mystruct*, which is a different type. So your compiler warns you.
Similar issues occur in C++ when trying to convert Derived** to Base**; but it applies here in C as well. (void* operates like the "base class" of all pointer types) Consider a piece of code like this:
void does_things(void** outParameter)
{
*outParameter = malloc(42); // Looks fine; writing void* to void* :)
}
int main()
{
my_struct* x = /*...*/;
does_things(&myStruct); // Ut oh, tries to make x point to
// something that is *not* a my_struct
}
I have two functions, each taking a pointer to a different type:
void processA(A *);
void processB(B *);
Is there a function pointer type that would be able to hold a pointer to either function without casting?
I tried to use
typedef void(*processor_t)(void*);
processor_t Ps[] = {processA, processB};
but it didn't work (compiler complains about incompatible pointer initialization).
Edit: Another part of code would iterate through the entries of Ps, without knowing the types. This code would be passing a char* as a parameter. Like this:
Ps[i](data_pointers[j]);
Edit: Thanks everyone. In the end, I will probably use something like this:
void processA(void*);
void processB(void*);
typedef void(*processor_t)(void*);
processor_t Ps[] = {processA, processB};
...
void processA(void *arg)
{
A *data = arg;
...
}
If you typedef void (*processor_t)(); then this will compile in C. This is because an empty argument list leaves the number and types of arguments to a function unspecified, so this typedef just defines a type which is "pointer to function returning void, taking an unspecified number of arguments of unspecified type."
Edit: Incidentally, you don't need the ampersands in front of the function names in the initializer list. In C, a function name in that context decays to a pointer to the function.
It works if you cast them
processor_t Ps[] = {(processor_t)processA, (processor_t)processB};
By the way, if your code is ridden with this type of things and switch's all over the place to figure out which function you need to call, you might want to take a look at object oriented programming. I personally don't like it much (especially C++...), but it does make a good job removing this kind of code with virtual inheritance.
This can be done without casts by using a union:
typedef struct A A;
typedef struct B B;
void processA(A *);
void processB(B *);
typedef union { void (*A)(A *); void (*B)(B *); } U;
U Ps[] = { {.A = processA}, {.B = processB} };
int main(void)
{
Ps[0].A(0); // 0 used for example; normally you would supply a pointer to an A.
Ps[1].B(0); // 0 used for example; normally you would supply a pointer to a B.
return 0;
}
You must call the function using the correct member name; this method only allows you to store one pointer or the other in each array element, not to perform weird function aliasing.
Another alternative is to use proxy functions that do have the type needed when calling with a parameter that is a pointer to char and that call the actual function with its proper type:
typedef struct A A;
typedef struct B B;
void processA(A *);
void processB(B *);
typedef void (*processor_t)();
void processAproxy(char *A) { processA(A); }
void processBproxy(char *B) { processB(B); }
processor_t Ps[] = { processAproxy, processBproxy };
int main(void)
{
char *a = (char *) address of some A object;
char *b = (char *) address of some B object;
Ps[0](a);
Ps[1](b);
return 0;
}
I used char * above since you stated you are using it, but I would generally prefer void *.