Получить список членов структуры C - PullRequest
13 голосов
/ 06 октября 2011

Можно ли получить список элементов структуры в виде char **?

Например, что-то вроде этого:

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

/* ... */

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

Меня также интересует компилятор-зависимые методы.Есть ли такая вещь?

Спасибо, что уделили время.

Ответы [ 6 ]

12 голосов
/ 06 октября 2011

Вот подтверждение концепции:

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

Это вывод:

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

Вы можете улучшить его, чтобы лучше поддерживать более сложные типы членов и фактически создать список имен членов.

4 голосов
/ 06 октября 2011

Стандартного способа определенно не существует.

Если вы готовы скомпилировать код дважды, вы можете включить кодированный путь препроцессора, определенный только для второго прохода, который считывает отладочную информацию из модулей компиляциипроизводится первым проходом, чтобы получить имена членов.Вы также можете проанализировать исходный код, чтобы получить список во время выполнения.

Наконец, вы можете использовать макросы препроцессора для определения структуры и иметь макросы также для создания записи в другой переменной для каждого члена структуры, эффективно сохраняядва не связанных непосредственно элемента в синхронизации.

2 голосов
/ 06 октября 2011

Нет портативного стандартного способа сделать это. В прошлый раз, когда я хотел решить подобную проблему, я использовал SWIG для создания некоторого XML, который я затем обработал, чтобы сгенерировать мета-информацию, которую я хотел. gcc-xml тоже может сделать то же самое.

1 голос
/ 11 августа 2015

Взгляните на библиотеку Metaresc https://github.com/alexanderchuranov/Metaresc

Предоставляет интерфейс для объявления типов, который также генерирует метаданные для типа.На основе метаданных вы можете легко сериализовать / десериализовать объекты любой сложности.Из коробки вы можете сериализовать / десериализовать XML, JSON, XDR, нисходящую нотацию Lisp, нотацию C-init.

Вот простой пример:

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

Эта программа выведет

$ ./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'

Библиотека прекрасно работает для последних версий gcc и clang.

1 голос
/ 06 октября 2011

Нет, это невозможно.

C - статически типизированный язык без отражения. Имена типов не имеют никакого значения после стадии компиляции, и даже в том случае, когда какая-либо конкретная переменная вообще видна в двоичном коде, это не имеет никакого значения. Компилятор может свободно оптимизировать и переупорядочивать, если программа ведет себя , как описано в стандарте языка.

Вы можете попробовать магию препроцессора, чтобы получить ограниченный контроль над именами типов, но это далеко от общего размышления (и, строго говоря, за пределами языка C).

Принцип, который вы не можете сделать в C, заключается в следующем:

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

Имена типов не понятия времени выполнения; и, кроме того, статическая типизация делает невозможной такую ​​конструкцию.

Вот одна из немногих уловок препроцессора, о которых я могу подумать:

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

print_size(int);
print_size(long double);
0 голосов
/ 06 октября 2011

Qt сканирует файлы .h и генерирует .cpp с функцией, возвращающей такой код.

Вы также можете достичь этого с помощью нескольких макросов, но вам нужно написать их вручную для каждого типа

Добро пожаловать на сайт PullRequest, где вы можете задавать вопросы и получать ответы от других членов сообщества.
...