BitShift
BitShift

Reputation: 1047

Array of jump tables in C

I'm trying to optimize access to some jump tables I have made, they are as follows:

int (*const usart_ctrl_table[USART_READ_WRITE_CLEAR])() =
{zg_usartCtrlRead, zg_usartCtrlWrite, zg_usartCtrlClr};

int (*const usart_frame_table[USART_READ_WRITE_CLEAR])() =
{zg_usartFrameRead, zg_usartFrameWrite, zg_usartFrameClr};

int (*const usart_trig_ctrl_table[USART_READ_WRITE_CLEAR])() =
{zg_usartTrigctrlRead, zg_usartTrigctrlWrite, zg_usartTrigctrlClr};

As you can see, the functions are for accessing a usart peripheral on a hardware level and are arranged in the table in the order of read/write/clear.

What I am attempting to do is have another jump table of jump tables, this way I can either run through initializing all the usart's registers in startup or simply change a single register later if desired.

i.e.

<datatype> (*usart_peripheral_table[<number of jump tables>])() = 
{usart_ctrl_table, usart_frame_table, usart_trig_ctrl_table};

This way I can expose that table to my middleware layer, which will help maintain a standard across changing HALs, and also I can use a define to index this table i.e.

fn_ptr = usart_peripheral_table[CTRL_TABLE]
fn_ptr[WRITE](bitmask);
fn_ptr[READ](buffer);

As you may have already guessed, I am struggling to figure out how to construct this table. I figured it is one of two things:

  1. Another simple array of pointers, as even a jump table itself is just an array of pointers. Hence my initialization would be:

    const int* (*usart_peripheral_table[<number of jump tables])() = 
    {usart_ctrl_table, usart_frame_table, usart_trig_ctrl_table};
    

