Eina value custom type example

For this example we'll be creating our own custom type of eina value. Eina value can already store struct timeval(man gettimeofday for more information) but it has no type to store struct timezone, so that's what this example will do.

Note
Struct timezone is actually obsolete, so using it in real world programs is probably not a good idea, but this is an example so, bear with us.

To create our own custom eina value type we need to define functions to do the following operations on it:

  • Setup
  • Flush
  • Copy
  • Compare
  • Set
  • Get
  • Conversion

Most of this functions are very simple, so let's look at them, starting with setup which only clear the memory so that we can be certain we won't be using stale data:

//Compile with:
//gcc eina_value_03.c -o eina_value_03 `pkg-config --cflags --libs eina`
#include <Eina.h>
#include <sys/time.h>
static Eina_Bool
_tz_setup(const Eina_Value_Type *type, void *mem)
{
memset(mem, 0, type->value_size);
return EINA_TRUE;
}

Now the flush function, which is even simpler, it does nothing, that's because there is nothing we need to do, all the necessary steps are taken by eina value itself:

static Eina_Bool
_tz_flush(const Eina_Value_Type *type EINA_UNUSED, void *mem EINA_UNUSED)
{
return EINA_TRUE;
}

Our next function, copy, is a bit more interesting, but not much, it just casts our void pointers to struct timezone pointers and does the copy:

static Eina_Bool
_tz_copy(const Eina_Value_Type *type EINA_UNUSED, const void *src, void * dst)
{
struct timezone *tzsrc = (struct timezone *)src;
struct timezone *tzdst = dst;
*tzdst = *tzsrc;
return EINA_TRUE;
}
Note
By now you might be wondering why our functions receive void pointers instead of pointers to struct timezone, and this is a good point. The reason for this is that eina value doesn't know anything about our type so it must use a generic void pointer, casting that pointer into a proper value is the job of the implementer of the new type.

Next we have the comparison function, which compares the tz_minuteswest field of struct timezone, we don't compare tz_dsttime because that field is not used in linux:

static int
_tz_compare(const Eina_Value_Type *type EINA_UNUSED, const void *a, const void *b)
{
struct timezone tza = *(struct timezone*)a;
struct timezone tzb = *(struct timezone*)b;
if (tza.tz_minuteswest < tzb.tz_minuteswest)
return -1;
else if (tza.tz_minuteswest > tzb.tz_minuteswest)
return 1;
return 0;
}

Next we have setting, this however requires not one but rather two functions, the reason for this is because to be able to receive arguments of any type eina value uses variadic functions, so we need a function to get the argument from a va_list and another to actually to the setting.

Lets first look at the pset function which sets the received value to a pointer:

static Eina_Bool
_tz_pset(const Eina_Value_Type *type EINA_UNUSED, void *mem, const void *ptr)
{
*(struct timezone*)mem = *(struct timezone*)ptr;
return EINA_TRUE;
}

Next we have the vset function which get the argument from the va_list and passes it to the pset function:

static Eina_Bool
_tz_vset(const Eina_Value_Type *type, void *mem, va_list args)
{
const struct timezone tz = va_arg(args, struct timezone);
return _tz_pset(type, mem, &tz);
}

And now the function to get the value, a very simple copying of the value to the given pointer:

static Eina_Bool
_tz_pget(const Eina_Value_Type *type, const void *mem, void *ptr)
{
memcpy(ptr, mem, type->value_size);
return EINA_TRUE;
}

And finally our conversion function, this is our longest and most interesting one. For numeric type we simply assign the value of tz_minuteswest to the new type and call a set function using it:

