Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Passing `int (*)(char const*)` where `int (*)(char*)` is expected

I have a function pointer whose function is declared as expecting char * arguments.Into it, I'd like to save a pointer to a function declared as taking char const* arguments.

I guess I can either use a wrapper or a cast. Casts seem more straightforward, but can I legally call the result of such a function pointer cast?

Example code below:

static int write_a(char * X){
    return 0;
}

static int write_b(char const* X){
    return 0;
}
static int wrapped_write_b(char * X){
    return write_b(X);
}

typedef int (*write_fn)(char * );

write_fn a = write_a;
write_fn b = wrapped_write_b;
write_fn b1 = (write_fn)write_b; //is b1 legally callable?
like image 704
PSkocik Avatar asked Dec 01 '16 14:12

PSkocik


2 Answers

This is undefined behavior - You can use a pointer to call a function of another type only if the types are compatible (6.3.2.3/8):

A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined.

Two functions have compatible types if (simplified version) they have same return and arguments are compatible (6.7.6.3, Semantics/15):

For two function types to be compatible, both shall specify compatible return types.146) Moreover, the parameter type lists, if both are present, shall agree in the number of parameters and in use of the ellipsis terminator; corresponding parameters shall have compatible types.

A const char * is not compatible with a char * (6.7.6.1, Semantics/2):

For two pointer types to be compatible, both shall be identically qualified and both shall be pointers to compatible types.

Since const char * and char * are not identically qualified, they are not compatible, and calling write_b through b is undefined behavior.

like image 93
Holt Avatar answered Nov 09 '22 21:11

Holt


Strictly speaking, it is not allowed.

A pointer-to-something is not compatible with a pointer-to-qualified-something. Because a pointer-to-qualified-something is not a qualified type of pointer-to-something

The same applies for

pointer-to-function-accepting-something

and

pointer-to-function-accepting-qualified-something.

This can be found through C11 6.2.7 compatible type:

Two types have compatible type if their types are the same. Additional rules for determining whether two types are compatible are described in 6.7.2 for type specifiers, in 6.7.3 for type qualifiers...

Where 6.7.3 is the relevant part. It says

For two qualified types to be compatible, both shall have the identically qualified version of a compatible type;

The conversion chapter 6.3.2.3 does not contradict this:

A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined.

EDIT

As noted in the answer by Holt, the compatibility of two functions is explicitly described in 6.7.6.3/15.


I still think that a wrapper function is the best solution. The root of the problem is that write_a isn't const-correct. If you can't change that function, then write a wrapper around it.

like image 45
Lundin Avatar answered Nov 09 '22 20:11

Lundin