Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Is there a way to dump a C struct?

Tags:

json

c

struct

I've written a program to probe the limits of a system's C time.h functions and dump them out in JSON. Then other things which depend on those functions can know their limits.

# 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() and localtime() are simple enough, they just take numbers, but mktime() takes a tm struct. I wrote a custom function to turn a tm struct into a JSON hash.

/* 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;
}

Is there a way to do this generically, for any given struct?

Note: C, not C++.

like image 765
Schwern Avatar asked Feb 12 '10 06:02

Schwern


3 Answers

Not in C—at least in general. But if the C module is compiled with debug symbols, and the object module is available, you could parse that and discover everything about the structure. I bet there's a library for your system to assist with that.

like image 121
wallyk Avatar answered Oct 12 '22 13:10

wallyk


This won't quite give you what you're asking for, but it might help a little:

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

You could then iterate, e.g. something like (note: not tested; consider this pseudo-code):

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

    /* ... */
}

You could similarly define NAME_AND_STRING, NAME_AND_LONG, etc. (for tm_zone and tm_gmtoff) as needed.

Again, it's not a generic solution, but it at least gets you a little closer, maybe.

like image 38
lindes Avatar answered Oct 12 '22 13:10

lindes


Having come across the same issue, i wrote one myself. https://github.com/jamie-pate/jstruct . It's written to allow annotating existing c structures, then generate meta-data information based on the annotations. The library reads the metadata to import/export c structures to json strings and back. A python script takes care of parsing the annotated header and generating new headers and metadata initializers. The jstruct library uses https://github.com/json-c/json-c internally. I have also noticed https://github.com/marel-keytech... but that was after writing the entire thing. (and info on that project's page is sparse)

There's no support yet for annotating a single struct from an existing system lib but you could wrap the tm struct in an annotated custom struct. (I think?)

Feel free to add features requests or even pull requests if you have ideas that would make the library more useful to you. One idea I had would be to add a way to embed that kind of read only struct inside an annotated wrapper with a @inline annotation: eg (currently unsupported but could be added)

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

Code:

struct my_time t;

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

In the mean time you could do the same thing without @inline (since it hasn't been written yet) and just extract the tm property by hand with json_object_to_json_string(json_object_object_get(result, "tm"))

like image 27
Jamie Pate Avatar answered Oct 12 '22 15:10

Jamie Pate