static Eina_Bool
_tz_convert_to(const Eina_Value_Type *type EINA_UNUSED, const Eina_Value_Type *convert, const void *type_mem, void *convert_mem)
{
struct timezone v = *(struct timezone*)type_mem;
if (convert == EINA_VALUE_TYPE_UCHAR)
{
unsigned char other_mem = v.tz_minuteswest;
return eina_value_type_pset(convert, convert_mem, &other_mem);
}
else if (convert == EINA_VALUE_TYPE_USHORT)
{
unsigned short other_mem = v.tz_minuteswest;
return eina_value_type_pset(convert, convert_mem, &other_mem);
}
else if (convert == EINA_VALUE_TYPE_UINT)
{
unsigned int other_mem = v.tz_minuteswest;
return eina_value_type_pset(convert, convert_mem, &other_mem);
}
else if ((convert == EINA_VALUE_TYPE_ULONG) || (convert == EINA_VALUE_TYPE_TIMESTAMP))
{
unsigned long other_mem = v.tz_minuteswest;
return eina_value_type_pset(convert, convert_mem, &other_mem);
}
else if (convert == EINA_VALUE_TYPE_UINT64)
{
uint64_t other_mem = v.tz_minuteswest;
return eina_value_type_pset(convert, convert_mem, &other_mem);
}
else if (convert == EINA_VALUE_TYPE_CHAR)
{
char other_mem = v.tz_minuteswest;
return eina_value_type_pset(convert, convert_mem, &other_mem);
}
else if (convert == EINA_VALUE_TYPE_SHORT)
{
short other_mem = v.tz_minuteswest;
return eina_value_type_pset(convert, convert_mem, &other_mem);
}
else if (convert == EINA_VALUE_TYPE_INT)
{
int other_mem = v.tz_minuteswest;
return eina_value_type_pset(convert, convert_mem, &other_mem);
}
else if (convert == EINA_VALUE_TYPE_LONG)
{
long other_mem = v.tz_minuteswest;
return eina_value_type_pset(convert, convert_mem, &other_mem);
}
else if (convert == EINA_VALUE_TYPE_INT64)
{
int64_t other_mem = v.tz_minuteswest;
return eina_value_type_pset(convert, convert_mem, &other_mem);
}
else if (convert == EINA_VALUE_TYPE_FLOAT)
return eina_value_type_pset(convert, convert_mem, &v.tz_minuteswest);
else if (convert == EINA_VALUE_TYPE_DOUBLE)
return eina_value_type_pset(convert, convert_mem, &v.tz_minuteswest);
Note
It would be a good idea to add checks for over and underflow for these types and return EINA_FALSE in those cases, we omit this here for brevity.

For string types we use snprintf() to format our tz_minuteswest field and put it in a string(again tz_dsttime is ignored because it's not used):

else if (convert == EINA_VALUE_TYPE_STRINGSHARE ||
{
const char *other_mem;
char buf[64];
snprintf(buf, sizeof(buf), "%d", v.tz_minuteswest);
other_mem = buf; /* required due &buf == buf */
return eina_value_type_pset(convert, convert_mem, &other_mem);
}

Finally we handle any other types by returning an error in that case:

return EINA_FALSE;
}

Now that we have all the functions, we can populate an Eina_Value_Type to later use it with eina_value_setup():

static Eina_Value_Type TZ_TYPE = {
sizeof(struct timezone),
"struct timezone",
_tz_setup,
_tz_flush,
_tz_copy,
_tz_compare,
_tz_convert_to,
NULL, //No convert from
_tz_vset,
_tz_pset,
_tz_pget
};

We can now finally use our new TZ_TYPE with eina value, so lets conclude our example by practicing that by setting its value and printing it:

int main(int argc EINA_UNUSED, char **argv EINA_UNUSED)
{
Eina_Value vtv, vtz;
struct timeval tv;
struct timezone tz;
char *s;
eina_value_setup(&vtz, &TZ_TYPE);
gettimeofday(&tv, &tz);
eina_value_set(&vtv, tv);
eina_value_set(&vtz, tz);
printf("time: %s\n", s);
free(s);
printf("timezone: %s\n", s);
free(s);
}

For the full source code see eina_value_03.c.

EINA_VALUE_TYPE_VERSION
#define EINA_VALUE_TYPE_VERSION
Definition: eina_value.h:3669
EINA_VALUE_TYPE_USHORT
const EAPI Eina_Value_Type * EINA_VALUE_TYPE_USHORT
Definition: eina_value.c:5592
eina_value_setup
static Eina_Bool eina_value_setup(Eina_Value *value, const Eina_Value_Type *type)
Initializes generic value storage.
EINA_VALUE_TYPE_UINT64
const EAPI Eina_Value_Type * EINA_VALUE_TYPE_UINT64
Definition: eina_value.c:5596
_Eina_Value_Type
Definition: eina_value.h:3677
EINA_VALUE_TYPE_INT
const EAPI Eina_Value_Type * EINA_VALUE_TYPE_INT
Definition: eina_value.c:5599
EINA_VALUE_TYPE_TIMEVAL
const EAPI Eina_Value_Type * EINA_VALUE_TYPE_TIMEVAL
Definition: eina_value.c:5609
EINA_UNUSED
#define EINA_UNUSED
Definition: eina_types.h:321
EINA_VALUE_TYPE_DOUBLE
const EAPI Eina_Value_Type * EINA_VALUE_TYPE_DOUBLE
Definition: eina_value.c:5603
Eina.h
Eina Utility library.
EINA_VALUE_TYPE_UINT
const EAPI Eina_Value_Type * EINA_VALUE_TYPE_UINT
Definition: eina_value.c:5593
EINA_FALSE
#define EINA_FALSE
Definition: eina_types.h:502
eina_init
int eina_init(void)
Initializes the Eina library.
Definition: eina_main.c:277
EINA_VALUE_TYPE_SHORT
const EAPI Eina_Value_Type * EINA_VALUE_TYPE_SHORT
Definition: eina_value.c:5598
EINA_VALUE_TYPE_UCHAR
const EAPI Eina_Value_Type * EINA_VALUE_TYPE_UCHAR
Definition: eina_value.c:5590
eina_value_set
static Eina_Bool eina_value_set(Eina_Value *value,...)
Sets the generic value.
EINA_VALUE_TYPE_FLOAT
const EAPI Eina_Value_Type * EINA_VALUE_TYPE_FLOAT
Definition: eina_value.c:5602
EINA_VALUE_TYPE_TIMESTAMP
const EAPI Eina_Value_Type * EINA_VALUE_TYPE_TIMESTAMP
Definition: eina_value.c:5595
EINA_VALUE_TYPE_INT64
const EAPI Eina_Value_Type * EINA_VALUE_TYPE_INT64
Definition: eina_value.c:5601
eina_value_free
EAPI void eina_value_free(Eina_Value *value)
Frees value and its data.
Definition: eina_value.c:5649
eina_shutdown
int eina_shutdown(void)
Shuts down the Eina library.
Definition: eina_main.c:348
EINA_VALUE_TYPE_STRING
const EAPI Eina_Value_Type * EINA_VALUE_TYPE_STRING
Definition: eina_value.c:5605
EINA_TRUE
#define EINA_TRUE
Definition: eina_types.h:508
Eina_Bool
unsigned char Eina_Bool
Definition: eina_types.h:496
EINA_VALUE_TYPE_CHAR
const EAPI Eina_Value_Type * EINA_VALUE_TYPE_CHAR
Definition: eina_value.c:5597
eina_value_to_string
EAPI char * eina_value_to_string(const Eina_Value *value)
Converts value to string.
Definition: eina_value.c:5723
eina_value_type_pset
static Eina_Bool eina_value_type_pset(const Eina_Value_Type *type, void *mem, const void *ptr)
Sets memory using type descriptor and pointer.
_Eina_Value
Definition: eina_value.h:661
EINA_VALUE_TYPE_LONG
const EAPI Eina_Value_Type * EINA_VALUE_TYPE_LONG
Definition: eina_value.c:5600
EINA_VALUE_TYPE_ULONG
const EAPI Eina_Value_Type * EINA_VALUE_TYPE_ULONG
Definition: eina_value.c:5594
EINA_VALUE_TYPE_STRINGSHARE
const EAPI Eina_Value_Type * EINA_VALUE_TYPE_STRINGSHARE
Definition: eina_value.c:5604