However this doesn't seem to be working. Then I thought:

  1. An array of pointers to pointers. So I tried all kinds of combos:

     const int**(*usart_perip...
    
    
     const int**(usart_perip...
    
    
    const int** (*usart_peripheral_table[<number of jump tables])() = 
    {&usart_ctrl_table, &usart_frame_table[0], usart_trig_ctrl_table};
    

Nothing seems to work. Do I need to store the address of the lower jump tables in yet another pointer before assigning that variable to a pointer-to-pointer array? i.e.

    int* fn_ptr = usart_ctrl_table;


    <dataytype>(*const usart_periph[<number>])() = {fn_ptr};

Thanks in advance, any help would be greatly appreciated.

MM25

EDIT:

const int** (*const peripheral_table[1])() =
{&usart_ctrl_table[0]};


const int** (*const peripheral_table[1])() =
{usart_ctrl_table};

The above both give the error "initialization from incomaptible pointer type", as do all other combinations I have tried

Upvotes: 0

Views: 666

Answers (2)

Davislor
Davislor

Reputation: 15144

You might find that defining a typedef for your function pointers makes your code easier to read and maintain (although I’ve seen people recommend against it too):

#include <stdio.h>
#include <stdlib.h>

#define UART_RWC 3U

typedef int (*uart_ctl_func)(void);

int uart_read(void)
{
  printf("Read.\n");
  fflush(stdout);
  return 0;
}

int uart_write(void)
{
  printf("Write.\n");
  fflush(stdout);
  return(0);
}

int uart_clear(void)
{
  printf("Clear.\n");
  fflush(stdout);
  return 0;
}

uart_ctl_func uart_ctl_jump_table[][UART_RWC] = {
  { uart_read, uart_write, uart_clear },
  { uart_read, uart_write, uart_clear }
};

int main(void)
{
  uart_ctl_jump_table[0][1](); // Write.
  uart_ctl_jump_table[1][0](); // Read.
  uart_ctl_jump_table[1][2](); // Clear.

  return EXIT_SUCCESS;
}

The next step might be to make the jump table a struct so you end up writing Uart_ctl_table.frame.read(), or to at least define an enum for the constants.

#include <stdio.h>
#include <stdlib.h>

#define UART_RWC 3U

typedef int (*uart_ctl_func)(void);

int uart_read(void)
{
  printf("Read.\n");
  fflush(stdout);
  return 0;
}

int uart_write(void)
{
  printf("Write.\n");
  fflush(stdout);
  return(0);
}

int uart_clear(void)
{
  printf("Clear.\n");
  fflush(stdout);
  return 0;
}

typedef struct {
  uart_ctl_func read;
  uart_ctl_func write;
  uart_ctl_func clear;
} uart_ctl_set_t;

typedef struct {
  uart_ctl_set_t ctrl;
  uart_ctl_set_t frame;
  uart_ctl_set_t trig;
} uart_ctl_table_t;

const uart_ctl_table_t uart_ctl_table = {
  .ctrl = { uart_read, uart_write, uart_clear },
  .frame = { uart_read, uart_write, uart_clear },
  .trig = { uart_read, uart_write, uart_clear }
};

int main(void)
{
  uart_ctl_table.ctrl.write(); // Write.
  uart_ctl_table.frame.read(); // Read.
  uart_ctl_table.trig.clear(); // Clear.

  return EXIT_SUCCESS;
}

Upvotes: 2

KamilCuk
KamilCuk

Reputation: 141165

Just add a * like you added [] when defining an array.

int zg_usartCtrlRead();
int zg_usartCtrlWrite();
int zg_usartCtrlClr();
int zg_usartFrameRead();
int zg_usartFrameWrite();
int zg_usartFrameClr();
int zg_usartTrigctrlRead();
int zg_usartTrigctrlWrite();
int zg_usartTrigctrlClr();

int (*const usart_ctrl_table[])() =
{zg_usartCtrlRead, zg_usartCtrlWrite, zg_usartCtrlClr};

int (*const usart_frame_table[])() =
{zg_usartFrameRead, zg_usartFrameWrite, zg_usartFrameClr};

int (*const usart_trig_ctrl_table[])() =
{zg_usartTrigctrlRead, zg_usartTrigctrlWrite, zg_usartTrigctrlClr};

int (* const * const usart_peripheral_table[])() = 
{usart_ctrl_table, usart_frame_table, usart_trig_ctrl_table};

Usage:

usart_peripheral_table[1][2](5, 1, 3, 5, 6);

Btw, an empty parameter list on function declaration () means unspecified number and type of arguments. Do (void) if you want no arguments passed to your function.

This:

const int* (*usart_peripheral_table[<number of jump tables])();

Is an array of functions pointers that take unspecified number of arguments and return a pointer to constant integer.

This:

const int** (*usart_peripheral_table[<number of jump tables])() 

Is an array of function pointers that take unspecified number of arguments and return a pointer to a pointer to a constant integer.

You can also go with a 2D array:

int (* const usart_peripheral_table_2d[][3])() = {
    {
        zg_usartCtrlRead, zg_usartCtrlWrite, zg_usartCtrlClr, 
    }, {
        zg_usartFrameRead, zg_usartFrameWrite, zg_usartFrameClr,
    }, {
        zg_usartTrigctrlRead, zg_usartTrigctrlWrite, zg_usartTrigctrlClr,
    },
};

But maybe you want to write accessor functions that will return a pointer to an array of functions. Nothing simpler!

#include <stddef.h>

int (*usart_ctrl_table_get(size_t idx))() {
    return usart_ctrl_table[idx];
}

int (*usart_frame_table_get(size_t idx))() {
    return usart_frame_table[idx];
}

int (*usart_trig_ctrl_table_get(size_t idx))() {
    return usart_trig_ctrl_table[idx];
}

int (* const (* const usart_peripheral_table_indirect[])(size_t))() = {
    usart_ctrl_table_get, 
    usart_frame_table_get,
    usart_trig_ctrl_table_get,
};

Usage sample:

int main() {
    usart_peripheral_table_indirect[2](1)();
}

Upvotes: 1

Related Questions