Thread: Function pointer question

  1. #1
    Registered User
    Join Date
    Aug 2004
    Posts
    4

    Function pointer question

    Hi,

    I'm writing a little program targetting a microcontroller. It's somewhat a state machine, where the program is always looping throw a defined state until it founds that some condition changed and hence must go to a new state.
    These states are implemented as individual functions:

    Code:
    char stateA() {
      for(;;) {
        if(condition1) {
          //do something
          return 'B';  //go to state B
        }
        else if(condition2) {
          //do something else
          return 'D';  //go to state B
        }
      }
    }
    The main function initially sets the state and then enters an infinete loop switching between states:

    Code:
    main() {
      char state = 'A';
      for(;;) {
        switch(state) {
          case 'A':
            state = stateA();
            break;
          case 'B':
            state = stateB();
            break;
          case 'C':
            state = stateB();
            break;
          case 'D':
            state = stateD();
            break;
        }
      }
    }
    This is working find, but it's not very comfortable to add new states because I need to touch a lot of code (the program is big, I have enums, constants, #defines, etc)

    So I think a nicer approach would be to make the states functions return a pointer to another state function instead of a char. The problem is I don't know how to declare such a pointer (I don't know if it is even possible) since the type of the function pointed to is the same as the type being defined.

    Can anyone tell me how can I do this, it is at all possible ?

    Thanks a lot

  2. #2
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    I can't come up with a way this is possible. The reason is, with function pointers, they always have a type of function they point to. That is to say, a function that returns a void or what not. You'd have to have a function wich returned a pointer to a function returning a pointer to a function wich returned a function pointer that... yeah. Perhaps Salem, Hammer, Prelude, or someone else can see a way around it, but I'm not seeing it.

    You could always do:
    Code:
    #include <stdio.h>
    #include <stdlib.h>
    
    typedef size_t (*FP)( void );
    size_t fun( void );
    
    
    int main( void )
    {
            FP states[20] = { fun };
            FP s;
    
            /* like so... */
            s = states[0];
            s();
    
            /* or simply... */
            states[0]();
    
            return 0;
    }
    
    size_t fun( void )
    {
            printf("wheeee...\n");
            return (size_t)1;
    }
    Then all you have to do is something like:
    Code:
    while( callstate != STATE_GOBYEBYE )
    {
        callstate = states[callstate]();
    }
    Ta-dah! Or tweak the loop slightly if you have a "gobyebye" function to call... You get the idea.

    Quzah.
    Hope is the first step on the road to disappointment.

  3. #3
    vae victus! skorman00's Avatar
    Join Date
    Nov 2003
    Posts
    594
    The only possibility I could see would be to create a scripting language that can be used to define the behavior of functions. To have it work during runtime, you would need a struct that can somehow hold that behavior in a string form or something, and can use that string to execute commands you would define (like a VM of sort). Then you would have to hold those structs in a dynamic array. This is assuming that all of the states have some shared behaviors. You would still need to add functionality to these structs, and this would all wind up being a massive headache to do this in C.

  4. #4
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    A function cannot return a pointer to itself. A function pointer contains the return type, so in the act of returning it, you change the return type!
    It's a bit like a structure which contains a copy of itself.

    However, you can break the cycle by going via a void pointer, though strictly speaking casting function pointers to void* and back again is not permitted. void* can point at any data, but functions are not data.

    With this in mind, you could do this
    Code:
    #include <stdio.h>
    
    typedef void *(*fn)(int);
    
    void * s1 ( int state );
    void * s2 ( int state );
    void * s3 ( int state );
    
    void * s1 ( int state ) {
      void * next_state = s2;
      printf( "s1(%d)\n", state );
      return next_state;
    }
    
    void * s2 ( int state ) {
      void * next_state = s3;
      printf( "s2(%d)\n", state );
      return next_state;
    }
    
    void * s3 ( int state ) {
      void * next_state = NULL;
      printf( "s3(%d)\n", state );
      return next_state;
    }
    
    int main ( ) {
      fn  state = s1;
      while ( state != NULL ) {
        state = state(123);
      }
      return 0;
    }
    For a totally "by the book" answer, extend Quzah's answer to include an enumeration. It's a bit of extra work, but the enumeration keeps things nice and readable.
    Code:
    #include <stdio.h>
    
    typedef enum {
      FN_S1,
      FN_S2,
      FN_S3,
      FN_FINISH
    } fn_et;
    
    typedef fn_et (*fn)(int);
    
    fn_et s1 ( int state );
    fn_et s2 ( int state );
    fn_et s3 ( int state );
    
    fn_et s1 ( int state ) {
      fn_et next_state = FN_S2;
      printf( "s1(%d)\n", state );
      return next_state;
    }
    
    fn_et s2 ( int state ) {
      fn_et next_state = FN_S3;
      printf( "s2(%d)\n", state );
      return next_state;
    }
    
    fn_et s3 ( int state ) {
      fn_et next_state = FN_FINISH;
      printf( "s3(%d)\n", state );
      return next_state;
    }
    
    // table of functions, matching the enum
    fn funcs[] = {
      s1,
      s2,
      s3,
    };
    int main ( ) {
      fn_et  state = FN_S1;
      while ( state != FN_FINISH ) {
        state = funcs[state](123);
      }
      return 0;
    }
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  5. #5
    Registered User
    Join Date
    Aug 2004
    Posts
    4
    I think I might have found a solution. Tell me what you think.

    Code:
    struct statetag;
    
    typedef struct statetag(*state_func)();
    
    typedef struct statetag {
    	state_func execute;
    }state_t;
    
    state_t stateA();
    state_t stateB();
    state_t stateC();
    
    int main(int argc, char *argv[]) {
    	state_t curstate;
    	curstate.execute = stateA;
    	for(;;) {
    		curstate = curstate.execute();
    	}
       getchar();
       return 0;
    }
    
    state_t stateA() {
    	char c;
    	state_t s;
    	printf("State A\n");
    	for(;;) {
    		c = getch();
    		switch(c) {
    			case 'b': s.execute = stateB; break;
    			case 'c': s.execute = stateC; break;
    		}
    		return s;
    	}
    }
    
    state_t stateB() {
    	char c;
    	state_t s;
    	printf("State B\n");
    	for(;;) {
    		c = getch();
    		switch(c) {
    			case 'a': s.execute = stateA; break;
    			case 'c': s.execute = stateC; break;
    		}
    		return s;
    	}
    }
    
    state_t stateC() {
    	char c;
    	state_t s;
    	printf("State C\n");
    	for(;;) {
    		c = getch();
    		switch(c) {
    			case 'a': s.execute = stateA; break;
    			case 'b': s.execute = stateB; break;
    		}
    		return s;
    	}
    }

  6. #6
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    As long as you're not forced to -ANSI it will compile just fine. I still think an array of function pointers returning an enumeration is a cleaner way to do it. But it's an interesting work around.

    Quzah.
    Hope is the first step on the road to disappointment.

  7. #7
    Registered User
    Join Date
    Aug 2004
    Posts
    4
    Quote Originally Posted by quzah
    As long as you're not forced to -ANSI it will compile just fine. I still think an array of function pointers returning an enumeration is a cleaner way to do it. But it's an interesting work around.
    Quzah.
    Hi,
    What part isn't ANSI compliant ? I compiled it with both my microcontroller compiler and with borland bcc55 compiler, and didn't receive any warning.

    I'm not very fond of the array of pointers, since adding a new function means touching three different parts of the code. I think It's as little maintainable as the code I'm using right now (see the original post).

  8. #8
    ATH0 quzah's Avatar
    Join Date
    Oct 2001
    Posts
    14,826
    Quote Originally Posted by sbayeta
    Hi,
    What part isn't ANSI compliant ? I compiled it with both my microcontroller compiler and with borland bcc55 compiler, and didn't receive any warning.
    Nevermind me. I'm having an off day I guess. I was misusing a flag. "-ansi" works just fine, I was trying "-ANSI"

    Quote Originally Posted by sbayeta
    I'm not very fond of the array of pointers, since adding a new function means touching three different parts of the code. I think It's as little maintainable as the code I'm using right now (see the original post).
    Well with enumerations and an array, yes, three parts of code:

    1) Creat a new enum tag.
    Code:
    typedef enum { ...stuff..., NewFooState } foo_t;
    2) Update your array by adding the new function:
    Code:
    fn funcs[] =
    {
        ...stuff...
        newFooFunction
    };
    3) Write your actual function:
    Code:
    foo_t newFooFunction( ...stuff... )
    {
    ...stuff...
    }
    
    Still, is writing 2 lines of code plus your actual function really that hard? I suppose it all depends on your taste. But that seems quite clear and very simple, and very maintainable.

    [edit]
    In your case, you actually end up writing more lines, if that is your measuring stick on how easy it is to maintain:

    1) switch ...
    2) state = newfunction();
    3) break;
    4) The function itself.
    [/edit]

    Quzah.
    Last edited by quzah; 08-04-2004 at 10:43 PM.
    Hope is the first step on the road to disappointment.

  9. #9
    and the hat of int overfl Salem's Avatar
    Join Date
    Aug 2001
    Location
    The edge of the known universe
    Posts
    39,660
    > since adding a new function means touching three different parts of the code.
    Don't you mean your way is m+n and my way is 2+m+n
    m being the number of lines in the function you added
    n being the number of places in the existing code which need to be changed to get into the new state.

    Besides, I can do
    return FN_S1;
    but your code is full of temporaries and s.execute = bar;
    because 'C' can't return a structure expression.

    Do you know how efficiently your embedded compiler returns structures by value?

    But hey, it's your code, and it seems to work and you're happy with it
    If you dance barefoot on the broken glass of undefined behaviour, you've got to expect the occasional cut.
    If at first you don't succeed, try writing your phone number on the exam paper.

  10. #10
    Quote Originally Posted by sbayeta
    So I think a nicer approach would be to make the states functions return a pointer to another state function instead of a char. The problem is I don't know how to declare such a pointer (I don't know if it is even possible) since the type of the function pointed to is the same as the type being defined.

    Can anyone tell me how can I do this, it is at all possible ?
    Try this

    http://mapage.noos.fr/emdel/clib.htm
    Module FSM

    (The missing material is on the same link, just d/l it)

    Let me know if you are stuck.
    Emmanuel Delahaye

    "C is a sharp tool"

Popular pages Recent additions subscribe to a feed

Similar Threads

  1. In over my head
    By Shelnutt2 in forum C Programming
    Replies: 1
    Last Post: 07-08-2008, 06:54 PM
  2. Problem with Visual C++ Object-Oriented Programming Book.
    By GameGenie in forum C++ Programming
    Replies: 9
    Last Post: 08-29-2005, 11:21 PM
  3. c++ linking problem for x11
    By kron in forum Linux Programming
    Replies: 1
    Last Post: 11-19-2004, 10:18 AM
  4. Contest Results - May 27, 2002
    By ygfperson in forum A Brief History of Cprogramming.com
    Replies: 18
    Last Post: 06-18-2002, 01:27 PM
  5. qt help
    By Unregistered in forum Linux Programming
    Replies: 1
    Last Post: 04-20-2002, 09:51 AM