Есть ли способ вывести структуру C? - PullRequest
8 голосов
/ 12 февраля 2010

Я написал программу для проверки ограничений системных функций 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 ]

4 голосов
/ 12 февраля 2010

Не в С - по крайней мере, в целом. Но если модуль C скомпилирован с символами отладки и объектный модуль доступен, вы можете проанализировать это и узнать все о структуре. Бьюсь об заклад, есть библиотека для вашей системы, чтобы помочь с этим.

2 голосов
/ 10 января 2016

Столкнувшись с той же проблемой, я сам написал одну. 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 (поскольку оно еще не было записано) и просто извлечь свойство tm вручную с помощью json_object_to_json_string(json_object_object_get(result, "tm"))

1 голос
/ 23 февраля 2011

Это не совсем даст вам то, о чем вы просите, но это может немного помочь:

#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_zone и tm_gmtoff) при необходимости.

Опять же, это не общее решение, но, по крайней мере, оно немного приблизит вас, может быть.

0 голосов
/ 14 сентября 2011

Этот макрос не делает именно то, что вы хотите (генерировать JSON-дамп данных C), но я думаю, что он показывает некоторую возможность. Вы можете вывести содержимое любых данных C с помощью "p (...);" позвоните.

Я использовал 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

0 голосов
/ 15 апреля 2011

Том Кристиансен однажды написал pstruct / h2ph, который находится в perl CORE для анализа информации .stabs из используемого компилятора и создания читаемой информации для всех структур данных.

Структура C в JSON тривиальна на основе h2ph. http://perl5.git.perl.org/perl.git/blob/HEAD:/utils/h2ph.PL

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...