Feature request: struct introspection
Another of the features I really miss in C is struct introspection (ie: having automatic getters and setters for struct fields accessed by their numerical index), which would open the door to automatic struct serialization and very useful related things.
For example, I imagine something like this:
struct myvector{
double x;
double y;
double z;
};
struct mydata{
uint32_t onevalue;
struct myvector v;
char id[5];
};
struct mydata datavalues={0};
size_t numfields = numfieldsof(struct mydata);
for(size_t f=0; f<numfields; f++) {
fread(&datavalues+offsetof(fieldof(struct mydata, f), sizeof(fieldof(struct mydata, f), 1, fileptr));
};
This would need two new operators: numfieldsof(), and fieldof(), and of course offsetof() should be available.
I also foresee the need of assigning flags or attributes to struct fields, so that the program could have for example an enum for the type of the field (which could be necessary in some kinds of serialization), or a flag for not serializing a field which shouldn't be serialized). Perhaps an extra operator returning the field name as a C string could be nice as well, something that could let you do printf("The name of the third field in struct mydata is: '%s'", fieldident(fieldof(struct mydata, 2)));
Things that might require some additional thought:
- Pointers to dynamic arrays (maybe a raw implementation as just a pointer could be the way to go).
- C strings (null terminated strings: if they are arrays, no problem, but if they are pointers to dynamic arrays, then it's perhaps a particular case of previous comment).
- Bitfields (not sure if they would need an additional consideration).
- How to let the program define flags or enums for each struct field (i.e: How to set and read per-field flags such as "DONT_SERIALIZE", "TYPE_U32", "TYPE_FLOAT"... of course all these enums or definitions should be something defined only in the user program... Cake shouldn't know nor care about these flags/enums, because otherwise it would limit this feature by constraining what the implementation can do and cannot do).
Note that nested structs shouldn't require additional thought: My simple example above has a nested struct.
Getting the field identifier as a C string would definitely be necessary for implementing serialization formats that are made of token-value pairs, so at least I foresee the need for three operators: numfieldsof(), fieldof() and fieldident() (or other alternative names, of course).
To be practical, I guess this introspection should run at compile time right?
Otherwise the compiler generates, let's say a constant for numfieldsof but then the programmer has to run the program separately (not during compilation anymore).
So, having to run the program later, it is the same as if cake generated a json file with the AST and a separated program consume this AST.
A thirthy option is just write a "AST visit" source and recompile cake with this visit.
Yes, I believe that the hypothetical numfieldsof(), fieldof() and fieldident() would behave just like sizeof() and offsetof() in which they evaluate at compile time, but however their evaluation is used at runtime.
Anyway, I kept thinking after I posted, and now I found that perhaps an easier solution could be to provide a function pointer to each struct field (it could be defined as a struct field C23 attribute perhaps, so each field has a "callback"). Then maybe Cake could have a pragma called something like "InvokeStructCallbacks", and the client program could use the callbacks for registering the fields in the way it desires.
The callback prototype could be for example void (*cb)(const char *ident, size_t idx, void *clientdata), where ident is the C string of the struct name, idx is the # of the field in the struct (0 is the first), and clientdata is a pointer to data provided by the client program (and which could hold any flags or data the program desires).
So, if your program invokes _Pragma(InvokeStructCallbacks(struct mystruct)), then Cake would convert that to calls to all the callbacks registered to the struct fields in mystruct (if any). If some fields don't have callback, they would be skipped.
Maybe this implementation would be much easier to accomplish, but I'm not yet sure.
Actually, I think idx is not needed at all. Why do you need to know the position of a field in a struct? The offset in bytes of the field would be much more interesting (and perhaps even its size).
So, the callback prototype could be: void (*cb)(const char *ident, size_t offset, size_t size, void *clientdata)
In the same way we can generate json files with the AST, we also could generate C data types with the AST.
struct X {
int i;
};
struct struct_member{
char* name;
char* type;
};
struct X_Reflection {
char* name;
struct struct_member* members[1];
}
x_reflection =
{
.name = "X",
.members = {
&(struct struct_member){"i", "int"}
}
};
The command
cake source.c -reflect
could generate source.reflect.h with all the data.
(closed the feature by mistake - reopening it)
I believe your suggestion for cake source.c -reflect would be exactly what I need. The only thing I would add is to be able to specify attributes for each member, and get also such attributes in the reflect result (I guess C23 supports per-member attributes in structs, doesn't it?). About these attributes, do you think it would be possible to specify attributes that Cake doesn't understand, but that the client program will understand? For example, imagine that you specify the attribute [[myprogram::dontserialize]] when defining a member in the struct definition. Obviously, that attribute is neither standard, nor Cake should know about it. But it would be nice that Cake doesn't issue an error for unknown attribute, and so the client program will be able to read it.
I am adding a static_debug that prints information about static analysis I could create a static_reflection and print on compiler output the reflection text. For instance a json, or c struct. Then is just a matter of define what/how to print. let´s say
struct X x;
static_reflection(x, "json");
static_reflection(x, "c");
Do you mean that the output of static_reflection() would be used-defined? Or do you mean that your idea is to implement in Cake the reflection for C (which would be similar to the example you wrote on your last post from Jun 16), and the reflection for json I/O? Note that doing json I/O directly from Cake sounds awesome but that's not easy: how would you serialize dynamic arrays, which are not first-class citizens in C and would need at least one struct field for the pointer, and another struct field for the number of items in the array?
(I need this feature for automatic serialization, so if your plan is to implement the json reflection, that would be great and would save me lots of time, but what I was expecting is to have the reflection as C code and having to write the serialization code myself)
Ah, BTW, don't forget to include support for attributes in the C reflection output, because I need to tag fields with the "flavours" that each field has (as I said in a previous post, I'd like to be able to provide my custom user-defined attributes).