Date: Fri, 8 Jul 2022 11:53:42 +0100
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: }
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: }
Received on 2022-07-08 10:53:43