Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

enumerating over a structure fields in C

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!

like image 847
AK_ Avatar asked Oct 18 '15 13:10

AK_


1 Answers

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?)

like image 95
ams Avatar answered Sep 28 '22 02:09

ams