代码之家  ›  专栏  ›  技术社区  ›  Schwern

有方法转储C结构吗?

  •  8
  • Schwern  · 技术社区  · 15 年前

    我已经编写了一个程序来探测系统的c time.h函数的限制,并用json将其转储。然后其他依赖于这些函数的东西可以知道它们的极限。

    # system time.h limits, as JSON
    {
        "gmtime": { "max": 2147483647, "min": -2147483648 },
        "localtime": { "max": 2147483647, "min": -2147483648 },
        "mktime": {
            "max": { "tm_sec": 7, "tm_min": 14, "tm_hour": 19, "tm_mday": 18, "tm_mon": 0, "tm_year": 138, "tm_wday": 1, "tm_yday": 17, "tm_isdst": 0 },
            "min": { "tm_sec": 52, "tm_min": 45, "tm_hour": 12, "tm_mday": 13, "tm_mon": 11, "tm_year": 1, "tm_wday": 5, "tm_yday": 346, "tm_isdst": 0 }
        }
    }
    

    gmtime()和localtime()非常简单,它们只接受数字,而mktime()接受TM结构。我编写了一个自定义函数来将tm结构转换为json散列。

    /* Dump a tm struct as a json fragment */
    char * tm_as_json(const struct tm* date) {
        char *date_json = malloc(sizeof(char) * 512);
    #ifdef HAS_TM_TM_ZONE
        char zone_json[32];
    #endif
    #ifdef HAS_TM_TM_GMTOFF
        char gmtoff_json[32];
    #endif
    
        sprintf(date_json,
                "\"tm_sec\": %d, \"tm_min\": %d, \"tm_hour\": %d, \"tm_mday\": %d, \"tm_mon\": %d, \"tm_year\": %d, \"tm_wday\": %d, \"tm_yday\": %d, \"tm_isdst\": %d",
                date->tm_sec, date->tm_min, date->tm_hour, date->tm_mday,
                date->tm_mon, date->tm_year, date->tm_wday, date->tm_yday, date->tm_isdst
        );
    
    #ifdef HAS_TM_TM_ZONE
        sprintf(&zone_json, ", \"tm_zone\": %s", date->tm_zone);
        strcat(date_json, zone_json);
    #endif
    #ifdef HAS_TM_TM_GMTOFF
        sprintf(&gmtoff_json", \"tm_gmtoff\": %ld", date->tm_gmtoff);
        strcat(date_json, gmtoff_json);
    #endif
    
        return date_json;
    }
    

    对于任何给定的结构,有没有一种通用的方法可以做到这一点?

    注:C,不是C++。

    5 回复  |  直到 15 年前
        1
  •  4
  •   wallyk    15 年前

    至少在一般情况下不是。但是,如果C模块是用调试符号编译的,并且对象模块是可用的,那么您可以分析它并发现结构的所有内容。我敢打赌你的系统一定有一个库来帮助你。

        2
  •  2
  •   Jamie Pate    9 年前

    遇到同样的问题,我自己写了一篇。 https://github.com/jamie-pate/jstruct 是的。编写它是为了允许对现有的c结构进行注释,然后根据注释生成元数据信息。库读取元数据以将c结构导入/导出到json字符串并返回。python脚本负责解析带注释的头并生成新的头和元数据初始值设定项。jstruct库使用 https://github.com/json-c/json-c 在内部。 我也注意到了 https://github.com/marel-keytech …但那是在写了整件事之后。(而且那个项目页面上的信息很稀疏)

    目前还不支持从现有系统库中注释单个结构,但可以将 tm 带注释的自定义结构中的结构。(我想?)

    如果您有使库对您更有用的想法,可以随意添加功能请求,甚至拉取请求。我的一个想法是添加一种方法来嵌入 只读 在带注释的包装器中使用 @inline 注释:eg(当前不支持,但可以添加)

    //@json
    struct my_time {
        //@inline
        struct tm tm;
    }
    

    代码:

    struct my_time t;
    
    mktime(&t.tm);
    struct json_object *result = jstruct_export(t, my_time);
    

    同时你也可以不用 @内联 (因为还没写完)直接手工提取TM属性 json_object_to_json_string(json_object_object_get(result, "tm"))

        3
  •  1
  •   lindes    13 年前

    这并不能满足你的要求,但可能会有点帮助:

    #define NAME_AND_INT(buf, obj, param) \
            sprintf((buf), "\"%s\": %d, ", #param, (obj)->(param))
    

    然后可以迭代,例如(注意:未测试;请考虑此伪代码):

    char * tm_as_json(const struct tm* date) {
        /* ... */
        char buf[BUFSIZ]; /* or, use your date_json */
    
        pos = buf; /* I note you use the equivalent of &buf -- that works too */
                   /* (not sure which is "better", but I've always left the & off
                    * things like that -- buf is essentially a pointer, it's just
                    * been allocated in a different way.  At least that's how I
                    * think of it. */
        pos += NAME_AND_INT(pos, date, tm_sec);
        pos += NAME_AND_INT(pos, date, tm_min);
        /* ... more like this ... */
    
        /* strip trailing ", " (comma-space): */
        pos-=2;
        *pos = '\0';
    
        /* ... */
    }
    

    你同样可以定义 NAME_AND_STRING 我是说, NAME_AND_LONG 等(用于TM区和TM GMTOFF)。

    再说一遍,这不是一个通用的解决方案,但它至少让你更接近,也许。

        4
  •  0
  •   rurban    13 年前

    tom christiansen曾经编写过pstruct/h2ph,它位于perl内核中,用于解析所用编译器中的.stabs信息,并为所有数据结构创建可读信息。

    基于h2ph,将c结构转换成json非常简单。 http://perl5.git.perl.org/perl.git/blob/HEAD:/utils/h2ph.PL

        5
  •  0
  •   Taisuke Yamada    13 年前

    这个宏并没有完全按照您的要求(生成c数据的json转储),但我认为它显示了一些可能性。您可以使用“p(…);”调用转储任何c数据的内容。

    我使用gdb作为外部助手来实现这个功能,但是可以用libbfd实现一个。在这种情况下,您可以完全控制输出,就像生成JSON兼容的输出一样。

    #ifndef PP_H
    #define PP_H
    /*
    * Helper function (macro) for people who loves printf-debugging.
    * This dumps content of any C data/structure/expression without prior
    * knowledge of actual format. Works just like "p" or "pp" in Ruby.
    *
    * Usage:
    * p(anyexpr);
    *
    * NOTE:
    * - Program should be compiled with "-g" and preferrably, with "-O0".
    *
    * FIXME:
    * - Would be better if this doesn't depend on external debugger to run.
    * - Needs improvement on a way prevent variable from being optimized away.
    */
    
    #include <stdio.h>
    #include <stdlib.h>
    #include <unistd.h>
    #include <stdarg.h>
    
    // Counts number of actual arguments.
    #define COUNT_(_1, _2, _3, _4, _5, _6, _7, _8, N, ...) N
    #define COUNT(...) COUNT_(__VA_ARGS__, 8, 7, 6, 5, 4, 3, 2, 1)
    
    // Dispatches macro call by number of actual arguments.
    // Following is an example of actual macro expansion performed in case
    // of 3 arguments:
    //
    // p(a, b, c)
    // -> FUNC_N(p, COUNT(a, b, c), a, b, c)
    // -> FUNC_N(p, 3, a, b, c)
    // -> p_3(a, b, c)
    //
    // This means calling with simple "p(...)" is fine for any number of
    // arguments, simulating "true" variadic macro.
    #define CONCAT(name, count) name##count
    #define FUNC_N(name, count, ...) CONCAT(name, count)(__VA_ARGS__)
    
    // Forbids variable from being optimized out, so debugger can access it.
    //
    // FIXME:
    // - Current implementation does not work with certain type of symbols
    #define ENSURE(...) FUNC_N(ENSURE_, COUNT(__VA_ARGS__), __VA_ARGS__)
    #define ENSURE_1(a) asm(""::"m"(a))
    #define ENSURE_2(a, ...) do { ENSURE_1(a); ENSURE_1(__VA_ARGS__); } while (0)
    #define ENSURE_3(a, ...) do { ENSURE_1(a); ENSURE_2(__VA_ARGS__); } while (0)
    #define ENSURE_4(a, ...) do { ENSURE_1(a); ENSURE_3(__VA_ARGS__); } while (0)
    #define ENSURE_5(a, ...) do { ENSURE_1(a); ENSURE_4(__VA_ARGS__); } while (0)
    #define ENSURE_6(a, ...) do { ENSURE_1(a); ENSURE_5(__VA_ARGS__); } while (0)
    #define ENSURE_7(a, ...) do { ENSURE_1(a); ENSURE_6(__VA_ARGS__); } while (0)
    #define ENSURE_8(a, ...) do { ENSURE_1(a); ENSURE_7(__VA_ARGS__); } while (0)
    
    // Dumps content of given symbol (uses external GDB for now)
    //
    // NOTE:
    // - Should use libbfd instead of gdb? (but this adds complexity...)
    #define PP_D(...) do { \
    char *arg[] = { __VA_ARGS__, NULL }; \
    char **argp = arg; \
    char cmd[1024]; \
    FILE *tmp = tmpfile(); \
    fprintf(tmp, "attach %d\n", getpid()); \
    fprintf(tmp, "frame 2\n"); \
    while (*argp) \
    fprintf(tmp, "p %s\n", *argp++); \
    fprintf(tmp, "detach\n"); \
    fflush(tmp); \
    sprintf(cmd, "gdb -batch -x /proc/%d/fd/%d", \
    getpid(), fileno(tmp)); \
    system(cmd); \
    fclose(tmp); \
    } while (0)
    
    #define PP(...) do { \
    FUNC_N(PP_, COUNT(__VA_ARGS__), __VA_ARGS__); \
    ENSURE(__VA_ARGS__); \
    } while (0)
    #define PP_1(a) do { PP_D(#a); } while (0)
    #define PP_2(a,b) do { PP_D(#a,#b); } while (0)
    #define PP_3(a,b,c) do { PP_D(#a,#b,#c); } while (0)
    #define PP_4(a,b,c,d) do { PP_D(#a,#b,#c,#d); } while (0)
    #define PP_5(a,b,c,d,e) do { PP_D(#a,#b,#c,#d,#e); } while (0)
    #define PP_6(a,b,c,d,e,f) do { PP_D(#a,#b,#c,#d,#e,#f); } while (0)
    #define PP_7(a,b,c,d,e,f,g) do { PP_D(#a,#b,#c,#d,#e,#f,#g); } while (0)
    #define PP_8(a,b,c,d,e,f,g,h) do { PP_D(#a,#b,#c,#d,#e,#f,#g,#h); } while (0)
    
    // Comment this out if you think this is too aggressive.
    #define p PP
    
    #endif
    

    缩进在上面的粘贴中丢失,但您可以从以下位置获取源: https://github.com/tai/ruby-p-for-c