Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Can a function pointer with a const argument be used as a function pointer with a nonconst argument?

Perhaps the title isn't clear in itself... I have a function f (provided by some library) that takes as an argument a function pointer of signature void g(int*), i.e.

void f(void (*g)(int*));

However, I would like to use it using a function g (that I defined) with signature void g(const int*). A priori, I can't see how this can violate any const-correctness, as all the signature of f says is that g will only ever be called with a (non-const) int* (non-const), and indeed I can call a void (const int*) function with a non-const int* argument.

But GCC complains and says,

expected 'void (*)(int *)', but argument is of type 'void (*)(const int *)'

I can't see how this complaint can be legitimate, so does anyone know whether my understanding of that is wrong, or if there is a way around that?

like image 748
antony Avatar asked Nov 23 '11 00:11

antony


2 Answers

You seem to have found something that the compiler writers and standards writers did not account for. From C99 draft n1256, §6.7.5.3 paragraph 15,

corresponding parameters shall have compatible types.

Note that const int * is not compatible with int *. However, int * may be converted to const int *. From §6.3.2.3, paragraph 2,

For any qualifier q, a pointer to a non-q-qualified type may be converted to a pointer to the q-qualified version of the type

More sophisticated rules for inferring when it is acceptable to substitute types derived from qualified or unqualified versions of the same type are simply not present in the standard. Therefore, your code is technically in violation of the standard.

My conclusion: It seems to me that this error should be treated as "pedantic" by the compiler: your code does not technically conform to the standard, but the meaning is unambiguous and the code is absolutely safe. Feel free to write a feature request to your compiler vendor. There are plenty of nonconformant practices which do not generate warnings without -pedantic.

As a final note, I compiled with Clang and the compiler informed me that the warning was pedantic. However, I had not requested pedantic warnings... so there appears to be no way to turn it off.

warning: incompatible pointer types passing 'void (int const *)', expected 'void (*)(int *)'
      [-pedantic]

Workaround: Use an explicit cast.

void g(const int *);

f((void (*)(int *)) g);
like image 115
Dietrich Epp Avatar answered Oct 22 '22 10:10

Dietrich Epp


You are right, there's no reason C should disallow that call (other than because the C standard says it should). U(*)(T*) should be a sub-type of U(*)(const T*) because int* is a sub-type of const int* through substitutability.

Why C does not allow this, I don't know.

As for work-arounds, you can provide a proxy function:

void foo(const int* x) { ... } // <-- the function you want to pass in

void bar(int* x) { foo(x); } // proxy

f(bar); // instead of f(foo)

The fact that using a safe, standard-compliant proxy like this works at all should be proof enough that the call should have been valid in the first place.

like image 40
Peter Alexander Avatar answered Oct 22 '22 09:10

Peter Alexander