Document Number: N2230
Submitter: Martin Sebor
Submission Date: March 26, 2018
Subject: Generic Function Pointer

Summary

Since C89, the language has provided void* as a generic object pointer that can be used to convert to and from pointers to any other object type (complete or otherwise) without change. void pointers are commonly relied on to store pointers to objects of arbitrary types and, based on some runtime parameters, be ultimately converted back to the original type and dereferenced to access the value of the pointed-to object. Only one other kind of pointer shares this property of void*: a pointer to a character type. Other object pointers need not be convertible among themselves, and such conversion need not have defined semantics. A useful property of a void pointer is that it cannot be dereferenced. This prevents the typeless void pointer from being used to try to obtain a value of an incompatible type by accident.

A void pointer is not convertible to a function pointer, and conversely, function pointers aren't convertible to void*. It is conceivable that an implementation may (and some, in fact, do) define function pointers to have a different size or representation than void*, and such conversions could result in the corruption of the opriginal value.

However, C does not specify an analogous generic function pointer. Unlike object types, a pointer to a function of one type can be converted to a pointer to a function of any other type and back with no change. This is a useful guarantee that makes function pointers safely interchangeable (except to call a function, of course). At the same time, the absence of a generic function pointer analogous to void* has led to a proliferation of workarounds in applications, libraries, and implementations alike. To allow runtime symbol binding, the POSIX standard, for example, requires that function pointers be convertible to void* and vice versa. Likewise, many application programs make the assumption that object and function pointers are the same size and use void* as both a generic data pointer and a generic funtion pointer. For better type safety, however, others make an effort to differentiate between generic object and function pointers. Of these, many choose the same function type for such a pointer. A common example of such a generic function pointer is void (*)(void). Others still use a pointer to a function that takes some initial sequence of argument types that the functions are known to be called with and convert the pointer to the appropriate function type based on some runtime flag. All of these ultimately either convert this generic function pointer to a pointer to the function of the type it points to, or to one through the function is safe to call.

All of these solutions share the same problems: either they rely on a conversion that may result in the corruption of the function pointer (using void*) or one that makes it easy to inadvertently convert a function pointer to a data pointer and dereference it, or they make it easy to inadvertently use the wrong function pointer type to call an incompatible function. In addition, having many different but incomaptible (and unportable) solutions to the same problem compromises interoperability as well as analyzability by compilers and static analysis tools.

To alleviate some of these problems, the upcoming release of GCC 8 provides under the -Wbad-function-cast warning feature that makes it possible to detect certain kinds of conversions between incompatible function types. However, due to need for a generic function pointer in many application and the absence of a portable notation for such a pointer, the feature treats void(*)(void) as such a pointer, and thus also suffers from one of the problems discussed above.

Approaches Considered

We would like to suggest a solution to alleviate the problems above: a generic function pointer type akin to void*. Like void*, a generic function pointer would allow implicit conversion from any function pointer. Conversion from such a pointer to other function pointers could either be explicit for additional type safety, or implicit if that is considered important, or it could be left unspecified to the quality of implementation. Also similar to void*, our generic function pointer could not be used to call a function unless converted to a different function pointer type.

To achieve the goal above we have considered two approaches.

  1. Specify a new core language function (or function pointer) type to denote a function type to which any function type can be converted but that that cannot be used to call a function.

    The advantage of this approach is that it doesn't require programs to include a library header to make use of the new type. The disadvantage is that it requires a good notation for such a type and likely increases the complexity of the core language specification.

  2. Specify a library type that has the properties above without extending the core specification. This approach would leave details of such a type except its name unspecified.

    The advantage of this approach is simplicity of specification. The downside is that using such a type requires programs to include a library header.

The following list shows the notations we have considered for (1): a generic pointer a function that takes arbitrary argumens the return type, where specified, would be ignored for the purposes for conversion to the type of the pointer. We have not explored the challenges of integrating any of these notations into the syntax of the language and it is possible that ambiguitiues would prevent adopting any or all of them. The following outlines the only notation we have considered for (2).
	#include <stdarg.h>

	typedef void (*funcptr_t)(unspecified-incomplete-type);

Proposed Resolution

For simplicity we choose approach (2) above and propose to add a new library type to serve as the generic pointer type described above. To that end, we propose to make changes as indicated below. These changes are possibly incomplete and are being floated only for illustration and to gauge interest in the feature and the general appoproach.

In §7.16 Variable arguments <stdarg.h> make the following changes:

-1- The header <stdarg.h> declares two a types, and defines four macros...

-3- The types declared areis

	  va_list

	  funcptr_t
which denotes an unspecified function pointer type to which any other function pointer can be implicitly converted but which cannot be used to call a function.

A possible implementation of funcptr_t might be

	struct _Incomplete;
	typedef void __attribute__ ((convertible))) (*funcptr_t)(struct _Incomplete);
where the (implementation-specific) struct _Incomplete prevents functions from being called through funcptr_t because its type cannot be completed and thus arguments of the type cannot be created, and the (also implementation-specific) attribute tells the compiler that any function type is implicitly convertible to the pointer type.