views:

83

answers:

5

In the embedded system I'm working on, we are using a table of function pointers to support proprietary Dynamic Libraries.

We have a header file that uses named constants (#define) for the function pointer indices. These values are used in calculating the location in the table of the function's address.

Example:

*(export_table.c)*

// Assume each function in the table has an associated declaration
typedef void (*Function_Ptr)(void);

Function_Ptr    Export_Function_Table[] =
{
  0,
  Print,
  Read,
  Write,
  Process,
};

Here is the header file:
*export_table.h*

#define ID_PRINT_FUNCTION 1
#define ID_READ_FUNCTION  2
#define ID_WRITE_FUNCTION 3
#define ID_PROCESS_FUNCTION 4

I'm looking for a scheme to define the named constants in terms of their location in the array so that when the order of the functions changes, the constants will also change.
(Also, I would like the compiler or preprocessor to calculate the indices to avoid human mistakes like typeo's.)

+1  A: 

Instead of an array you can define a structure with named elements for each function pointer:

struct call_table {
    Function_Ptr reserved;    
    Function_Ptr print_fcn;    
    Function_Ptr read_fcn;    
    Function_Ptr write_fcn;    
    Function_Ptr process_fcn;    
};
Michael Burr
This would be a good idea, unfortunately, the tables are in legacy code. Changing the could would mean explaining everything to everybody involved.
Thomas Matthews
+1  A: 
Aidan Cully
This is a lot of work to apply to existing (legacy) code. I believe it is a better solution, but has a high maintenance rating (not only does the code need to be maintained, but the language and tool must be documented and maintained. I can't justify that in the current schedule).
Thomas Matthews
+1  A: 

See this answer for a way to coerce the preprocessor in doing it for you.

florin
A: 

X-macros could help. For example, create a new file export_table_x.h, containing:

X_MACRO(Print),
X_MACRO(Read),
X_MACRO(Write),
X_MACRO(Process)

Then in your export_table.h, use:

#define X_MACRO(x) ID_ ## x ## _FUNCTION
enum
{
    ID_INVALID = 0,
    #include "export_table_x.h"
};
#undef X_MACRO

And in export_table.c, write:

#include "export_table.h"

// ...

#define X_MACRO(x) x
Function_Ptr Export_Function_Table[] =
{
    0,
    #include "export_table_x.h"
};

One change to your original functionality is that ID_PRINT_FUNCTION is now ID_Print_FUNCTION, etc.

Having to add an extra file is annoying, but you could avoid this by using #ifdef's and putting everything in the original header file, although this is less clear.

Joseph Quinsey
+3  A: 

Using C99, you can use designated initializers:

enum {
    ID_PRINT_FUNCTION   = 1,
    ID_READ_FUNCTION    = 2,
    ID_WRITE_FUNCTION   = 3,
    ID_PROCESS_FUNCTION = 4,
};

(The trailing comma is allowed in C99; technically, it is not in C90.)

// Assume each function in the table has an associated declaration
typedef void (*Function_Ptr)(void);

Function_Ptr Export_Function_Table[] =
{
    [ID_READ_FUNCTION]    = Read,
    [ID_WRITE_FUNCTION]   = Write,
    [ID_PROCESS_FUNCTION] = Process,
    [ID_PRINT_FUNCTION]   = Print,
};

Note that I deliberately reordered that - and the compiler sorts it out. Also, although I rewrote the '#define' values into 'enum' values, it would work with either.

Note that MSVC on Windows does not, AFAIK, support this notation. This means that I cannot use it in code that has to be ported between Linux and Windows - much to my exasperation.

Jonathan Leffler
Unfortunately, our tools are not up to C99 standards. Upgrading the tools must go through management. :-(
Thomas Matthews