2011-10-06 10 views
8

¿Es posible obtener la lista de miembros de una estructura como char **?Obtener la lista de miembros de la estructura C

Por ejemplo, algo como esto:

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

/* ... */ 

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

también estoy interesado en los métodos de compilación-dependiente. ¿Hay tal cosa?

Gracias por su tiempo.

Respuesta

2

Definitivamente no hay manera estándar.

Si está dispuesto a compilar el código dos veces, podría tener un código de ruta preprocesado-definido-envuelto solo habilitado para el segundo pase que lee la información de depuración de las unidades de compilación producidas por el primer pase para obtener los nombres de los miembros. También podría analizar el código fuente para obtener la lista en tiempo de ejecución.

Finalmente, podría usar macros de preprocesador para definir la estructura y hacer que las macros también emitan una entrada en otra variable para cada miembro de la estructura, manteniendo efectivamente sincronizados dos elementos no directamente relacionados.

+0

+1 Muéstrame más sobre el último párrafo. – nc3b

+0

@ nc3b Utiliza una macro compiladora 'DEFSTRUCT (int, x, float, y, char *, z)' o similar que crea la estructura anterior y también emite 'char ** members = {" x "," y " , "z", NULL}; '. Si quiere una macro menos complicada, puede hacerlo en dos invocaciones, como 'DEFSTRUCT (int, x)' y luego 'DEFLIST (int, x)' (o con una macro que defina para tener diferentes comportamientos para cada llamada) y luego invocar dos veces). – Borealid

2

No hay una forma estándar portátil de hacerlo. La última vez que quise resolver un problema similar utilicé SWIG para producir un poco de XML que luego procesé para generar la metainformación que deseaba. gcc-xml podría hacer lo mismo también.

0

Qt escanea los archivos .h y genera .cpp con una función que devuelve dicho código.

También puede lograr esto con un grupo de macros, pero hay que escribir a mano para cada tipo

+0

Muéstrame un ejemplo de trabajo. Veo que mencionaste Qt, solo estoy interesado en C, no en C++. – nc3b

1

No, eso no es posible.

C es un lenguaje estáticamente tipado sin reflexión. Los nombres de tipos no tienen ningún significado más allá de la etapa de compilación, y ni siquiera es el caso de que una variable en particular esté visible en el código binario. El compilador tiene mucha libertad para optimizar y reordenar, siempre que el programa se comporte como según lo describe el estándar de lenguaje.

Puede probar un poco de magia de preprocesador para obtener un manejo limitado de los nombres de tipo, pero eso está lejos de la reflexión general (y estrictamente hablando fuera del lenguaje C).

Lo que principio no puede hacer en C es la siguiente:

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

nombres de tipos no son conceptos de tiempo de ejecución; y por encima de eso, la tipificación estática hace imposible cualquier construcción de este tipo.

Aquí está uno de los pocos trucos preprocesador que se me ocurren:

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

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

He aquí una prueba de concepto:

#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; 
} 

Ésta es su salida:

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

Puede mejorarlo para admitir mejor los tipos de miembros más complejos y crear una lista de nombres de los miembros.

+0

+1 ¡Un montón de esfuerzo, gracias! – nc3b

1

Tome un vistazo a la biblioteca Metaresc https://github.com/alexanderchuranov/Metaresc

Se proporciona una interfaz para la declaración de tipos que también generará meta-datos para el tipo. Según los metadatos, puede serializar/deserializar fácilmente objetos de cualquier complejidad. Fuera de la caja, puede serializar/deserializar XML, JSON, XDR, notación tipo Lisp, notación C-init.

Aquí está un ejemplo sencillo:

#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); 
} 

Este programa dará salida

$ ./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 funciona bien para la última gcc y sonido metálico.

Cuestiones relacionadas