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()
{
...
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);
...
}
#define EET_T_STRING
Data type: char *.
Definition: Eet.h:2589
#define EET_T_INT
Data type: int.
Definition: Eet.h:2581
EAPI Eet_Data_Descriptor * eet_data_descriptor_stream_new(const Eet_Data_Descriptor_Class *eddc)
This function creates a new data descriptor and returns a handle to the new data descriptor.
Definition: eet_data.c:2084
#define EET_DATA_DESCRIPTOR_ADD_BASIC(edd, struct_type, name, member, type)
Adds a basic data element to a data descriptor.
Definition: Eet.h:3432

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>
Eina Utility library.
#include <Eet.h>
The file that provides the eet functions.
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;
unsigned char Eina_Bool
Type to mimic a boolean.
Definition: eina_types.h:527
Type for a generic double linked list.
Definition: eina_list.h:318

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;
struct _Eet_Data_Descriptor Eet_Data_Descriptor
Opaque handle that have information on a type members.
Definition: Eet.h:2631

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)
{
Instructs Eet about memory management for different needs under serialization and parse process.
Definition: Eet.h:2828
_my_conf_descriptor = eet_data_descriptor_stream_new(&eddc);
_my_conf_sub_descriptor = eet_data_descriptor_stream_new(&eddc);
#define EET_EINA_STREAM_DATA_DESCRIPTOR_CLASS_SET(clas, type)
This macro is an helper that set all the parameter of an Eet_Data_Descriptor_Class correctly when you...
Definition: Eet.h:3034

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);
#define EET_T_UCHAR
Data type: unsigned char.
Definition: Eet.h:2585
#define EET_T_UINT
Data type: unsigned int.
Definition: Eet.h:2587

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.

(_my_conf_descriptor, My_Conf_Type, "subs", subs, _my_conf_sub_descriptor);
#define EET_DATA_DESCRIPTOR_ADD_LIST(edd, struct_type, name, member, subtype)
Adds a linked list type to a data descriptor.
Definition: Eet.h:3511

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 */