2010-02-12 11 views
7

He escrito un programa para sondear los límites de las funciones C time.h de un sistema y las descargo en JSON. Luego, otras cosas que dependen de esas funciones pueden conocer sus límites.¿Hay alguna manera de volcar una C struct?

# 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() y localtime() son bastante simple, que acaba de tomar los números, pero mktime() toma una estructura tm. Escribí una función personalizada para convertir una estructura tm en un hash 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; 
} 

¿Hay alguna manera de hacer esto genéricamente, para cualquier estructura dada?

Nota: C, no C++.

Respuesta

4

No en C, al menos en general. Pero si el módulo C está compilado con símbolos de depuración y el módulo de objeto está disponible, podría analizarlo y descubrir todo sobre la estructura. Apuesto a que hay una biblioteca para que tu sistema te ayude con eso.

+0

1 para mí superando a la misma. –

1

Esto no será bastante darle lo que usted está pidiendo, pero podría ayudar un poco:

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

A continuación, podría repetir, por ejemplo, algo así como (nota: no probado; considera este pseudo-código):

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'; 

    /* ... */ 
} 

Se podría definir de manera similar NAME_AND_STRING, NAME_AND_LONG, etc. (por tm_zone y tm_gmtoff) según sea necesario.

Una vez más, no es una solución genérica, pero al menos lo acerca un poco, tal vez.

+0

Gracias, eso es un poco más agradable para trabajar. – Schwern

+0

Bueno, me alegro de que sea útil. Además, me di cuenta de que debería haber tenido paréntesis alrededor de "buf" en la expansión de la macro. He editado mi respuesta para agregarlos. – lindes

0

Tom Christiansen escribió una vez pstruct/h2ph que está en perl CORE para analizar la información .stabs del compilador utilizado y crear información legible para todas las estructuras de datos.

C estructura en JSON es trivial basado en h2ph. http://perl5.git.perl.org/perl.git/blob/HEAD:/utils/h2ph.PL

+0

Gracias, pero ¿es eso el análisis de la estructura C con Perl? – Schwern

+0

Es casi imposible analizar una estructura C con Perl solamente, solo si reescribe partes del compilador en perl. Y no es necesario, ya que su compilador podría ser capaz de crear información simbólica para las estructuras de este mismo compilador: stabs, dwarf, xml. Para xml hay GCC :: TranslationUnit, y también hay Convert :: Binary :: C que usa ucpp. – rurban

0

Esta macro no hace exactamente lo que quiere (generar el volcado JSON de datos C), pero creo que muestra alguna posibilidad. Puede volcar el contenido de cualquier información C con un "p (...);" llamada.

Utilicé gdb como ayudante externo para hacer esto, pero es posible implementar uno con libbfd. En ese caso, puede controlar completamente su salida, como generar una salida compatible con 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 

sangría se pierde en la pasta anterior, pero se puede agarrar la fuente de: https://github.com/tai/ruby-p-for-c

+0

Gracias por la respuesta. Depender de bibliotecas no estándar, y especialmente no de programas externos, no es posible en mi caso. Aunque me interesaría ver cómo se vería una versión de libbfd. – Schwern

2

Después de haber llegado a través de la misma edición, he escrito una yo. https://github.com/jamie-pate/jstruct. Está escrito para permitir anotar estructuras c existentes, luego generar información de metadatos basada en las anotaciones. La biblioteca lee los metadatos para importar/exportar estructuras c a json strings y viceversa. Una secuencia de comandos python se encarga de analizar el encabezado anotado y generar nuevos encabezados e inicializadores de metadatos. La biblioteca jstruct usa https://github.com/json-c/json-c internamente. También he notado https://github.com/marel-keytech ... pero eso fue después de escribir todo. (y la información en la página de ese proyecto es escasa)

Todavía no hay soporte para anotar una única estructura de una lib existente del sistema, pero podría envolver la estructura tm en una estructura personalizada anotada. (¿Creo?)

Siéntase libre de agregar solicitudes de funciones o incluso solicitudes de extracción si tiene alguna idea que la haga más útil. Una idea que tenía sería añadir una manera de incorporar ese tipo de sólo lectura estructura dentro de un envoltorio anotado con una anotación de @inline: por ejemplo (no reconocido actualmente, pero podría añadirse)

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

Código:

struct my_time t; 

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

por el momento se podría hacer lo mismo sin @inline (ya que no se ha escrito todavía) y acaba de extraer la propiedad tm a mano con json_object_to_json_string(json_object_object_get(result, "tm"))

Cuestiones relacionadas