Is it possible to get the list of members of a structure as a char **
?
For example, something like this:
struct mystruct {
in开发者_如何学Got 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.
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.
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.
Take a look at Metaresc library https://github.com/alexanderchuranov/Metaresc
It provides interface for types declaration that will also generate meta-data for the type. Based on meta-data you can easily serialize/deserialize objects of any complexity. Out of the box you can serialize/deserialize XML, JSON, XDR, Lisp-like notation, C-init notation.
Here is a simple example:
#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 (NULL == tdp)
return (EXIT_FAILURE);
int i;
for (i = 0; i < tdp->fields_size / sizeof (tdp->fields[0]); ++i)
MR_PRINT ("offset [", tdp->fields[i].fdp->offset,
"] size ", tdp->fields[i].fdp->size,
" field '", tdp->fields[i].fdp->name.str,
"' type '", tdp->fields[i].fdp->type,
"' mr_type ", (mr_type_t, &tdp->fields[i].fdp->mr_type)
);
return (EXIT_SUCCESS);
}
This program will output
$ ./struct
offset [0] size 4 field 'x' type 'int' mr_type MR_TYPE_INT32
offset [4] size 4 field 'y' type 'float' mr_type MR_TYPE_FLOAT
offset [8] size 8 field 'z' type 'string_t' mr_type MR_TYPE_STRING
Library works fine for latest gcc and clang.
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.
No, that's not possible.
C is a statically typed language without reflection. Type names don't have any meaning past the compilation stage, and it's not even the case that any particular variable is at all visible in the binary code. The compiler has a lot of freedom to optimize and reorder, as long as the program behaves as described by the language standard.
You can try some preprocessor magic to get a limited handle on type names, but that's far from general reflection (and strictly speaking outside the C langauge).
The principle thing you cannot do in C is this:
const char * tn = "int";
auto n = get_type(tn)(42); // a.k.a. "int n = 42;", NOT POSSIBLE
Type names are not runtime concepts; and above that, static typing makes any such construction impossible.
Here's one of the few preprocessor gimmicks I can think of:
#define print_size(t) printf("sizeof(" #t ") = %u\n", sizeof(t));
print_size(int);
print_size(long double);
Qt scans the .h files and generated .cpp with function returning such code.
You can also achieve this with a bunch of macros, but you need to write them by hand for each types
精彩评论