forked from enlightenment/efl
Eina: EINA_MAGIC example and documentation.
SVN revision: 60535
This commit is contained in:
parent
c1f20d3e6b
commit
702eb939d9
|
@ -1115,7 +1115,7 @@ INCLUDE_FILE_PATTERNS =
|
|||
# undefined via #undef or recursively expanded use the := operator
|
||||
# instead of the = operator.
|
||||
|
||||
PREDEFINED = __UNUSED__= EINA_ARG_NONNULL()= EINA_MALLOC= EINA_WARN_UNUSED_RESULT= EAPI=
|
||||
PREDEFINED = EINA_MAGIC_DEBUG __UNUSED__= EINA_ARG_NONNULL()= EINA_MALLOC= EINA_WARN_UNUSED_RESULT= EAPI=
|
||||
|
||||
# If the MACRO_EXPANSION and EXPAND_ONLY_PREDEF tags are set to YES then
|
||||
# this tag can be used to specify a list of macro names that should be expanded.
|
||||
|
|
|
@ -0,0 +1,106 @@
|
|||
//Compile with:
|
||||
//gcc -g `pkg-config --cflags --libs eina` eina_magic_01.c -o eina_magic_01
|
||||
|
||||
#include <Eina.h>
|
||||
|
||||
#define BASETYPE_MAGIC 0x12345
|
||||
struct _person {
|
||||
EINA_MAGIC;
|
||||
char *name;
|
||||
};
|
||||
typedef struct _person person;
|
||||
|
||||
#define SUBTYPE_MAGIC 0x3333
|
||||
struct _pilot {
|
||||
person base;
|
||||
EINA_MAGIC;
|
||||
char *callsign;
|
||||
};
|
||||
typedef struct _pilot pilot;
|
||||
|
||||
person *
|
||||
person_new(const char *name)
|
||||
{
|
||||
person *ptr = malloc(sizeof(person));
|
||||
EINA_MAGIC_SET(ptr, BASETYPE_MAGIC);
|
||||
ptr->name = strdup(name);
|
||||
}
|
||||
|
||||
void
|
||||
person_free(person *ptr) {
|
||||
if (!EINA_MAGIC_CHECK(ptr, BASETYPE_MAGIC))
|
||||
{
|
||||
EINA_MAGIC_FAIL(ptr, BASETYPE_MAGIC);
|
||||
return;
|
||||
}
|
||||
EINA_MAGIC_SET(ptr, EINA_MAGIC_NONE);
|
||||
free(ptr->name);
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
pilot *
|
||||
pilot_new(const char *name, const char *callsign)
|
||||
{
|
||||
pilot *ptr = malloc(sizeof(pilot));
|
||||
EINA_MAGIC_SET(ptr, SUBTYPE_MAGIC);
|
||||
EINA_MAGIC_SET(&ptr->base, BASETYPE_MAGIC);
|
||||
ptr->base.name = strdup(name);
|
||||
ptr->callsign = strdup(callsign);
|
||||
}
|
||||
|
||||
void
|
||||
pilot_free(pilot *ptr) {
|
||||
if (!EINA_MAGIC_CHECK(ptr, SUBTYPE_MAGIC))
|
||||
{
|
||||
EINA_MAGIC_FAIL(ptr, SUBTYPE_MAGIC);
|
||||
return;
|
||||
}
|
||||
EINA_MAGIC_SET(ptr, EINA_MAGIC_NONE);
|
||||
EINA_MAGIC_SET(&ptr->base, EINA_MAGIC_NONE);
|
||||
free(ptr->base.name);
|
||||
free(ptr->callsign);
|
||||
free(ptr);
|
||||
}
|
||||
|
||||
void
|
||||
print_person(person *ptr)
|
||||
{
|
||||
if (!EINA_MAGIC_CHECK(ptr, BASETYPE_MAGIC)){
|
||||
EINA_MAGIC_FAIL(ptr, BASETYPE_MAGIC);
|
||||
return;
|
||||
}
|
||||
printf("name: %s\n", ptr->name);
|
||||
}
|
||||
|
||||
void
|
||||
print_pilot(pilot *ptr)
|
||||
{
|
||||
if (!EINA_MAGIC_CHECK(ptr, SUBTYPE_MAGIC)) {
|
||||
EINA_MAGIC_FAIL(ptr, SUBTYPE_MAGIC);
|
||||
return;
|
||||
}
|
||||
print_person(&ptr->base);
|
||||
printf("callsign: %s\n", ptr->callsign);
|
||||
}
|
||||
|
||||
int
|
||||
main(int argc, char **argv)
|
||||
{
|
||||
person *base;
|
||||
pilot *sub;
|
||||
|
||||
eina_init();
|
||||
eina_magic_string_set(BASETYPE_MAGIC, "person");
|
||||
eina_magic_string_static_set(SUBTYPE_MAGIC, "pilot");
|
||||
|
||||
base = person_new("Tyrol");
|
||||
sub = pilot_new("thrace", "starbuck");
|
||||
|
||||
print_person(base);
|
||||
print_person((person *)sub);
|
||||
|
||||
print_pilot(base); //BAD
|
||||
print_pilot(sub);
|
||||
|
||||
eina_shutdown();
|
||||
}
|
|
@ -22,27 +22,171 @@
|
|||
#include "eina_config.h"
|
||||
#include "eina_types.h"
|
||||
|
||||
/**
|
||||
* @page eina_magic_example_01_page
|
||||
* @dontinclude eina_magic_01.c
|
||||
*
|
||||
* Whenever using Eina we must include it:
|
||||
* @skipline #include
|
||||
*
|
||||
* For this example we are going to define two classes, person and pilot, and
|
||||
* since every pilot is a person we use inheritance. To be type safe we are
|
||||
* going to add EINA_MAGIC to our classes:
|
||||
* @until struct _pilot pilot
|
||||
* @note The values of BASETYPE_MAGIC and SUBTYPE_MAGIC have no meaning, the
|
||||
* only important thing about them is that they be unique.
|
||||
*
|
||||
* Here we have a function to create a perso given a name, nothing too fancy:
|
||||
* @until }
|
||||
*
|
||||
* And now the counterpart, a function the free a person.
|
||||
* @until {
|
||||
* Before we start releasing resources we check that the pointer we were given
|
||||
* actually points to a person, and if not we will print an error message and
|
||||
* quit:
|
||||
* @until }
|
||||
* @note EINA_MAGIC_FAIL is a macro that make's it easy to print an appropriate
|
||||
* (and consistent) error message.
|
||||
* Now knowing that ptr is indeed of type person we prooced to set EINA_MAGIC to
|
||||
* EINA_MAGIC_NONE and free alocated memory:
|
||||
* @until }
|
||||
* @note Setting EINA_MAGIC to EINA_MAGIC_NONE is important to prevent the
|
||||
* struct from being used after freed.
|
||||
*
|
||||
* Now we have our function to create a pilot, this one is a little more complex
|
||||
* because we need to set EINA_MAGIC for the pilot and pilot->base, this is very
|
||||
* important so that checking the EINA_MAGIC of (person*)my_pilot will work:
|
||||
* @until }
|
||||
*
|
||||
* The function to free a pilot is not too different from the one that frees a
|
||||
* person:
|
||||
* @until }
|
||||
* @until }
|
||||
*
|
||||
* We also create functions to print a person or a pilot that check the type of
|
||||
* the pointers they receive:
|
||||
* @until }
|
||||
* @until }
|
||||
*
|
||||
* And on to our main function where we declare some variables and initialize
|
||||
* Eina:
|
||||
* @until eina_init
|
||||
*
|
||||
* For Eina to be able to provide more informative error messages we are going
|
||||
* to give names to our EINA_MAGIC types:
|
||||
* @until string_set
|
||||
*
|
||||
* Since our types won't live longer than the scope of the current function we
|
||||
* can set the name without eina making a copy of the string:
|
||||
* @until static_set
|
||||
*
|
||||
* Now we create a person, a pilot and print both as persons:
|
||||
* @until person *
|
||||
*
|
||||
* Now we try to print both as pilots, which will obvisouly not work since base
|
||||
* is not a pilot:
|
||||
* @until pilot(sub
|
||||
*
|
||||
* That's all folks:
|
||||
* @until }
|
||||
*
|
||||
* See full source @ref eina_magic_example_01_c "here".
|
||||
*/
|
||||
/**
|
||||
* @page eina_magic_example_01_c Eina_Magic
|
||||
* @include eina_magic_01.c
|
||||
* @example eina_magic_01.c
|
||||
*/
|
||||
/**
|
||||
* @addtogroup Eina_Tools_Group Tools
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @defgroup Eina_Magic_Group Magic
|
||||
*
|
||||
* @brief Eina_Magic provides run-time type-checking.
|
||||
*
|
||||
* 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 code we often need to deal with casts, either explicit or
|
||||
* implicit by means of @c void*. We also need to resort to casts when doing
|
||||
* inheritance in C.
|
||||
*
|
||||
* Eina_Magic give us a way to do casts and still be certain of the type we are
|
||||
* opearting on.
|
||||
*
|
||||
* @note It should be noted that it is considered good practice to @b disable
|
||||
* Eina_Magic for production code. The reasoning is that any Eina_Magic errors
|
||||
* should have been caught during testing and therefore there is no reason to
|
||||
* incur the performance downside of Eina_Magic.
|
||||
*
|
||||
* An @ref eina_magic_example_01_page "example" should elucidate matters.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* An abstract type for a magic number.
|
||||
*/
|
||||
typedef unsigned int Eina_Magic;
|
||||
|
||||
/**
|
||||
* @typedef Eina_Magic
|
||||
* An abstract type for a magic number.
|
||||
* @brief Return the string associated to the given magic identifier.
|
||||
*
|
||||
* @param magic The magic identifier.
|
||||
* @return The string associated to the identifier.
|
||||
*
|
||||
* This function returns the string associated to @p magic. Even if none are
|
||||
* found this function still returns non @c NULL, in this case an identifier
|
||||
* such as "(none)", "(undefined)" or "(unknown)".
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @warning The returned value must not be freed.
|
||||
*/
|
||||
EAPI const char *eina_magic_string_get(Eina_Magic magic) EINA_WARN_UNUSED_RESULT;
|
||||
/**
|
||||
* @brief Set the string associated to the given magic identifier.
|
||||
*
|
||||
* @param magic The magic identifier.
|
||||
* @param magic_name The string associated to the identifier, must not
|
||||
* be @c NULL.
|
||||
*
|
||||
* @return #EINA_TRUE on success, #EINA_FALSE on failure.
|
||||
*
|
||||
* This function sets the string @p magic_name to @p magic. It is not
|
||||
* checked if number or string are already set, in which case you will end with
|
||||
* duplicates. Internally, eina will make a copy of @p magic_name.
|
||||
*
|
||||
* @see eina_magic_string_static_set()
|
||||
*/
|
||||
EAPI Eina_Bool eina_magic_string_set(Eina_Magic magic,
|
||||
const char *magic_name) EINA_ARG_NONNULL(2);
|
||||
|
||||
/**
|
||||
* @brief Set the string associated to the given magic identifier.
|
||||
*
|
||||
* @param magic The magic identifier.
|
||||
* @param magic_name The string associated to the identifier, must not be
|
||||
* @c NULL.
|
||||
*
|
||||
* @return #EINA_TRUE on success, #EINA_FALSE on failure.
|
||||
*
|
||||
* This function sets the string @p magic_name to @p magic. It is not checked if
|
||||
* number or string are already set, in which case you might end with
|
||||
* duplicates. Eina will @b not make a copy of @p magic_name, this means that
|
||||
* @p magic_name has to be a valid pointer for as long as @p magic is used.
|
||||
*
|
||||
* @see eina_magic_string_set()
|
||||
*/
|
||||
EAPI Eina_Bool eina_magic_string_static_set(Eina_Magic magic,
|
||||
const char *magic_name) EINA_ARG_NONNULL(2);
|
||||
|
||||
|
@ -118,6 +262,32 @@ EAPI Eina_Bool eina_magic_string_static_set(Eina_Magic magic,
|
|||
__FUNCTION__, \
|
||||
__LINE__);
|
||||
|
||||
/**
|
||||
* @brief Display a message or abort if a magic check failed.
|
||||
*
|
||||
* @param d The checked data pointer.
|
||||
* @param m The magic identifer to check.
|
||||
* @param req_m The requested magic identifier to check.
|
||||
* @param file The file in which the magic check failed.
|
||||
* @param fnc The function in which the magic check failed.
|
||||
* @param line The line at which the magic check failed.
|
||||
*
|
||||
* @warning You should @b strongly consider using @ref EINA_MAGIC_FAIL(d, m)
|
||||
* instead.
|
||||
*
|
||||
* This function displays an error message if a magic check has
|
||||
* failed, using the following logic in the following order:
|
||||
* @li If @p d is @c NULL, a message warns about a @c NULL pointer.
|
||||
* @li Otherwise, if @p m is equal to #EINA_MAGIC_NONE, a message
|
||||
* warns about a handle that was already freed.
|
||||
* @li Otherwise, if @p m is equal to @p req_m, a message warns about
|
||||
* a handle that is of wrong type.
|
||||
* @li 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.
|
||||
*/
|
||||
EAPI void eina_magic_fail(void *d, Eina_Magic m, Eina_Magic req_m,
|
||||
const char *file, const char *fnc,
|
||||
int line) EINA_ARG_NONNULL(4, 5);
|
||||
|
|
|
@ -191,120 +191,6 @@ eina_magic_string_shutdown(void)
|
|||
/*============================================================================*
|
||||
* API *
|
||||
*============================================================================*/
|
||||
|
||||
/**
|
||||
* @addtogroup Eina_Magic_Group Magic
|
||||
*
|
||||
* @brief 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 @c void*. We also need to resort
|
||||
* to casts when doing inheritance in C, as seen in the example below:
|
||||
*
|
||||
* @code
|
||||
* struct base {
|
||||
* int id;
|
||||
* char *name;
|
||||
* };
|
||||
* int base_id_get(struct base *ptr) {
|
||||
* return ptr->id;
|
||||
* }
|
||||
*
|
||||
* struct subtype {
|
||||
* struct base base;
|
||||
* time_t date;
|
||||
* };
|
||||
* @endcode
|
||||
*
|
||||
* It is perfectly valid to use @c {struct subtype} blobs for functions
|
||||
* that expect @c {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 @c {&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.
|
||||
*
|
||||
* @code
|
||||
* #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");
|
||||
* }
|
||||
* @endcode
|
||||
*
|
||||
* 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.
|
||||
*
|
||||
* @{
|
||||
*/
|
||||
|
||||
/**
|
||||
* @brief Return the string associated to the given magic identifier.
|
||||
*
|
||||
* @param magic The magic identifier.
|
||||
* @return The string associated to the identifier.
|
||||
*
|
||||
* This function returns the string associated to @p magic. If none
|
||||
* are found, the this function still returns non @c 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.
|
||||
*/
|
||||
EAPI const char *
|
||||
eina_magic_string_get(Eina_Magic magic)
|
||||
{
|
||||
|
@ -329,21 +215,6 @@ eina_magic_string_get(Eina_Magic magic)
|
|||
return "(unknown)";
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the string associated to the given magic identifier.
|
||||
*
|
||||
* @param magic The magic identifier.
|
||||
* @param magic_name The string associated to the identifier, must not
|
||||
* be @c NULL.
|
||||
*
|
||||
* @return #EINA_TRUE on success, #EINA_FALSE on failure.
|
||||
*
|
||||
* This function sets the string @p magic_name to @p magic. It is not
|
||||
* checked if number or string are already set, then you might end
|
||||
* with duplicates in that case.
|
||||
*
|
||||
* @see eina_magic_string_static_set()
|
||||
*/
|
||||
EAPI Eina_Bool
|
||||
eina_magic_string_set(Eina_Magic magic, const char *magic_name)
|
||||
{
|
||||
|
@ -369,22 +240,6 @@ eina_magic_string_set(Eina_Magic magic, const char *magic_name)
|
|||
return EINA_TRUE;
|
||||
}
|
||||
|
||||
/**
|
||||
* @brief Set the string associated to the given magic identifier.
|
||||
*
|
||||
* @param magic The magic identifier.
|
||||
* @param magic_name The string associated to the identifier, must not be
|
||||
* @c NULL, it will not be duplcated, just referenced thus it must
|
||||
* be live during magic number usage.
|
||||
*
|
||||
* @return #EINA_TRUE on success, #EINA_FALSE on failure.
|
||||
*
|
||||
* This function sets the string @p magic_name to @p magic. It is not
|
||||
* checked if number or string are already set, then you might end
|
||||
* with duplicates in that case.
|
||||
*
|
||||
* @see eina_magic_string_set()
|
||||
*/
|
||||
EAPI Eina_Bool
|
||||
eina_magic_string_static_set(Eina_Magic magic, const char *magic_name)
|
||||
{
|
||||
|
@ -408,29 +263,6 @@ eina_magic_string_static_set(Eina_Magic magic, const char *magic_name)
|
|||
# undef eina_magic_fail
|
||||
#endif
|
||||
|
||||
/**
|
||||
* @brief Display a message or abort is a magic check failed.
|
||||
*
|
||||
* @param d The checked data pointer.
|
||||
* @param m The magic identifer to check.
|
||||
* @param req_m The requested magic identifier to check.
|
||||
* @param file The file in which the magic check failed.
|
||||
* @param fnc The function in which the magic check failed.
|
||||
* @param 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:
|
||||
* @li If @p d is @c NULL, a message warns about a @c NULL pointer.
|
||||
* @li Otherwise, if @p m is equal to #EINA_MAGIC_NONE, a message
|
||||
* warns about a handle that was already freed.
|
||||
* @li Otherwise, if @p m is equal to @p req_m, a message warns about
|
||||
* a handle that is of wrong type.
|
||||
* @li 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.
|
||||
*/
|
||||
EAPI void
|
||||
eina_magic_fail(void *d,
|
||||
Eina_Magic m,
|
||||
|
|
Loading…
Reference in New Issue