Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Get list of C structure members

Is it possible to get the list of members of a structure as a char ** ?

For example, something like this:

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

/* ... */

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

I am also interested in compiler-dependent methods. Is there such a thing ?

Thank you for your time.

like image 759
nc3b Avatar asked Oct 06 '11 12:10

nc3b


3 Answers

Here's a proof of concept:

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

This is it's output:

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

You can improve it to better support more complex member types and to actually build a list of names of the members.

like image 174
Alexey Frunze Avatar answered Oct 03 '22 09:10

Alexey Frunze


There's definitely no standard way.

If you're willing to compile the code twice, you could have a preprocessor-define-wrapped codepath only enabled for the second pass which reads debugging information from the compilation units produced by the first pass to get the member names. You could also analyze the source code to get the list at run time.

Finally, you could use preprocessor macros to define the struct and have the macros also emit an entry in another variable for each struct member, effectively keeping two not-directly-related items in sync.

like image 7
Borealid Avatar answered Oct 03 '22 07:10

Borealid


There's no portable standard way of doing this. Last time I wanted to solve a similar problem I used SWIG to produce some XML which I then processed to generate the meta information I wanted. gcc-xml could do the same thing too.

like image 2
Flexo Avatar answered Oct 03 '22 08:10

Flexo