Nested structures and Eet Data Descriptors

We've seen already a simple example of how to use Eet Data Descriptors to handle our structures, but it didn't show how this works when you have structures inside other structures.

Now, there's a very simple case of this, for when you have inline structs to keep your big structure more organized, you don't need anything else besides what this simple example does. Just use something like `some_struct.sub_struct.member` when adding the member to the descriptor and it will work.

For example:

typedef struct
{
int a_number;
char *a_string;
struct {
int other_num;
int one_more;
} sub;
} some_struct;
void some_function()
{
...
my_desc = eet_data_descriptor_stream_new(&eddc);
EET_DATA_DESCRIPTOR_ADD_BASIC(my_desc, some_struct, "a_number",
a_number, EET_T_INT);
EET_DATA_DESCRIPTOR_ADD_BASIC(my_desc, some_struct, "a_string",
a_string, EET_T_STRING);
EET_DATA_DESCRIPTOR_ADD_BASIC(my_desc, some_struct, "sub.other_num",
sub.other_num, EET_T_INT);
EET_DATA_DESCRIPTOR_ADD_BASIC(my_desc, some_struct, "sub.one_more",
sub.one_more", EET_T_INT);
...
}

But this is not what we are here for today. When we talk about nested structures, what we really want are things like lists and hashes to be taken into consideration automatically, and all their contents saved and loaded just like ordinary integers and strings are.

And of course, Eet can do that, and considering the work it saves you as a programmer, we could say it's even easier to do than handling just integers.

Let's begin with our example then, which is not all too different from the simple one introduced earlier.

We won't ignore the headers this time to show how easy it is to use Eina data types with Eet, but we'll still skip most of the code that is not pertinent to what we want to show now, but as usual, you can get it full by following this link.

#include <Eina.h>
#include <Eet.h>
typedef struct
{
unsigned int version; // it is recommended to use versioned configuration!
const char *name;
int id;
int not_saved_value; // example of not saved data inside!
Eina_Bool enabled;
Eina_List *subs;
} My_Conf_Type;
typedef struct
{
const char *server;
int port;
} My_Conf_Subtype;

Extremely similar to our previous example. Just a new struct in there, and a pointer to a list in the one we already had. Handling a list of subtypes is easy on our program, but now we'll see what Eet needs to work with them (Hint: it's easy too).

static Eet_Data_Descriptor *_my_conf_descriptor;
static Eet_Data_Descriptor *_my_conf_sub_descriptor;

Since we have two structures now, it's only natural that we'll need two descriptors. One for each, which will be defined exactly as before.

static void
_my_conf_descriptor_init(void)
{
Eet_Data_Descriptor_Class eddc;
EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, My_Conf_Type);
_my_conf_descriptor = eet_data_descriptor_stream_new(&eddc);
EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(&eddc, My_Conf_Subtype);
_my_conf_sub_descriptor = eet_data_descriptor_stream_new(&eddc);

We create our descriptors, each for one type, and as before, we are going to use a simple macro to set their contents, to save on typing.

#define MY_CONF_ADD_BASIC(member, eet_type) \
EET_DATA_DESCRIPTOR_ADD_BASIC \
(_my_conf_descriptor, My_Conf_Type, # member, member, eet_type)
#define MY_CONF_SUB_ADD_BASIC(member, eet_type) \
EET_DATA_DESCRIPTOR_ADD_BASIC \
(_my_conf_sub_descriptor, My_Conf_Subtype, # member, member, eet_type)
MY_CONF_SUB_ADD_BASIC(server, EET_T_STRING);
MY_CONF_SUB_ADD_BASIC(port, EET_T_INT);
MY_CONF_ADD_BASIC(version, EET_T_UINT);
MY_CONF_ADD_BASIC(name, EET_T_STRING);
MY_CONF_ADD_BASIC(id, EET_T_INT);
MY_CONF_ADD_BASIC(enabled, EET_T_UCHAR);

So far, nothing new. We have our descriptors and we know already how to save them separately. But what we want is to link them together, and even more so, we want our main type to hold a list of more than one of the new sub type. So how do we do that?

Simple enough, we tell Eet that our main descriptor will hold a list, of which each node will point to some type described by our new descriptor.

EET_DATA_DESCRIPTOR_ADD_LIST
(_my_conf_descriptor, My_Conf_Type, "subs", subs, _my_conf_sub_descriptor);

And that's all. We are closing the function now so as to not leave dangling curly braces, but there's nothing more to show in this example. Only other additions are the necessary code to free our new data, but you can see it in the full code listing.

#undef MY_CONF_ADD_BASIC
#undef MY_CONF_SUB_ADD_BASIC
} /* _my_conf_descriptor_init */