It looks to me like you are trying to find the right answer to the wrong
question, typical of an XY problem. In the example you provide in
your own answer, you call the function pointer right after having set
its value. In this case, there is no use to the function pointer, as you
could directly call either read_chars()
or write_chars()
.
Obviously, this is just because that example is excessively simplified.
In a more realistic use case, you would set the function pointer in one
part of the code, and call it in a completely different part. Assuming
the functions and the pointer are defined like this:
void (*fun_ptr)();
void read_chars(uint8_t address) {
printf("address of array is 0x%.2x\n", address);
}
void write_chars(uint8_t row, uint8_t col, const char *str) {
printf("[%d, %d]: %s\n", row, col, str);
}
One section of the program sets the pointer:
// Here we learn what action should be performed.
if (should_read()) {
fun_ptr = (void(*)()) read_chars;
} else { // should write
fun_ptr = (void(*)()) write_chars;
}
and a different section calls it:
// Here we have to perform the action.
if (fun_ptr == (void(*)()) read_chars) {
((void(*)(uint8_t)) fun_ptr)(1);
} else if (fun_ptr == (void(*)()) write_chars) {
((void(*)(uint8_t, uint8_t, const char *)) fun_ptr)(
1, 1, "messaage0");
}
Note that this second section must know what function it is calling
in order to pass the correct arguments. Hence the if...then
test. Note
also that, in order to have valid C++, you must explicitly cast the
function pointer before calling it. This gets very clumsy, and it is a
lot more convenient to store this information (what function should be
called) in an enum
rather than a function pointer:
enum {
CALL_NONE, CALL_READ_CHARS, CALL_WRITE_CHARS
} what_to_call = CALL_NONE;
which would be used like this:
// Here we learn what action should be performed.
if (should_read()) {
what_to_call = CALL_READ_CHARS;
} else {
what_to_call = CALL_WRITE_CHARS;
}
// ...
// Here we have to perform the action.
if (what_to_call == CALL_READ_CHARS)
read_chars(1);
else if (what_to_call == CALL_WRITE_CHARS)
write_chars(1, 1, "messaage1");
That being said, it is often the case that when it is time to perform
the action, the part of the code responsible of doing so does not know
what arguments should be passed, and this information is more readily
available at the time when when learn what action should be performed.
This is a quite common situation, and a standard C idiom in this case is
to give the function a void *
parameter that it will cast to the
appropriate type in order to retrieve the data it needs. For instance:
void (*fun_ptr)(void *);
void *fun_ptr_data;
void read_chars(void * data) {
uint8_t address = * (uint8_t *) data;
printf("address of array is 0x%.2x\n", address);
}
struct write_chars_data {
uint8_t row;
uint8_t col;
const char *str;
};
void write_chars(void *data) {
write_chars_data *d = (write_chars_data *) data;
printf("[%d, %d]: %s\n", d->row, d->col, d->str);
}
Note that, since write_chars()
needs to retrieve multiple parameters
from a single pointer, it uses a pointer to a struct
.
Now, the part of the code that learns what should be done has the
responsibility of providing the data:
// Here we learn what action should be performed.
if (should_read()) {
fun_ptr = read_chars;
uint8_t *p = new uint8_t;
*p = 1;
fun_ptr_data = (void *) p;
} else { // should write
fun_ptr = write_chars;
write_chars_data *p = new write_chars_data;
p->row = 1;
p->col = 1;
p->str = "messaage2";
fun_ptr_data = (void *) p;
}
The part of the code that has to perform the action now is very simple:
// Here we have to perform the action.
if (fun_ptr)
fun_ptr(fun_ptr_data);
Note that this code snippet doesn't free the allocated memory. You will
have to figure out who will be responsible for that.
This pattern is quite common in libraries, where the client (the user of
the library) wants an action to be performed, but the library is
responsible for performing that action at the right time. The client
would then provide the library a pointer to a function that accomplishes
the action (called a “callback”), together with a generic pointer to the
data needed by the callback. The use of a generic void *
pointer makes
sense, because the library cannot know what kind of data the callback
will need.
In your case, since you know what kind of data you may need, you could
use a pointer to an union
instead of a generic pointer:
union fun_data {
uint8_t address; // for read_chars
struct { // for write_chars
uint8_t row;
uint8_t col;
const char *str;
};
};
void (*fun_ptr)(fun_data *);
fun_data *fun_ptr_data;
A more idiomatic C++ style would be to pack together the callback
function and its data into a “functor”, i.e. an object that can be
called like a function, and can also store the data it needs. You would
then use inheritance in order to create specific functor types while
calling a generic one.
fun_ptr
is a pointer to a function without any parameters, whilefun1()
is a function with auint8_t
parameter. Though it is unclear to me, why you are doing this, since you want to implement your FSM with a switch case. What structure do you want to build with the function pointer? Please show it the the context of your FSM, then we can try give you a good answerfun_ptr()
, you do not supply any argument. Thus it wouldn't even make sense forfun_ptr
to reference a function that does expect arguments.auto
type, templates/macros, and "void pointers" can be your best friends in this case