From: gastal Date: Mon, 20 Jun 2011 21:15:17 +0000 (+0000) Subject: Eina: EINA_MAGIC example and documentation. X-Git-Tag: 2.0_alpha~69^2~41 X-Git-Url: http://review.tizen.org/git/?a=commitdiff_plain;h=6a6b81d0f04b6e2460e23c5c3431174fff15166d;p=framework%2Fuifw%2Feina.git Eina: EINA_MAGIC example and documentation. git-svn-id: svn+ssh://svn.enlightenment.org/var/svn/e/trunk/eina@60535 7cbeb6ba-43b4-40fd-8cce-4c39aea84d33 --- diff --git a/doc/Doxyfile.in b/doc/Doxyfile.in index 21f933d..5843161 100644 --- a/doc/Doxyfile.in +++ b/doc/Doxyfile.in @@ -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. diff --git a/src/examples/eina_magic_01.c b/src/examples/eina_magic_01.c new file mode 100644 index 0000000..c3fb388 --- /dev/null +++ b/src/examples/eina_magic_01.c @@ -0,0 +1,106 @@ +//Compile with: +//gcc -g `pkg-config --cflags --libs eina` eina_magic_01.c -o eina_magic_01 + +#include + +#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(); +} \ No newline at end of file diff --git a/src/include/eina_magic.h b/src/include/eina_magic.h index 4b3fee2..44cd4e9 100644 --- a/src/include/eina_magic.h +++ b/src/include/eina_magic.h @@ -23,26 +23,170 @@ #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); diff --git a/src/lib/eina_magic.c b/src/lib/eina_magic.c index 51f49e6..1adde92 100644 --- a/src/lib/eina_magic.c +++ b/src/lib/eina_magic.c @@ -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,