2010-02-12 6 views
7

나는 시스템의 C time.h 함수의 한계를 조사하고이를 JSON으로 덤프하는 프로그램을 작성했다. 그런 기능에 의존하는 다른 것들은 한계를 알 수 있습니다.C 구조체를 덤프하는 방법이 있습니까?

# 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()과의 현지()는 단지 숫자 걸릴 정도로 간단하지만에서는 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++가 아닙니다.

답변

4

적어도 일반적으로는 C가 아닙니다. 그러나 C 모듈이 디버그 기호로 컴파일되고 오브젝트 모듈이 사용 가능하면이를 구문 분석하여 구조에 대한 모든 것을 발견 할 수 있습니다. 시스템에 도움이되는 라이브러리가있을 것입니다.

+0

한 손으로 TM 속성을 추출합니다. –

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_STRING, NAME_AND_LONG (tm_zone 및 tm_gmtoff에 대한) 정의 할 수 있습니다.

다시 말하지만, 일반적인 해결책은 아니지만 적어도 조금 더 가까워 질 수 있습니다.

+0

감사합니다. 함께 일하는 것이 좀 더 즐겁습니다. – Schwern

+0

좋았어, 도움이 돼서 기쁩니다. 또한 매크로 확장시 "buf"주위에 괄호가 있어야한다는 것을 알았습니다. 내가 그들을 추가 할 내 대답을 편집했습니다. – lindes

0

Tom Christiansen은 중고 컴파일러에서 .stabs 정보를 구문 분석하고 모든 데이터 구조에 대해 읽을 수있는 정보를 생성하기 위해 perl CORE에있는 pstruct/h2ph를 작성했습니다.

JSON으로 structs는 h2ph를 기반으로 하찮은 것입니다. http://perl5.git.perl.org/perl.git/blob/HEAD:/utils/h2ph.PL

+0

고마워,하지만 Perl로 C 구조체를 파싱하는거야? – Schwern

+0

perl에서만 컴파일러의 일부를 다시 작성하는 경우에만 C 구조체를 perl로 구문 분석하는 것이 거의 불가능합니다. 컴파일러가 stabs, dwarf, xml과 같은 매우 복잡한 컴파일러의 구조체에 대한 기호 정보를 작성할 수 있기 때문에 필요하지 않습니다. xml에는 GCC :: TranslationUnit이 있으며, 또한 Convert :: Binary :: C가 있으며 ucpp를 사용합니다. – rurban

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

+0

답해 주셔서 감사합니다. 필자의 경우 비표준 라이브러리, 특히 외부 프로그램에 의존하지 않는다. 비록 내가 libbfd 버전이 어떻게 생겼는지에 관심이있다. – Schwern

2

, 내가 하나를 자신을 썼다. https://github.com/jamie-pate/jstruct. 기존의 C 구조에 주석을 달고 주석을 기반으로 메타 데이터 정보를 생성 할 수 있도록 작성되었습니다. 라이브러리는 C 구조체를 json 문자열로 가져 오거나 내보내려면 메타 데이터를 읽습니다. 파이썬 스크립트는 주석이 달린 헤더를 파싱하고 새로운 헤더와 메타 데이터 이니셜 라이저를 생성합니다. jstruct 라이브러리는 내부적으로 https://github.com/json-c/json-c을 사용합니다. 나는 또한 https://github.com/marel-keytech을 알아 챘다. 그러나 그것은 전부를 쓰고 난 후에 있었다. (프로젝트 페이지의 정보가 희박합니다)

기존 시스템 lib의 단일 구조체에 주석을 달기위한 지원은 없지만 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"))

관련 문제