2010-02-12 34 views
7

Tôi đã viết một chương trình để thăm dò các giới hạn của các hàm C time.h của hệ thống và loại bỏ chúng ra trong JSON. Sau đó, những thứ khác phụ thuộc vào những chức năng đó có thể biết giới hạn của chúng.Có cách nào để đổ một cấu trúc 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() và localtime() đủ đơn giản, chúng chỉ lấy số, nhưng mktime() lấy cấu trúc tm. Tôi đã viết một hàm tùy chỉnh để biến cấu trúc tm thành một băm 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ách nào để làm điều này một cách tổng quát, cho bất kỳ cấu trúc cụ thể nào không?

Lưu ý: C, không phải C++.

Trả lời

4

Không có trong C — ít nhất là nói chung. Nhưng nếu mô-đun C được biên dịch với các biểu tượng gỡ lỗi và mô-đun đối tượng có sẵn, bạn có thể phân tích cú pháp đó và khám phá mọi thứ về cấu trúc. Tôi đặt cược có một thư viện cho hệ thống của bạn để hỗ trợ điều đó.

+0

1 để đánh bại tôi vào nó. –

1

này sẽ không hoàn toàn cung cấp cho bạn những gì bạn đang yêu cầu, nhưng nó có thể giúp một chút:

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

Sau đó, bạn có thể lặp lại, ví dụ một cái gì đó tương tự (lưu ý: không được kiểm tra, xem xét này 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'; 

    /* ... */ 
} 

Bạn tương tự có thể định nghĩa NAME_AND_STRING, NAME_AND_LONG, vv (cho tm_zone và tm_gmtoff) khi cần thiết.

Một lần nữa, nó không phải là một giải pháp chung, nhưng ít nhất nó giúp bạn gần hơn một chút, có thể.

+0

Cảm ơn, đó là một chút dễ chịu hơn để làm việc với. – Schwern

+0

Tốt, tôi rất vui vì nó hữu ích. Ngoài ra, tôi chỉ nhận thấy rằng tôi nên có dấu ngoặc đơn xung quanh "buf" trong việc mở rộng vĩ mô. Tôi đã chỉnh sửa câu trả lời của mình để thêm chúng. – lindes

0

Tom Christiansen đã từng viết pstruct/h2ph trong perl CORE để phân tích thông tin .stabs từ trình biên dịch đã sử dụng và tạo thông tin có thể đọc được cho tất cả các cấu trúc dữ liệu.

Cấu trúc C thành JSON là tầm thường dựa trên h2ph. http://perl5.git.perl.org/perl.git/blob/HEAD:/utils/h2ph.PL

+0

Cảm ơn, nhưng đó là phân tích cú pháp C struct với Perl? – Schwern

+0

Hầu như không thể phân tích cú pháp cấu trúc C với chỉ perl, chỉ khi bạn viết lại các phần của trình biên dịch trong perl. Và nó không cần thiết, vì trình biên dịch của bạn có thể có khả năng tạo ra thông tin tượng trưng cho các cấu trúc cho trình biên dịch này: stabs, dwarf, xml. Đối với xml có GCC :: TranslationUnit, và cũng có Convert :: Binary :: C sử dụng ucpp. – rurban

0

Macro này không thực hiện chính xác những gì bạn muốn (tạo ra kết xuất JSON của dữ liệu C), nhưng tôi nghĩ nó cho thấy một số khả năng. Bạn có thể đổ nội dung của bất kỳ dữ liệu C nào bằng "p (...);" gọi điện.

Tôi đã sử dụng gdb làm trình trợ giúp bên ngoài để thực hiện công việc này, nhưng có thể thực hiện một công việc bằng libbfd. Trong trường hợp đó, bạn hoàn toàn có thể kiểm soát đầu ra của mình - như tạo ra đầu ra tương thích 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 

Indentation bị mất trong dán trên, nhưng bạn có thể lấy nguồn từ: https://github.com/tai/ruby-p-for-c

+0

Cảm ơn câu trả lời. Dựa vào các thư viện không chuẩn, và đặc biệt không phải các chương trình bên ngoài, là không thể trong trường hợp của tôi. Mặc dù tôi muốn được nhìn thấy những gì một phiên bản libbfd sẽ như thế nào. – Schwern

2

Sau khi đi qua cùng một vấn đề, tôi đã viết một bản thân mình. https://github.com/jamie-pate/jstruct. Nó được viết để cho phép chú thích các cấu trúc c hiện có, sau đó tạo thông tin siêu dữ liệu dựa trên các chú thích. Thư viện đọc siêu dữ liệu để nhập/xuất cấu trúc c thành chuỗi json và ngược lại. Một kịch bản python sẽ chăm sóc phân tích cú pháp tiêu đề có chú thích và tạo ra các tiêu đề mới và bộ khởi tạo siêu dữ liệu. Thư viện jstruct sử dụng https://github.com/json-c/json-c nội bộ. Tôi cũng đã nhận thấy https://github.com/marel-keytech ... nhưng đó là sau khi viết toàn bộ nội dung. (và thông tin trên trang của dự án đó thưa thớt)

Không có hỗ trợ nào cho chú thích một cấu trúc đơn lẻ từ lib hệ thống hiện có nhưng bạn có thể bọc cấu trúc tm trong cấu trúc tùy chỉnh được chú thích. (Tôi nghĩ?)

Hãy thoải mái thêm yêu cầu tính năng hoặc thậm chí kéo yêu cầu nếu bạn có ý tưởng giúp thư viện hữu ích hơn cho bạn. Một ý tưởng tôi đã sẽ có thêm một cách để nhúng rằng loại chỉ đọc struct bên trong một wrapper chú thích với một chú thích @inline: ví dụ (hiện không được hỗ trợ, nhưng có thể được thêm vào)

//@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); 

Trong lúc này bạn có thể làm điều tương tự mà không @inline (vì nó đã không được viết nào) và chỉ cần giải nén tài sản tm bằng tay với json_object_to_json_string(json_object_object_get(result, "tm"))

Các vấn đề liên quan