代码之家  ›  专栏  ›  技术社区  ›  Paul Schifferer

同步函数指针表到表内容的索引

  •  2
  • Paul Schifferer  · 技术社区  · 6 年前

    我们有一个使用命名常量的头文件( #define )函数指针索引。这些值用于计算函数地址表中的位置。

    例子:

    (导出表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,
    };
    


    导出表.h

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

    我正在寻找一个方案,根据命名常量在数组中的位置来定义它们,这样当函数的顺序改变时,常量也会改变。
    (另外,我希望编译器或预处理器计算索引,以避免像typeo那样的人为错误。)

    6 回复  |  直到 9 年前
        1
  •  1
  •   Huppie    7 年前

    看到了吗 this answer

        2
  •  3
  •   Jonathan Leffler    14 年前

    使用C99,可以使用指定的初始值设定项:

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

    (C99中允许尾随逗号;从技术上讲,它不在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,
    };
    

    请注意,我故意对其重新排序—编译器会对其进行排序。另外,虽然我将“#define”值重写为“enum”值,但它可以与其中任何一个一起使用。

    注意,Windows上的MSVC不支持这种表示法。这意味着我不能在必须在Linux和Windows之间移植的代码中使用它——这让我非常恼火。

        3
  •  1
  •   Michael Burr    14 年前

    您可以为每个函数指针定义具有命名元素的结构,而不是数组:

    struct call_table {
        Function_Ptr reserved;    
        Function_Ptr print_fcn;    
        Function_Ptr read_fcn;    
        Function_Ptr write_fcn;    
        Function_Ptr process_fcn;    
    };
    
        4
  •  1
  •   Aidan Cully    14 年前

    DSL

    我们在这里使用这种技术。DSL基本上是一个带注释的C文件,看起来像这样:

    @@generate .h
    #ifndef __HEADER_H
    #define __HEADER_H
    
    @export FN_LIST
    
    #endif
    @@generate .c
    #include "foo.h"
    
    @define FN_LIST
    int myArray[] = 
    {
        @FN_INDEX_FOO
        12,
        @FN_INDEX_BAR
        13,
        @FN_INDEX_BAZ
        14,
    }
    

    这将产生一个 foo.h

    #ifndef __HEADER_H
    #define __HEADER_H
    
    #define FN_INDEX_FOO 0
    #define FN_INDEX_BAR 1
    #define FN_INDEX_BAZ 2
    
    #endif
    

    和一个 foo.c

    #include "foo.h"
    
    int myArray[] = 
    {
        /* FN_INDEX_FOO = 0 */
        12,
        /* FN_INDEX_BAR = 1 */
        13,
        /* FN_INDEX_BAZ = 2 */
        14,
    }
    

        5
  •  0
  •   Zhivago    14 年前

    X宏可能会有所帮助。例如,创建一个新的文件export\u table\u x.h,其中包含:

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

    然后在导出表.h中,使用:

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

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

    对原始功能的一个更改是ID\u PRINT\u函数现在是ID\u PRINT\u函数,等等。

    必须添加一个额外的文件是很烦人的,但是您可以通过使用ifdef并将所有内容放在原始头文件中来避免这种情况,尽管这不太清楚。

        6
  •  0
  •   technosaurus    9 年前

    很少有C程序员利用 #undef ,特别是为当前目的重新定义宏。这允许您将所有数据设置在一个可在一个位置更新和修改的可读表中。

    #define FUNCTION_MAP { \
       MAP(ID_NOP_FUNCTION,     NULL), \
       MAP(ID_PRINT_FUNCTION,   Print), \
       MAP(ID_READ_FUNCTION,    Read), \
       MAP(ID_WRITE_FUNCTION,   Write), \
       MAP(ID_PROCESS_FUNCTION, Process), \
    }
    
    #define MAP(x,y) x
    enum function_enums FUNCTION_MAP;
    #undef MAP
    
    #define MAP(x,y) y
    Function_Ptr Export_Function_Table[] = FUNCTION_MAP;
    #undef MAP
    

    #define FUNCTION_MAP  \
       /*   ENUM              Function, return, Arguments ...                  */ \
       MAP(ID_PRINT_FUNCTION,   Print,  int,    char *fmt, va_list *ap          ) \
       MAP(ID_READ_FUNCTION,    Read,   int,    int fd,    char *buf, size_t len) \
       MAP(ID_WRITE_FUNCTION,   Write,  int,    int fd,    char *buf, size_t len) \
       MAP(ID_PROCESS_FUNCTION, Process,int,    int                             )
    
    //function enums
    #define MAP(x,y,...) x,
    enum function_enums { FUNCTION_MAP };
    #undef MAP
    
    //function pre-definitions with unspecified number of args for function table
    #define MAP(x,fn_name,ret,...) ret fn_name();
    FUNCTION_MAP
    #undef MAP
    
    //function tables with unspecified number of arguments
    #define MAP(x,y,...) y,
    typedef int (*Function_Ptr)();
    Function_Ptr Export_Function_Table[] = { FUNCTION_MAP };
    #undef MAP
    
    //function definitions with actual parameter types
    #define MAP(x,fn_name,ret,...) ret fn_name(__VA_ARGS__);
    FUNCTION_MAP
    #undef MAP
    
    //function strings ... just in case we want to print them
    #define MAP(x,y,...) #y,
    const char *function_strings[] = { FUNCTION_MAP };
    #undef MAP
    
    //function enum strings ... just in case we want to print them
    #define MAP(x,y,...) #x,
    const char *function_enum_strings[] = { FUNCTION_MAP };
    #undef MAP
    #undef FUNCTION_MAP
    

    现在,您只需将每个新函数添加到此标题顶部的一个位置,如果希望保留作为库的向后兼容性,最好添加到函数映射的末尾。。。否则你可以按字母顺序列出。