【问题标题】:Building a type description table for a struct at compile time在编译时为结构构建类型描述表
【发布时间】:2015-12-18 20:36:41
【问题描述】:

拥有一个在编译时接受定义行格式的结构的库。不同的用户有不同的结构变体。

一个例子:

typedef struct { INT16U a, INT32S b } logrow_t;

在运行时,我想遍历这个结构并打印每个元素及其类型。

想到的第一个解决方案是建表:

typedef struct { char var_name[16], char var_type[16], int bytelen } logrow_desc_t;

logrow_desc_t descriptions[] = { { "a", "INT16U", 2 }, { "b", "INT32S", 4 } };

是否有更好的解决方案允许库的任何用户指定不同的行结构?有没有办法利用预处理器/编译器在编译时构建描述表?

【问题讨论】:

标签: c gcc struct c-preprocessor


【解决方案1】:

有没有办法利用预处理器/编译器来构建 编译时的描述表?

如果您和您的用户不介意在宏中指定行格式(比如LOGROW)而不是直接在logrow_t 的定义中指定,则可以使用此answer from luser droog to "Foreach macro on macros arguments" 中描述的方法的变体用过。
我把通用的、可重用的部分放在文件ppnarg.h

/*
 * The PP_NARG macro evaluates to the number of arguments that have been
 * passed to it.
 * Laurent Deniau, "__VA_NARG__," 17 January 2006, <comp.std.c> (29 November 2007).
 */
#define PP_NARG(...)    PP_NARG_(__VA_ARGS__,PP_RSEQ_N())
#define PP_NARG_(...)   PP_ARG_N(__VA_ARGS__)
#define PP_ARG_N( \
        _1, _2, _3, _4, _5, _6, _7, _8, _9,_10,  \
        _11,_12,_13,_14,_15,_16,_17,_18,_19,_20, \
        _21,_22,_23,_24,_25,_26,_27,_28,_29,_30, \
        _31,_32,_33,_34,_35,_36,_37,_38,_39,_40, \
        _41,_42,_43,_44,_45,_46,_47,_48,_49,_50, \
        _51,_52,_53,_54,_55,_56,_57,_58,_59,_60, \
        _61,_62,_63,N,...) N
#define PP_RSEQ_N() \
        63,62,61,60,                   \
        59,58,57,56,55,54,53,52,51,50, \
        49,48,47,46,45,44,43,42,41,40, \
        39,38,37,36,35,34,33,32,31,30, \
        29,28,27,26,25,24,23,22,21,20, \
        19,18,17,16,15,14,13,12,11,10, \
        9,8,7,6,5,4,3,2,1,0

/* need extra level to force extra eval */
#define Paste(a,b) a ## b
#define XPASTE(a,b) Paste(a,b)

/* APPLYXn variadic X-Macro by M Joshua Ryan      */
/* Free for all uses. Don't be a jerk.            */
/* I got bored after typing 15 of these.          */
/* You could keep going upto 64 (PPNARG's limit). */
#define APPLYX1(a)           X(a)
#define APPLYX2(a,b)         X(a) X(b)
#define APPLYX3(a,b,c)       X(a) X(b) X(c)
#define APPLYX4(a,b,c,d)     X(a) X(b) X(c) X(d)
#define APPLYX5(a,b,c,d,e)   X(a) X(b) X(c) X(d) X(e)
#define APPLYX6(a,b,c,d,e,f) X(a) X(b) X(c) X(d) X(e) X(f)
#define APPLYX7(a,b,c,d,e,f,g) \
    X(a) X(b) X(c) X(d) X(e) X(f) X(g)
#define APPLYX8(a,b,c,d,e,f,g,h) \
    X(a) X(b) X(c) X(d) X(e) X(f) X(g) X(h)
#define APPLYX9(a,b,c,d,e,f,g,h,i) \
    X(a) X(b) X(c) X(d) X(e) X(f) X(g) X(h) X(i)
#define APPLYX10(a,b,c,d,e,f,g,h,i,j) \
    X(a) X(b) X(c) X(d) X(e) X(f) X(g) X(h) X(i) X(j)
#define APPLYX11(a,b,c,d,e,f,g,h,i,j,k) \
    X(a) X(b) X(c) X(d) X(e) X(f) X(g) X(h) X(i) X(j) X(k)
#define APPLYX12(a,b,c,d,e,f,g,h,i,j,k,l) \
    X(a) X(b) X(c) X(d) X(e) X(f) X(g) X(h) X(i) X(j) X(k) X(l)
#define APPLYX13(a,b,c,d,e,f,g,h,i,j,k,l,m) \
    X(a) X(b) X(c) X(d) X(e) X(f) X(g) X(h) X(i) X(j) X(k) X(l) X(m)
#define APPLYX14(a,b,c,d,e,f,g,h,i,j,k,l,m,n) \
    X(a) X(b) X(c) X(d) X(e) X(f) X(g) X(h) X(i) X(j) X(k) X(l) X(m) X(n)
#define APPLYX15(a,b,c,d,e,f,g,h,i,j,k,l,m,n,o) \
    X(a) X(b) X(c) X(d) X(e) X(f) X(g) X(h) X(i) X(j) X(k) X(l) X(m) X(n) X(o)
#define APPLYX_(M, ...) M(__VA_ARGS__)
#define APPLYXn(...) APPLYX_(XPASTE(APPLYX, PP_NARG(__VA_ARGS__)), __VA_ARGS__)

