4

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;
  ...
}
3
  • without a parameter list it will compile if this is pure C Commented Dec 11, 2013 at 18:19
  • If this were allowed then it would easily allow me to pass a A* to processB. After all any pointer type is convertible to void* hence it would trivially allow for me to violate type safety. Commented Dec 11, 2013 at 18:21
  • Technically speaking it compiles, it just throws a warning. My question was rather whether it is possible to get rid of the warning without casting the initializer? Commented Dec 11, 2013 at 18:25

3 Answers 3

4

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.

Sign up to request clarification or add additional context in comments.

7 Comments

I wonder, would ellipsis achieve the same effect? (Making it compile without warning)
Just tried it, got test1.c:4:29: error: ISO C requires a named argument before ‘...’
Well, that answers that. I suppose I should have guessed :) Thanks.
The behavior of this might not be defined by the C standard. processA is a function taking a pointer to an A. The type that processor_t points to is a function without a parameter list. It is legal to convert a pointer to the first type of function to a pointer to the second type. However, if the second type is used to call a function and an argument of type void * is used, then the void * argument is a different type from the A * parameter that is required. They are not compatible, as the C standard defines it, and the behavior is undefined.
@Andrey: You are correct, I used the wrong terminology. However, the point is correct: If you call processA, which is a function that takes a pointer to an A, using a pointer to a function with an unspecified parameter list and with an argument that is a pointer to void, the behavior is not defined.
|
2

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.

Comments

1

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 *.

11 Comments

In my case, there's a code that iterates across the array Ps.
@cvoque: Saying there is code that iterates through the array does not convey any information about how the elements of the array are used. The code could contain a switch statement or other dispatching.
@cvoque: How are you going to call these functions? Presumably you have some pointer you want to pass them. What type is that pointer? E.g., are you calling each function, regardless of type, by passing it a void * that has been converted from an original A * or a B *? Or are you passing them actual A * and B * values?
I mean, In my case, there's a generic code that iterates through the entries of array Ps, without knowing what type exactly is each function. It is always passing a char*.
@cvoque: In C 2011 (N1570) 6.2.7, compatible types are defined. Mostly, they need to be the same. void * or char * are not compatible with A *. (They cannot be because some C implementations keep additional information for char pointers than for pointers to larger objects; different types of pointers might have different representations in the machine.) Then we have to consider whether calling a function using one type of expression when the function is declared with another typeof expression is allowed.…
|

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.