有没有办法转储C结构?

时间:2010-02-12 06:45:18

标签: c json struct

我编写了一个程序来探测系统的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 ++。

6 个答案:

答案 0 :(得分:4)

不在C-至少一般。但是如果使用调试符号编译C模块,并且对象模块可用,则可以解析该模块并发现有关结构的所有内容。我打赌你的系统有一个库可以帮助你。

答案 1 :(得分:2)

遇到同样的问题,我自己写了一个。 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注释的带注释的包装器中嵌入那种只读结构:例如(目前不支持但可以添加)

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

代码:

struct my_time t;

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

同时你可以在没有@inline的情况下做同样的事情(因为它还没有编写),只需用json_object_to_json_string(json_object_object_get(result, "tm"))手动提取tm属性

答案 2 :(得分:1)

这并不能完全满足您的要求,但它可能会有所帮助:

#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_STRINGNAME_AND_LONG等(对于tm_zone和tm_gmtoff)。

同样,它不是一般的解决方案,但它至少可以让你更接近,也许。

答案 3 :(得分:1)

免责声明:我是项目https://github.com/tamask1s/zax-parser

的所有者

在库的帮助下,如果您提供了有关结构成员的一些需要转换的信息,则可以将C结构转换为JSON。无需在项目中包含生成的代码,但需要使用c ++ 11编译器才能使用它。

该库还很不成熟,因为我仅实现了所需的功能,但您可以扩展它,也可以将其用作灵感。

示例:

#define some_json_properties JSON_PROPERTY(x), JSON_PROPERTY(s) 
 
struct some_class 
{ 
    int x = 9; 
    std::string s = "something";  
 
    ZAX_JSON_SERIALIZABLE(some_class, some_json_properties) 
}; 
 
std::string some_json = some_obj; 

--- some_json的值:---

{"x":9, "s":"something"} 

也可以嵌套对象,请检查以下示例:https://tamask1s.github.io/zax-parser/index.html#Parsing_of_structures_with_fields_of_serializable_structures

答案 4 :(得分:0)

Tom Christiansen曾经在perl CORE中编写pstruct / h2ph来解析使用过的编译器中的.stabs信息,并为所有数据结构创建可读信息。

基于h2ph,C结构到JSON是微不足道的。 http://perl5.git.perl.org/perl.git/blob/HEAD:/utils/h2ph.PL

答案 5 :(得分:0)

这个宏并没有完全符合你的要求(生成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

获取来源