//#define ZIP2(a,b) /* to be defined by application */
#define ZIP4(a,b,c,d)                   ZIP2(a,b)                   ZIP2(c,d)
#define ZIP6(a,b,c,d,e,f)               ZIP4(a,b,c,d)               ZIP2(e,f)
#define ZIP8(a,b,c,d,e,f,g,h)           ZIP6(a,b,c,d,e,f)           ZIP2(g,h)
#define ZIP10(a,b,c,d,e,f,g,h,i,j)      ZIP8(a,b,c,d,e,f,g,h)       ZIP2(i,j)
#define ZIP12(a,b,c,d,e,f,g,h,i,j,k,l)  ZIP10(a,b,c,d,e,f,g,h,i,j)  ZIP2(k,l)
// define more of those ZIPxx() if more arguments are needed
#define ZIP(...) APPLYX_(XPASTE(ZIP, PP_NARG(__VA_ARGS__)), __VA_ARGS__)

在您的应用程序中,可以这样使用:

// only this line is to be defined by the users:
#define LOGROW  INT16U, a, INT32S, b

// the following defines `logrow_t` and `descriptions` according to the above:
#include <stddef.h>
#include "ppnarg.h"

#define ZIP2(a, b)  a b;
typedef struct { ZIP(LOGROW) } logrow_t;
#undef  ZIP2
typedef struct { char *name, *type; size_t size; } logrow_desc_t;
#define ZIP2(a, b)  { #b, #a, sizeof (a) },
logrow_desc_t descriptions[] = { ZIP(LOGROW) };
#undef  ZIP2

【讨论】:

    【解决方案2】:

    似乎可以使用宏。比如下面的代码:

    #include <stdio.h>
    #include <stddef.h>
    #include <stdint.h>
    
    // first we need a foreach macro for each 2 arguments apply a function
    #define FOR2EACH_2(f, _1,_2)       f(_1, _2)
    #define FOR2EACH_4(f, _1,_2, ...)  f(_1, _2) FOR2EACH_2(f, __VA_ARGS__)
    #define FOR2EACH_6(f, _1,_2, ...)  f(_1, _2) FOR2EACH_4(f, __VA_ARGS__)
    #define FOR2EACH_N(_8,_7,_6,_5,_4,_3,_2,_1,N,...) FOR2EACH_##N
    #define FOR2EACH(f, ...)  FOR2EACH_N(__VA_ARGS__,8,7,6,5,4,3,2,1)(f, __VA_ARGS__)
    
    // structure for holding description
    struct struct_desc_s {
        const char *name;
        const char *type;
        size_t size;
    };
    
    // then a callback for struct fields
    #define STRUCT_WITH_DESC_FIELD(type, name)  type name;
    // a callback for description values
    #define STRUCT_WITH_DESC_DESC(type, name)  { #name, #type, sizeof(type) },
    // and finally our definition
    #define STRUCT_WITH_DESC(NAME, ...) \
    struct NAME { \
        FOR2EACH(STRUCT_WITH_DESC_FIELD, __VA_ARGS__) \
    }; \
    const struct struct_desc_s NAME##_desc[] = { \
        FOR2EACH(STRUCT_WITH_DESC_DESC, __VA_ARGS__) \
    };
    
    STRUCT_WITH_DESC(logrow,
        uint16_t, a,
        uint32_t, b
    )
    
    int main() {
        for (size_t i = 0; i < sizeof(logrow_desc)/sizeof(logrow_desc[0]); ++i) {
            printf("%s %s %zu\n", logrow_desc[i].name, logrow_desc[i].type, logrow_desc[i].size);
        }
    }
    

    输出:

    a uint16_t 2
    b uint32_t 4
    

    【讨论】:

      【解决方案3】:

      考虑 XMacro。

      #define LOWGROW_XMACRO \
        X(a, INT16U) \
        X(b, INT32S)
      

      现在您可以将结构定义为:

      typedef struct {
      #define X(NAME,TYPE) TYPE NAME;
      LOWGROW_XMACRO
      #undef X
      } lowgrow_t;
      

      或创建描述:

      logrow_desc_t descriptions[] = {
      #define X(NAME,TYPE) { #NAME, #TYPE, sizeof(TYPE) },
      LOWGROW_XMACRO
      #undef X
      };
      

      可能的用途是无限的。

      可以将上述宏放到一个专用的头文件中(即“lowgrow.hpp”) 并在定义 XMacro 后包含它:

      #define LOWGROW_XMACRO \
      ...
      #include "lowgrow.hpp"
      

      或者,LOWGROW_XMACRO 可以将另一个宏作为参数。这个高阶宏用来制作更多功能的 API。

      // lowgrow.hpp
      #define LOWGROW_MAKE_STRUCT_MEMBER(NAME, TYPE) TYPE NAME;
      #define LOWGROW_MAKE_DESCRIPTION(NAME, TYPE)   { #NAME, #TYPE, sizeof (TYPE) },
      
      #define LOWGROW_DO_STUFF(X)      \
      typedef struct {                         \
      X(LOWGROW_MAKE_STRUCT_MEMBER)    \
      } lowgrow_t;                     \
                                       \
      logrow_desc_t descriptions[] = { \
      X(LOWGROW_MAKE_DESCRIPTION)      \
      };
      

      现在宏可以被实例化:

      #include "lowgrow.hpp"
      
      #define LOWGROW_XMACRO(X) \
        X(a, INT16U) \
        X(b, INT32S)
      
      LOWGROW_DO_STUFF(LOWGROW_XMACRO)
      

      扩展为(为清楚起见添加了一些换行符之后):

      typedef struct { INT16U a; INT32S b; } lowgrow_t;
      logrow_desc_t descriptions[] = {
      { "a", "INT16U", sizeof (INT16U) },
      { "b", "INT32S", sizeof (INT32S) },
      };
      

      【讨论】:

        猜你喜欢
        • 1970-01-01
        • 1970-01-01
        • 2022-07-11
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        • 1970-01-01
        相关资源
        最近更新 更多