2011-10-06 18 views
8

È possibile ottenere l'elenco dei membri di una struttura come char **?Ottieni l'elenco dei membri della struttura C

Per esempio, qualcosa di simile:

struct mystruct { 
    int x; 
    float y; 
    char *z; 
}; 

/* ... */ 

char **members = MAGIC(struct mystruct); /* {"x", "y", "z", NULL}. */ 

Sono anche interessato a metodi compilatore-dipendente. C'è una cosa del genere ?

Grazie per il vostro tempo.

risposta

2

C'è sicuramente un modo standard.

Se si desidera compilare il codice due volte, è possibile avere un codepath con preprocessore definito dall'utente abilitato solo per il secondo passaggio che legge le informazioni di debug dalle unità di compilazione prodotte dal primo passaggio per ottenere i nomi dei membri. È anche possibile analizzare il codice sorgente per ottenere l'elenco in fase di esecuzione.

Infine, è possibile utilizzare macro di preprocessore per definire la struttura e fare in modo che le macro emettano anche una voce in un'altra variabile per ogni membro della struttura, mantenendo in modo efficace due elementi non direttamente correlati in sincronizzazione.

+0

+1 Mostrami di più sull'ultimo paragrafo. – nc3b

+0

@ nc3b Si utilizza una macro del compilatore 'DEFSTRUCT (int, x, float, y, char *, z)' o simili che crea la struttura precedente e inoltre emette 'char ** members = {" x "," y " , "z", NULL}; '. Se vuoi una macro meno complicata, puoi farlo in due invocazioni, come 'DEFSTRUCT (int, x)' e quindi 'DEFLIST (int, x)' (o con una macro che definisci per avere comportamenti diversi per ogni chiamata e quindi invoca due volte). – Borealid

2

Non esiste un modo standard portatile per farlo. L'ultima volta che volevo risolvere un problema simile ho usato SWIG per produrre un codice XML che poi ho elaborato per generare le meta informazioni che volevo. gcc-xml potrebbe fare la stessa cosa.

0

Qt esegue la scansione dei file .h e genera .cpp con la funzione che restituisce tale codice.

È anche possibile raggiungere questo obiettivo con un gruppo di macro, ma è necessario scriverli a mano per ogni tipo

+0

Mostrami un esempio funzionante. Vedo che hai parlato di Qt, mi interessa solo C, non C++. – nc3b

1

No, non è possibile.

C è un linguaggio tipizzato in modo statico senza riflessione. I nomi dei tipi non hanno alcun significato oltre la fase di compilazione, e non è nemmeno il caso che qualsiasi particolare variabile sia visibile nel codice binario. Il compilatore ha molta libertà di ottimizzare e riordinare, purché il programma si comporti come descritto dallo standard di lingua.

È possibile provare alcune magie del preprocessore per ottenere un handle limitato sui nomi dei tipi, ma questo è lontano dalla riflessione generale (e in senso stretto al di fuori del linguaggio C).

La cosa principio si non può fare in C è questa:

const char * tn = "int"; 
auto n = get_type(tn)(42); // a.k.a. "int n = 42;", NOT POSSIBLE 

nomi tipo non sono concetti di esecuzione; e soprattutto, la tipizzazione statica rende impossibile qualsiasi costruzione del genere.

Ecco uno dei pochi espedienti preprocessore mi vengono in mente:

#define print_size(t) printf("sizeof(" #t ") = %u\n", sizeof(t)); 

print_size(int); 
print_size(long double); 
10

Ecco una prova di concetto:

#include <stdio.h> 
#include <string.h> 

#define MEMBER(TYPE,NAME,MORE) TYPE NAME MORE 

#define TSTRUCT(NAME,MEMBERS) \ 
    typedef struct NAME { \ 
    MEMBERS \ 
    } NAME; \ 
    const char* const NAME##_Members = #MEMBERS; 

#define PRINT_STRUCT_MEMBERS(NAME) printStructMembers(NAME##_Members) 

TSTRUCT(S, 
    MEMBER(int,x;, 
    MEMBER(void*,z[2];, 
    MEMBER(char,(*f)(char,char);, 
    MEMBER(char,y;, 
))))); 

void printStructMembers(const char* Members) 
{ 
    int level = 0; 
    int lastLevel = 0; 
    const char* p; 
    const char* pLastType = NULL; 
    const char* pLastTypeEnd = NULL; 

    for (p = Members; *p; p++) 
    { 
    if (strstr(p, "MEMBER(") == p) 
    { 
     p += 6; // strlen("MEMBER") 
     level++; 
     lastLevel = level; 
     pLastType = p + 1; 
    } 
    else if (*p == '(') 
    { 
     level++; 
    } 
    else if (*p == ')') 
    { 
     level--; 
    } 
    else if (*p == ',') 
    { 
     if (level == lastLevel) 
     { 
     if ((pLastType != NULL) && (pLastTypeEnd == NULL)) 
     { 
      pLastTypeEnd = p; 
     } 
     } 
    } 
    else if (strstr(p, ";,") == p) 
    { 
     if ((pLastType != NULL) && (pLastTypeEnd != NULL)) 
     { 
     const char* pp; 
     printf("["); 
     for (pp = pLastType; pp < pLastTypeEnd; pp++) 
      printf("%c", *pp); // print type 
     printf("] ["); 
     for (pp = pLastTypeEnd + 1; pp < p; pp++) 
      printf("%c", *pp); // print name 
     printf("]\n"); 
     } 
     pLastType = pLastTypeEnd = NULL; 
    } 
    } 
} 

char fadd(char a, char b) 
{ 
    return a + b; 
} 

S s = 
{ 
    1, 
    { NULL, NULL }, 
    &fadd, 
    'a' 
}; 

int main(void) 
{ 
    PRINT_STRUCT_MEMBERS(S); 
    return 0; 
} 

Questo è il suo output:

[int] [x] 
[void*] [z[2]] 
[char] [(*f)(char,char)] 
[char] [y] 

Puoi migliorarlo per supportare meglio i tipi di membri più complessi e creare effettivamente un elenco di nomi dei membri.

+0

+1 Un sacco di sforzi, grazie! – nc3b

1

Date un'occhiata a Metaresc biblioteca https://github.com/alexanderchuranov/Metaresc

Esso fornisce l'interfaccia per la dichiarazione tipi che anche generare metadati per il tipo. Basati su meta-dati, puoi facilmente serializzare/deserializzare oggetti di qualsiasi complessità. È possibile serializzare/deserializzare XML, JSON, XDR, notazione Lisp-like, notazione C-init.

Ecco un semplice esempio:

#include <stdio.h> 
#include <stdlib.h> 

#include "metaresc.h" 

TYPEDEF_STRUCT (sample_t, 
       int x, 
       float y, 
       string_t z 
       ); 

int main (int argc, char * argv[]) 
{ 
    mr_td_t * tdp = mr_get_td_by_name ("sample_t"); 

    if (tdp) 
    { 
     int i; 
     for (i = 0; i < tdp->fields_size/sizeof (tdp->fields[0]); ++i) 
     printf ("offset [%zd] size %zd field '%s'\n", 
       tdp->fields[i].fdp->offset, 
       tdp->fields[i].fdp->size, 
       tdp->fields[i].fdp->name.str, 
       tdp->fields[i].fdp->type); 
    } 
    return (EXIT_SUCCESS); 
} 

Questo programma emette

$ ./struct 
offset [0] size 4 field 'x' type 'int' 
offset [4] size 4 field 'y' type 'float' 
offset [8] size 8 field 'z' type 'string_t' 

Biblioteca funziona bene per l'ultimo gcc e clang.

Problemi correlati