2016-04-27

One of the most useless features in C

While writing code for a "file descriptor" which imposes an array of function pointers, i stumbled over a "problem" which i first thought was an error in sdcc. In order to report the error i simplified the source until it only consisted of these 4 lines:

typedef int (*MyFPtr)(struct Data*);
struct Data { int a; };
extern int bar(struct Data* f);
MyFPtr foo = bar;

➜ I make a typedef for a function pointer, because function pointers are so awkward in c (though they are a pretty compared to function pointers in c++ ... which resulted in the invention of the data type 'auto' ...)
Then at some point i actually define the struct.
Later i declare a function which matches the typedef.
Finally i try to assign this function to a function pointer variable.

compiling this source resulted in an error for the last line:

/foo-1.c:4: error 78: incompatible types
from type 'unsigned-int function ( struct Data generic* fixed) __reentrant fixed'
  to type 'unsigned-int function ( struct Data generic* fixed) __reentrant fixed'
/foo-1.c:5: error 78: incompatible types
from type 'unsigned-int function ( struct Data generic* fixed) __reentrant fixed'
  to type 'unsigned-int function ( struct Data generic* fixed) __reentrant fixed'

btw.: ignore the double error. Error messages in sdcc are always a little bit suboptimal.

This looked as if the compiler had a problem to see that too identical types are identical.

And indeed they aren't.

As i learned from my bug report, the first line implicitly declares a local data type. Local to – yes, i don't know exactly to what. But it's local. And so it's different to the later and globally defined struct.

One suggested solution was:

typedef unsigned int (*T)(struct Data*);
extern unsigned int foo(struct Data* f);
T bar = foo;

which compiled without error. But this now was actually an error in sdcc: This source is wrong too:
Line 1 and 2 both declare local data types which by that are different. Line 3 shouldn't work. That there was an error could be proved by actually trying to use the function pointer typedef:

typedef unsigned int (*T)(struct Data*); // local data type
extern unsigned int foo(struct Data* f); // local data type
T bar = foo;                             // works in sdcc but shouldn't
struct Data { unsigned int a; };
int main() { struct Data d = {0}; foo(&d); } // rejected

This lead me to the question:

For what is the implicit declaration of a local data type in a function's argument list good, anyway? I can't think of a real use case. It's near impossible to call such a function. You have to cast the function to a function which accepts the data type you actually have, because you cannot even cast your data to the local data type...

Second, it's just a pitfall: If you declared the data type before the typedef and before the function declaration or definition, then the global data type is used. If you didn't, then a local data type is used. Imagine a local variable a in a function body was local only if there was no global variable a defined before…

No comments:

Post a Comment