Home arrow C programming arrow Branch Tables via Function Pointer Arrays in C

Language Translator

Hacking Zone

Hacking Tools
Attacking

Configure Windows

Windows Configuration

Novels

Mix Novels

Human Personality

Body Language
Branch Tables via Function Pointer Arrays in C PDF Print E-mail
Written by Hemanshu Patel   
Saturday, 24 November 2007
Article Index
Branch Tables via Function Pointer Arrays in C
Page 2
Page 3
Page 4
Page 5
Page 6

Communication protocols


Although the keypad example is easy to appreciate, my experience in embedded systems is that communication links occur far more often than keypads. Communication protocols are a challenge ripe for a branch table solution. This is best illustrated by an example.

Last year, I worked on the design for an interface box to a very large industrial power supply. This interface box had to accept commands and return parameter values over a RS-232 link. The communications used a set of simple ASCII mnemonics to specify the action to be taken. The mnemonics consisted of a channel number (0,1, or 2), followed by a two character parameter. The code to handle a read request coming in over the serial link is shown below. The function process_read() is called with a pointer to a string fragment that is expected to consist of the three characters (null terminated) containing the required command.

const CHAR *fna(void);    //Example function prototype
static void process_read(const CHAR *buf)
{
    CHAR *cmdptr;
    UCHAR offset;
    const CHAR *replyptr;
    
    static const CHAR read_str[] =
        "0SV 0SN 0MO 0WF 0MT 0MP 0SW 1SP 1VO 1CC 1CA 1CB
         1ST 1MF 1CL 1SZ 1SS 1AZ 1AS 1BZ 1BS 1VZ 1VS 1MZ
         1MS 2SP 2VO 2CC 2CA 2CB 2ST 2MF 2CL 2SZ 2SS
         2AZ 2AS 2BZ 2BS 2VZ 2VS 2MZ 2MS ";    

    static const CHAR * (* const readfns[sizeof(read_str)/4])(void) = {
        fna,fnb,fnc, … };
    
    cmdptr = strstr(read_str, buf);
    
    if (cmdptr != NULL) {    
        /* cmdptr points to the valid command, so compute offset, in order
           to get entry into function jump table */
        offset = (cmdptr - read_str) / 4;  

        /* Call function, & get pointer to reply*/
        replyptr = (*readfns[offset])();
        /* rest of the code goes here */
    }
}

The code above is quite straightforward. A constant string, read_str, is defined. The read_str contains the list of all legal mnemonic combinations. Note the use of added spaces to aid clarity. Next, we have the array of function pointers, one pointer for each valid command. We determine if we have a valid command sequence by making use of the standard library function strstr(). If a match is found, it returns a pointer to the matching substring, else it returns NULL. We check for a valid pointer, compute the offset into the string, and then use the offset to call the appropriate handler function in the jump table. Thus, in four lines of code, we have determined if the command is valid and called the appropriate function. Although the declaration of readfns[] is complex, the simplicity of the runtime code is tough to beat.

Timed task list


A third area where function pointers are useful is in timed task lists. In this case, the input to the system is the passage of time. Many projects cannot justify the use of an RTOS. Instead, all that is required is that a number of tasks run at predetermined intervals. This is very simply handled as shown below.

typedef struct    {
   UCHAR interval;      /* How often to call the task */
   void (*proc)(void);    /* pointer to function returning void */
   }TIMED_TASK;


static const TIMED_TASK timed_task[] =
{
   {INTERVAL_16_MSEC,  fnA},
   {INTERVAL_50_MSEC,  fnB},
   {INTERVAL_500_MSEC, fnC},
   …
   {0,NULL}
};

extern volatile UCHAR tick;

void main(void)
{
    const TIMED_TASK    *ptr;
    UCHAR time;
    /* Initialization code goes here. Then enter the main loop */

    while (1) {
        if (tick) {        /* Check timed task list */
            tick--;
            time = computeElapsedTime(tick);
            for (ptr = timed_task; ptr->interval !=0; ptr++)
            if (!(time % ptr->interval))
                (ptr->proc)(); /* Time to call the function */
        }
    }
}        

In this case, we define our own data type (TIMED_TASK) that consists simply of an interval and a pointer to a function. We then define an array of TIMED_TASK, and initialize it with the list of functions that are to be called and their calling interval. In main(), we have the start up code which must enable a periodic timer interrupt that increments the volatile variable tick at a fixed interval. We then enter the infinite loop.

The infinite loop checks for a non-zero tick value, decrements the tick variable and computes the elapsed time since the program started running. The code then simply steps through each of the tasks, to see whether it is time for that one to be executed and, if so, calls it via the function pointer.

If your application only consists of two or three tasks, then this approach is probably overkill. However, if your project has a large number of timed tasks, or it is likely that you will have to add tasks in the future, then this approach is rather palatable. Note that adding tasks and/or changing intervals simply requires editing of the timed_task[] array. No code, per se, has to be changed.


 
< Prev   Next >
Your Ad Here

Donate us!!

Enter Amount:

RSS socialnet

Add to MyYahoo!
Subscribe in NewsGator Online
Add to Newsburst
Add to Google
Add to My AOL
Add to Pluck
Subscribe in FeedLounge
Add to Windows Live
Add to NetVibes
Subscribe in Rojo
Subscribe in Bloglines
Add to MyMSN
Add to Plusmo for your cellphone
Add to PageFlakes
Add to Technorati
Add to BlinkBits