7

I have a several structs in C and I want to write the following three functions:

get_field_list(...)
get_value_by_name(...)
set_value_by_name(...)

The first should return the list of fields defined in the struct. The second and third should get and set to the appropriate field by it's name.

I'm writing the structs. I'm willing to use any macro magic if required. It's OK if ill have a triplet of functions per each struct, but generic structures are better. Function pointers are also fine...

Basically I want some elementary reflections for structs....

Relevent: https://natecraun.net/articles/struct-iteration-through-abuse-of-the-c-preprocessor.html

motivation

I'm trying to build a DAL (Data Access Layer) for a native app written in C. I'm using SQLite as a DB. I need to store various structures, and to be able to insert\ update\ get(select by key)\ search (select by query), and also to create\ drop the required table.

Basicly I want something like Hibernate for C ...

My best idea so far is to use MACROs, or some code generation utility, or a script, to create my structs together with meta-data I could use to dynamically build all my SQL commands. And also to have a small 'generic' module to implement all the basic procedures i need...

Different or better ideas to solve my actual problem will also be appreciated!

AK_
  • 7,981
  • 7
  • 46
  • 78
  • 1
    C does not have this capability. – Random832 Oct 18 '15 at 13:54
  • 1
    @Random832 sure, in the sense that C doesnt have reflections... but I'm sure there is a way to get a similar behavior – AK_ Oct 18 '15 at 14:23
  • If your struct has a character array, a struct and an integer then what return type does get_value_by_name() have? Or do your structs members all have the same types? – Jerry Jeremiah Oct 18 '15 at 14:34
  • C++ allows pointers to member variables. That would make this really possible. – Jerry Jeremiah Oct 18 '15 at 14:36
  • 1
    See this question for a good discussion about reflections in C: http://stackoverflow.com/questions/1353022/reflection-support-in-c – Code Different Oct 18 '15 at 14:51
  • Your question lacks precision on how you want the "parameters" to these functions/macros be specified. If for the later two you allow an identifier to be passed in (not a string) then this can be done trivially. – Jens Gustedt Oct 18 '15 at 15:09
  • 1
    Typically this would be done by `offsetof` fiddling to access the fields relative to the structure pointers, and x-macros as well as `#` field name stringization to generate both the structure declarations as well as a field array descriptors. It's not terribly pretty though so it's not worth the effort unless it saves you a fair bit of manual typing. – doynax Oct 18 '15 at 16:06
  • @JensGustedt, obviously I want strings, or something I can decide at run-time... – AK_ Oct 28 '15 at 09:48
  • @JerryJeremiah I guess void*... I can find a way to handle the types... – AK_ Oct 28 '15 at 09:50
  • @AK_ It doesn't make any sense to name identifiers in run-time, period. – Lundin Oct 29 '15 at 15:23
  • @Lundin so basicly you're saying reflections don't makse sense? – AK_ Oct 31 '15 at 10:58
  • Sounds like you're trying to implement a [dictionary](https://en.wikipedia.org/wiki/Associative_array) – user3386109 Nov 01 '15 at 06:26
  • @user3386109 I really don't... I know I can solve this problem with a map, but I need to return structs as a part of the module I'm building... – AK_ Nov 01 '15 at 14:17
  • Why use structs instead of REAL db tables in relation C-SQL ? – Imobilis Nov 02 '15 at 07:50
  • CSQL you mean this: https://en.wikipedia.org/wiki/CSQL ? I'm building a Data Access Layer. the requirement is that the layer should use simple C structs in it's interface... – AK_ Nov 02 '15 at 10:05

3 Answers3

6

It can be done with "macro magic" as you suggested:

For each struct, create a header file (mystruct-fields.h) like this:

FIELD(int,   field1)
FIELD(int*,  field2)
FIELD(char*, string1)

Then, in another header (mystruct.h) you include that as many times as you need:

#define FIELD(T,N) T N;

struct mystruct {
#include "mystruct-fields.h"
};

#undef FIELD

#define FIELD(T,N) { STRINGIFY(T), STRINGIFY(N), offsetof(mystruct, N) },
#define STRINGIFY1(S) #S
#define STRINGIFY(S) STRINGIFY1(S)

struct mystruct_table {
  struct {
    const char *type, *name;
    size_t offset;
  } field[];
} table = {
#include "mystruct-fields.h"
  {NULL, NULL, 0}
};

#undef FIELD

You can then implement your reflection functions, using the table, however you choose.

It might be possible, using another layer of header file includes, to reuse the above code for any struct without rewriting it, so your top-level code might only have to say something like:

#define STRUCT_NAME mystruct
#include "reflectable-struct.h"
#undef STRUCT_NAME

Honestly though, it's easier for the people who come after you if you just write the struct normally, and then write out the table by hand; it's much easier to read, your IDE will be able to auto-complete your types, and prominent warnings in the comments should help prevent people breaking it in future (and anyway, you do have tests for this right?)

ams
  • 24,923
  • 4
  • 54
  • 75
  • While this might answer the question, it should be noted that macros like these are incredibly bad practice. Especially since there is no way to avoid hard-coding everything. @AK_ You need to consider what problem you are actually trying to solve here, rather than coming up with a solution for which there exists no matching problem. – Lundin Oct 29 '15 at 15:21
  • @ams could you take a look at the "motivation" part i added to the question? – AK_ Oct 31 '15 at 11:20
  • That's a different question to the one originally asked, and not well suited to StackOverflow. Basically, you want *serializable* types and C doesn't do that automatically. I would just write a function for each structure. You can just dump the bytes directly into a database string, and that'll just work for everything except except pointers. For those you'll need to use some kind of index. – ams Oct 31 '15 at 13:21
  • @ams I don't want serialization... I want to translate from C datatypes to SQL thingys... and I need them laid out correctly in the SQLite file, so i cant just dump them as a string/ stream... – AK_ Nov 01 '15 at 14:19
  • My answer would be the same though: I'd write a function per structure, just as in C++ you'd write a method per class. – ams Nov 01 '15 at 21:01
  • The thing here is that C really needs struct introspection, which could be very cleanly and straightforward implemented with a `memberof()` operator, and it wouldn't conflict with the C paradigm at all (in fact, new additions to the language like C11 `_Generic` expressions conflict more with the C paradigm than a `memberof()` operator would --I really love `_Generic`, I'm just putting it as an example of what can be accepted or not in the C language). When purists hear "reflection" they get scared, but we are not talking about reflection, just type introspection, a totally different thing. – cesss Aug 29 '21 at 09:38
0

The way to do it is to have your struct in a database format or xml or a text file or whatever format you are comfortable with. And use a C program to write a .h file for each struct. The .h file contains the struct , an enum of the fields, and array of char containing the names of each field. From there you can build anything you need. Preferably using a program generator.

BobRun
  • 756
  • 5
  • 12
0

Take a look at Metaresc library. It provides reflection capabilities in plain C. Metadata of types definition could be derived either from custom macro language that replaces standard C type definition semantics or from compiler debug info. Sample app is provided in README.md