Magic
[Tools]

These functions provide runtime type-checking (magic checks) management for projects. More...

Defines

#define EINA_MAGIC_NONE   0x1234fedc
 Random value for specifying that a structure using the magic feature has already been freed.

Typedefs

typedef unsigned int Eina_Magic
 An abstract type for a magic number.

Functions

const char * eina_magic_string_get (Eina_Magic magic)
 Return the string associated to the given magic identifier.
Eina_Bool eina_magic_string_set (Eina_Magic magic, const char *magic_name)
 Set the string associated to the given magic identifier.
Eina_Bool eina_magic_string_static_set (Eina_Magic magic, const char *magic_name)
 Set the string associated to the given magic identifier.
void eina_magic_fail (void *d, Eina_Magic m, Eina_Magic req_m, const char *file, const char *fnc, int line)
 Display a message or abort is a magic check failed.

Detailed Description

These functions provide runtime type-checking (magic checks) management for projects.

C is a weak statically typed language, in other words, it will just check for types during compile time and any cast will make the compiler believe the type is correct.

In real world projects we often need to deal with casts, either explicit or implicit by means of void*. We also need to resort to casts when doing inheritance in C, as seen in the example below:

 struct base {
    int id;
    char *name;
 };
 int base_id_get(struct base *ptr) {
    return ptr->id;
 }

 struct subtype {
    struct base base;
    time_t date;
 };

It is perfectly valid to use {struct subtype} blobs for functions that expect {struct base}, since the fields will have the same offset (as base member is the first, at offset 0). We could give the functions the {&subtype->base} and avoid the cast, but often we just cast.

In any case, we might be safe and check if the given pointer is actually of the expected type. We can do so by using eina_magic, that is nothing more than attaching an unique type identifier to the members and check for it elsewhere.

 #define BASE_MAGIC 0x12345
 #define SUBTYPE_MAGIC 0x3333
 struct base {
    int id;
    char *name;
    EINA_MAGIC;
 };
 int base_id_get(struct base *ptr) {
    if (!EINA_MAGIC_CHECK(ptr, BASE_MAGIC)) {
       EINA_MAGIC_FAIL(ptr, BASE_MAGIC);
       return -1;
    }
    return ptr->id;
 }
 void base_free(struct base *ptr) {
    if (!EINA_MAGIC_CHECK(ptr, BASE_MAGIC)) {
       EINA_MAGIC_FAIL(ptr, BASE_MAGIC);
       return;
    }
    EINA_MAGIC_SET(ptr, EINA_MAGIC_NONE);
    free(ptr->name);
    free(ptr);
 }
 struct base *base_new(int id, const char *name) {
    struct base *ptr = malloc(sizeof(struct base));
    EINA_MAGIC_SET(ptr, BASE_MAGIC);
    ptr->id = id;
    ptr->name = strdup(name);
 }

 struct subtype {
    struct base base;
    EINA_MAGIC;
    time_t date;
 };

 int my_init(void) {
    eina_init();
    eina_magic_string_set(BASE_MAGIC, "base type");
    eina_magic_string_set(SUBTYPE_MAGIC, "subtype");
 }

This code also shows that it is a good practice to set magic to EINA_MAGIC_NONE before freeing pointer. Sometimes the pointers are in pages that are still live in memory, so kernel will not send SEGV signal to the process and it may go unnoticed that you're using already freed pointers. By setting them to EINA_MAGIC_NONE you avoid using the bogus pointer any further and gets a nice error message.


Define Documentation

#define EINA_MAGIC_NONE   0x1234fedc

Random value for specifying that a structure using the magic feature has already been freed.

It is used by eina_magic_fail().

If the magic feature of Eina is disabled, EINA_MAGIC_NONE is just 0.


Function Documentation

const char * eina_magic_string_get ( Eina_Magic  magic  ) 

Return the string associated to the given magic identifier.

Parameters:
magic The magic identifier.
Returns:
The string associated to the identifier.

This function returns the string associated to magic. If none are found, the this function still returns non NULL, in this case an identifier such as "(none)", "(undefined)" or "(unknown)". The returned value must not be freed.

The following identifiers may be returned whenever magic is invalid, with their meanings:

  • (none): no magic was registered exists at all.
  • (undefined): magic was registered and found, but no string associated.
  • (unknown): magic was not found in the registry.
Eina_Bool eina_magic_string_set ( Eina_Magic  magic,
const char *  magic_name 
)

Set the string associated to the given magic identifier.

Parameters:
magic The magic identifier.
magic_name The string associated to the identifier, must not be NULL.
Returns:
EINA_TRUE on success, EINA_FALSE on failure.

This function sets the string magic_name to magic. It is not checked if number or string are already set, then you might end with duplicates in that case.

See also:
eina_magic_string_static_set()
Eina_Bool eina_magic_string_static_set ( Eina_Magic  magic,
const char *  magic_name 
)

Set the string associated to the given magic identifier.

Parameters:
magic The magic identifier.
magic_name The string associated to the identifier, must not be NULL, it will not be duplcated, just referenced thus it must be live during magic number usage.
Returns:
EINA_TRUE on success, EINA_FALSE on failure.

This function sets the string magic_name to magic. It is not checked if number or string are already set, then you might end with duplicates in that case.

See also:
eina_magic_string_set()
void eina_magic_fail ( void *  d,
Eina_Magic  m,
Eina_Magic  req_m,
const char *  file,
const char *  fnc,
int  line 
)

Display a message or abort is a magic check failed.

Parameters:
d The checked data pointer.
m The magic identifer to check.
req_m The requested magic identifier to check.
file The file in which the magic check failed.
fnc The function in which the magic check failed.
line The line at which the magic check failed.

This function displays an error message if a magic check has failed, using the following logic in the following order:

  • If d is NULL, a message warns about a NULL pointer.
  • Otherwise, if m is equal to EINA_MAGIC_NONE, a message warns about a handle that was already freed.
  • Otherwise, if m is equal to req_m, a message warns about a handle that is of wrong type.
  • Otherwise, a message warns you about ab-using that function...

If the environment variable EINA_LOG_ABORT is set, abort() is called and the program stops. It is useful for debugging programs with gdb.