David Ranieri
David Ranieri

Reputation: 41017

Generic pointer to function and state machines

I'm not able to find in the standard anything related to generic function pointers, in the C FAQ (Question 1.22) they are using:

typedef int (*funcptr)();         /* generic function pointer */
typedef funcptr (*ptrfuncptr)();  /* ptr to fcn returning g.f.p. */

Playing a bit with state machines that's my approach:

#include <stdio.h>

#define STM(x) (stm)x

typedef void (*stm)(void);
typedef stm (*pstm)(void *);

stm start(void *),
    state1(void *),
    state2(void *),
    state3(void *),
    stop(void *);

static int exit_state(int state)
{
    char str[2];
    int c;

    printf("Exit state %d? ", state);
    if (fgets(str, sizeof str, stdin)) {
        while (((c = fgetc(stdin)) != '\n') && (c != EOF));
        return (str[0] == 'y') || (str[0] == 'Y');
    }
    return 0;
}

static void state_machine(pstm pstart, void *data)
{
    pstm state = pstart;

    while (state != NULL) {
        state = (pstm)(*state)(data);
    }
}

stm start(void *data)
{
    puts("Starting state machine");
    *(char **)data = "Comes from start";
    return STM(state1);
}

stm state1(void *data)
{
    puts(*(char **)data);
    puts("State 1");
    if (!exit_state(1)) {
        return STM(state1);
    }
    *(char **)data = "Comes from state 1";
    return STM(state2);
}

stm state2(void *data)
{
    puts(*(char **)data);
    puts("State 2");
    if (!exit_state(2)) {
        return STM(state2);
    }
    *(char **)data = "Comes from state 2";
    return STM(state3);
}

stm state3(void *data)
{
    puts(*(char **)data);
    puts("State 3");
    if (!exit_state(3)) {
        return STM(state1);
    }
    return STM(stop);
}

stm stop(void *data)
{
    (void)data;
    puts("Stopping state machine");
    return NULL;
}

int main(void)
{
    char *data;

    state_machine(start, &data);
    return 0;
}

My question is: it is valid to use

typedef void (*stm)(void);

as a generic pointer to function?

it seems from what I see that we can use any type of prototype before making the cast, i.e.

typedef long double (*stm)(unsigned long long);

is also valid

are my assumptions correct?

Upvotes: 3

Views: 148

Answers (1)

Vagish
Vagish

Reputation: 2547

Quoting from : http://c-faq.com/ptrs/generic.html

It is guaranteed, however, that all function pointers can be interconverted, as long as they are converted back to an appropriate type before calling. Therefore, you can pick any function type (usually int ()() or void ()(), that is, pointer to function of unspecified arguments returning int or void) as a generic function pointer. When you need a place to hold object and function pointers interchangeably, the portable solution is to use a union of a void * and a generic function pointer (of whichever type you choose).

So , Yes It us valid to use typedef void (*stm)(void);

OR typedef long double (*stm)(unsigned long long); as generic pointer to function.

References for the highlighted text from the link:

ISO Sec. 6.1.2.5, Sec. 6.2.2.3, Sec. 6.3.4 Rationale Sec. 3.2.2.3 H&S Sec. 5.3.3 p. 12

EDIT: ( Adding more details from another answer)

The reference in n1570 draft for C11 is 6.3 Conversions / 6.3.2.3 Pointers § 8:

A pointer to a function of one type may be converted to a pointer to a function of another type and back again; the result shall compare equal to the original pointer. If a converted pointer is used to call a function whose type is not compatible with the referenced type, the behavior is undefined.

Upvotes: 4

Related Questions