Hi,

This somewhat makes sense, but it should not implicitly convert in this direction. I would be fine with implicit cast in the opposite direction, and static_cast in the proposed one, and making the function call to be defined in both cases (it can turn into UB in the function body though).

It could be extended a bit further to make function pointers to be contravariant in pointer argument types, when the corresponding pointer arguments convert in a way that does not require adjustment in the object representation (so derived-base conversion would be out in general, but qualification conversion, function pointer conversion and the void pointer conversion could be allowed).

An other point of extension could be to make function pointer conversions to be part of the qualification conversion hierarchy. So `void (**)() noexcept` could be converted to `void (*const*)()`.

> there is only one pre-requisite here for this to work
properly:

> sizeof(void*) == sizeof(Alpha*) == sizeof(Beta*)

This is not the only requirement. The object representation also must not change when converting between these pointer types. But you are safe here in most ABIs as well.

Cheers,
Lénárd



On 8 July 2022 11:53:42 BST, Frederick Virchanza Gotham via Std-Proposals <std-proposals@lists.isocpp.org> wrote:
Let's say we have two functions as follows:

class Alpha;
class Beta ;

void Configure_Alpha(Alpha*);
void Configure_Beta (Beta *);

I propose that we should be able to use the following function pointer:

void (*Configure)(void*);

to store the address of either of the two functions (without the need
for a cast), and for the behaviour to be well-defined if we then use
'Configure' to make a function call. In terms of CPU architecture and
assembler, there is only one pre-requisite here for this to work
properly:

sizeof(void*) == sizeof(Alpha*) == sizeof(Beta*)

Specifically, if you look at the following 73-line program, I propose
that the casts on Lines #51 and #52 should be unnecessary.

Furthermore if we were to use "std::function" instead of raw function
pointers, I again propose implicit conversion and well-defined
behaviour.

Sample Program:

01: #include <cstddef> // size_t
02: #include <cstdlib> // EXIT_FAILURE
03: #include <cstring> // strlen
04: #include <memory> // unique_ptr
05: #include <stdexcept> // runtime_error
06: #include <iostream> // cout, endl
07:
08: struct Alpha { /* stuff */ char dummy[5u]; };
09: struct Beta { /* stuff */ char dummy[7u]; };
10:
11: void Configure_Alpha(Alpha const *pa)
12: {
13: /* activities */
14:
15: std::cout << static_cast<unsigned>(pa->dummy[4u]) << std::endl;
16: }
17:
18: void Configure_Beta (Beta const *pb)
19: {
20: /* activities */
21:
22: std::cout << static_cast<unsigned>(pb->dummy[6u]) << std::endl;
23: }
24:
25: unsigned Char_To_UInt(char const c)
26: {
27: switch ( c )
28: {
29: case 'a': case 'A': return 10u;
30: case 'b': case 'B': return 11u;
31: case 'c': case 'C': return 12u;
32: case 'd': case 'D': return 13u;
33: case 'e': case 'E': return 14u;
34: case 'f': case 'F': return 15u;
35: default:
36: if ( (c < '0') || (c > '9') ) throw
std::runtime_error("Invalid character in input hex string");
37: return c - '0';
38: }
39: }
40:
41: void (*Configure)(void*); // This function pointer can hopefully
either point to Configure_Alpha or Configure_Beta
42:
43: int main(int const argc, char **const argv)
44: {
45: // The next line makes sure we have only two command line arguments,
46: // and that the first one is just one character long.
47: if ( (3 != argc) || ('\0' != argv[1u][1u]) ) return EXIT_FAILURE;
48:
49: switch ( argv[1u][0u] )
50: {
51: case 'a': Configure = reinterpret_cast<void (*)(void const
*)>(&Configure_Alpha); break;
52: case 'b': Configure = reinterpret_cast<void (*)(void const
*)>(&Configure_Beta ); break;
53: default : return EXIT_FAILURE;
54: }
55:
56: // The second command line argument is a hex string
57:
58: std::size_t const len = std::strlen(argv[2u]);
59:
60: if ( 0u != (len % 2u) ) return EXIT_FAILURE; // A hex string
can be "aabb" or "aabbcc", but not "aabbc" (the length must be an even
number)
61:
62: std::unique_ptr<char unsigned[]> phexstr( new char unsigned[len / 2u] );
63:
64: char unsigned *p = phexstr.get();
65:
66: for ( size_t i = 0u; i < len; i += 2u )
67: {
68: *p = Char_To_UInt(argv[2u][i + 0u]) << 8u;
69: *p++ |= Char_To_UInt(argv[2u][i + 1u]) << 0u;
70: }
71:
72: Configure( phexstr.get() );
73: }
--
Std-Proposals mailing list
Std-Proposals@lists.isocpp.org
https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals