C++ Logo

std-proposals

Advanced search

Re: [std-proposals] Well-defined behaviour and implicit conversion for function pointers that take void*

From: Lénárd Szolnoki <cpp_at_[hidden]>
Date: Fri, 08 Jul 2022 13:47:08 +0100
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_at_[hidden]> 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_at_[hidden]
>https://lists.isocpp.org/mailman/listinfo.cgi/std-proposals

Received on 2022-07-08 12:47:15