Eina_Value struct usage

This example will examine a hypothetical situation in which we had a structure(which represented parameters) with two fields, and then need to add a third field to our structure. If using structs directly we'd need to rewrite every piece of code that touches the struct, by using eina value, and thus having the compiler not even know the struct, we can reduce the amount of changes needed and retain interoperability between the old and new format.

Our example will start with a function that creates descriptions of both of our structs for eina value usage. The first step is to create a struct and describe its members:

//Compile with:
//gcc eina_value_02.c -o eina_value_02 `pkg-config --cflags --libs eina`
#include <Eina.h>
static Eina_Value_Struct_Desc *V1_DESC = NULL;
static Eina_Value_Struct_Desc *V2_DESC = NULL;
void value_init(void)
{
typedef struct _My_Struct_V1 {
int param1;
char param2;
} My_Struct_V1;
static Eina_Value_Struct_Member v1_members[] = {
// no eina_value_type as they are not constant initializers, see below.
EINA_VALUE_STRUCT_MEMBER(NULL, My_Struct_V1, param1),
EINA_VALUE_STRUCT_MEMBER(NULL, My_Struct_V1, param2)
};
v1_members[0].type = EINA_VALUE_TYPE_INT;
v1_members[1].type = EINA_VALUE_TYPE_CHAR;
Eina.h
Eina Utility library.
EINA_VALUE_TYPE_CHAR
EINA_API const Eina_Value_Type * EINA_VALUE_TYPE_CHAR
manages char type.
Definition eina_value.c:5600
EINA_VALUE_TYPE_INT
EINA_API const Eina_Value_Type * EINA_VALUE_TYPE_INT
manages int type.
Definition eina_value.c:5602
EINA_VALUE_STRUCT_MEMBER
#define EINA_VALUE_STRUCT_MEMBER(eina_value_type, type, member)
Helper to define Eina_Value_Struct_Member fields, uses offsetof() with type and member.
Definition eina_value.h:3160
Eina_Value_Struct_Desc
struct _Eina_Value_Struct_Desc Eina_Value_Struct_Desc
Describes the struct by listing its size, members and operations.
Definition eina_value.h:3041
Eina_Value_Struct_Member
struct _Eina_Value_Struct_Member Eina_Value_Struct_Member
Describes a single member of struct.
Definition eina_value.h:3032
_Eina_Value_Struct_Member::type
const Eina_Value_Type * type
how to use this member
Definition eina_value.h:3123

Note
  We can't pass the types of the members to EINA_VALUE_STRUCT_MEMBER macro because they are not constant initializers.

So far it should be pretty easy to understand, we said `My_Struct_V1` has two members, one of type `int` and another of type `char`. We now create the description of the actual struct, again nothing overly complex, we signal which version of EINA_VALUE_STRUCT we're using, we declare no special operations, our members and our size:

static Eina_Value_Struct_Desc v1_desc = {
EINA_VALUE_STRUCT_DESC_VERSION,
NULL, // no special operations
v1_members,
EINA_C_ARRAY_LENGTH(v1_members),
sizeof(My_Struct_V1)
};
V1_DESC = &v1_desc;
EINA_C_ARRAY_LENGTH
#define EINA_C_ARRAY_LENGTH(arr)
Macro to return the array length of a standard c array.
Definition eina_types.h:621
EINA_VALUE_STRUCT_DESC_VERSION
#define EINA_VALUE_STRUCT_DESC_VERSION
Current API version, used to validate _Eina_Value_Struct_Desc.
Definition eina_value.h:3131

We now repeat the process for the second version of our struct, the only difference is the addition of a third parameter of type `int` :

typedef struct _My_Struct_V2 {
int param1;
char param2;
int param3;
} My_Struct_V2;
static Eina_Value_Struct_Member v2_members[] = {
// no eina_value_type as they are not constant initializers, see below.
EINA_VALUE_STRUCT_MEMBER(NULL, My_Struct_V2, param1),
EINA_VALUE_STRUCT_MEMBER(NULL, My_Struct_V2, param2),
EINA_VALUE_STRUCT_MEMBER(NULL, My_Struct_V2, param3)
};
v2_members[0].type = EINA_VALUE_TYPE_INT;
v2_members[1].type = EINA_VALUE_TYPE_CHAR;
v2_members[2].type = EINA_VALUE_TYPE_INT;
static Eina_Value_Struct_Desc v2_desc = {
EINA_VALUE_STRUCT_DESC_VERSION,
NULL, // no special operations
v2_members,
EINA_C_ARRAY_LENGTH(v2_members),
sizeof(My_Struct_V2)
};
V2_DESC = &v2_desc;
}

We'll now look at a function that sets the values of our structs. For simplicity's sake we initialize it we random values, a real world case would read these values from a file, a database or even from the network. The fundamental detail here is that this function works for both V1 and V2 structs, this is because setting a parameter that a struct that doesn't have does nothing without throwing any errors:

void rand_init(Eina_Value *v)
{
if (v->type != EINA_VALUE_TYPE_STRUCT)
return;
eina_value_struct_set(v, "param1", rand());
eina_value_struct_set(v, "param2", rand() % 256);
eina_value_struct_set(v, "param3", rand());
}
Eina_Value
struct _Eina_Value Eina_Value
Store generic values.
Definition eina_value.h:307
EINA_VALUE_TYPE_STRUCT
EINA_API const Eina_Value_Type * EINA_VALUE_TYPE_STRUCT
manages struct type.
Definition eina_value.c:5614

Note
  While using eina_value_struct_set() with an in-existing parameter causes no error, it does return EINA_FALSE, to notify it was not possible to set the value. This could be used to determine that we're handling a V1 struct and take some action based on that.

The next thing is to do is see what a function that uses the values of the struct looks like. We'll again be very simplistic in our usage, we'll just print the values, but a real world case, might send these values to another process use them to open a network/database connection or anything else. Since all versions of the struct have `param1` and `param2` we'll unconditionally use them:

void my_struct_use(Eina_Value *params)
{
int p1, p3;
char p2;
eina_value_struct_get(params, "param1", &p1);
eina_value_struct_get(params, "param2", &p2);
printf("param1: %d\nparam2: %c\n", p1, p2);

The next step is to conditionally use `param3`, which can fortunately be done in the same step in which we get it's value:

if (eina_value_struct_get(params, "param3", &p3))
printf("param3: %d\n", p3);
}

There we've now got functions that can both populate and use values from both our structs, so now let's actually use them in our main function by creating a struct of each type, initializing them and them using them:

int main(int argc, char **argv)
{
(void)argc;
(void)argv;
Eina_Value *v1, *v2;
eina_init();
value_init();
v1 = eina_value_struct_new(V1_DESC);
v2 = eina_value_struct_new(V2_DESC);
rand_init(v1);
my_struct_use(v1);
rand_init(v2);
my_struct_use(v2);
eina_value_free(v1);
eina_value_free(v2);
eina_shutdown();
}
eina_shutdown
EINA_API int eina_shutdown(void)
Shuts down the Eina library.
Definition eina_main.c:379
eina_init
EINA_API int eina_init(void)
Initializes the Eina library.
Definition eina_main.c:291
eina_value_struct_new
EINA_API Eina_Value * eina_value_struct_new(const Eina_Value_Struct_Desc *desc)
Creates generic value storage of type struct.
Definition eina_value.c:5823
eina_value_free
EINA_API void eina_value_free(Eina_Value *value)
Frees value and its data.
Definition eina_value.c:5645

This concludes our example. For the full source code see eina_value_02.c.