Compare commits

...

4 Commits

Author SHA1 Message Date
Xavier Del Campo Romero e0eaf50fdc Avoid exceeding 80-column per line 2020-09-03 15:51:00 +02:00
Xavier Del Campo Romero 4c7bfbf87f Avoid potential undesirable effects caused by macros
The macros provided by this library would expand to an unguarded
conditional that could have potential unwanted consequences. Consider
the following example:

    dynstr_append_or_ret_null(&d, "example");
    else
    {
        /* This is unexpectedly working. */
    }

The example above would expand to:

    if (dynstr_append(__VA_ARGS__) != DYNSTR_OK) return false;
    else
    {
        /* This is unexpectedly working. */
    }

Which is valid C yet allows possibly unexpected behaviour. The solution
is then to enclose these conditions into a `do while (0)` statement.
2020-09-03 15:42:56 +02:00
Xavier Del Campo Romero 9b85c4beaa Rephrase documentation to avoid confusion 2020-09-03 15:42:52 +02:00
Xavier Del Campo Romero 134ebfa751 Call dynstr_init on dynstr_free 2020-09-03 14:43:02 +02:00
2 changed files with 30 additions and 14 deletions

View File

@ -141,6 +141,6 @@ void dynstr_free(struct dynstr *const d)
if (d->str)
{
free(d->str);
memset(d, 0, sizeof *d);
dynstr_init(d);
}
}

View File

@ -30,27 +30,35 @@
/**
* Convenience macro that calls dynstr_append and returns NULL if failed.
*/
#define dynstr_append_or_ret_null(...) if (dynstr_append(__VA_ARGS__) != DYNSTR_OK) return NULL
#define dynstr_append_or_ret_null(...) \
do {if (dynstr_append(__VA_ARGS__) != DYNSTR_OK) return NULL;} while (0)
/**
* Convenience macro that calls dynstr_append and returns false if failed.
*/
#define dynstr_append_or_ret_false(...) if (dynstr_append(__VA_ARGS__) != DYNSTR_OK) return false
#define dynstr_append_or_ret_false(...) \
do {if (dynstr_append(__VA_ARGS__) != DYNSTR_OK) return false;} while (0)
/**
* Convenience macro that calls dynstr_append and returns its error code if failed.
*/
#define dynstr_append_or_ret(...) {const enum dynstr_err err = dynstr_append(__VA_ARGS__); if (err != DYNSTR_OK) return err;}
#define dynstr_append_or_ret(...) \
{ \
const enum dynstr_err err = dynstr_append(__VA_ARGS__); \
if (err != DYNSTR_OK) return err; \
}
/**
* Convenience macro that calls dynstr_append and returns zero if failed.
*/
#define dynstr_append_or_ret_zero(...) if (dynstr_append(__VA_ARGS__) != DYNSTR_OK) return 0
#define dynstr_append_or_ret_zero(...) \
do {if (dynstr_append(__VA_ARGS__) != DYNSTR_OK) return 0;} while (0)
/**
* Convenience macro that calls dynstr_append and returns one if failed.
*/
#define dynstr_append_or_ret_nonzero(...) if (dynstr_append(__VA_ARGS__) != DYNSTR_OK) return 1
#define dynstr_append_or_ret_nonzero(...) \
do {if (dynstr_append(__VA_ARGS__) != DYNSTR_OK) return 1;} while (0)
/**
* Dynamic string type used for this library.
@ -65,8 +73,11 @@ struct dynstr
/**
* List of errors that this library might return.
* @note If using GNU C extensions, this errors can be translated into other
* types by using the convenience macros above.
* @note Following the tradition, success code is guaranteed to always be
* zero.
* @attention Error codes are guaranteed to be non-zero, but their ordering
* might change, so please use descriptive error names instead of their integer
* equivalents.
*/
enum dynstr_err
{
@ -77,8 +88,10 @@ enum dynstr_err
};
/**
* Reportedly, it initializes an empty string with zero length.
* @attention Always call this function when creating a dynstr instance.
* This function initializes string pointer to NULL and sets zero length.
* @attention Always call this function before using an instance.
* For future compatibility, please avoid initializing instances by calling
* @c memset or similar and use this function instead.
* @param d Dynamic string to be initialized.
*/
void dynstr_init(struct dynstr *d);
@ -116,7 +129,8 @@ enum dynstr_err dynstr_prepend(struct dynstr *d, const char *format, ...);
* @return Returns one of the following error codes:
* # DYNSTR_OK if successful.
* # DYNSTR_ERR_ALLOC if no more memory is available.
* # DYNSTR_ERR_INIT if destination dynamic string was not initialized.
* # DYNSTR_ERR_INIT if destination dynamic string was either not
* initialized or already contains data.
* # DYNSTR_ERR_SRC if source dynamic string has no length or data.
* @note This function has the same effect as calling:
* @code
@ -131,9 +145,11 @@ enum dynstr_err dynstr_dup(struct dynstr *dst, const struct dynstr *src);
/**
* This function frees memory used by the dynamic string.
* @param d Dynamic string to be freed.
* @attention Attempting to call this function on an uninitialized or empty
* instance is undefined behaviour.
* @attention Parameters inside the struct are reset once memory is freed.
* @note This function does nothing on empty, initialized instances.
* @attention Calling this function on an uninitialized instance leads to
* undefined behaviour.
* @attention Once memory is freed, @ref dynstr_init is called so it can be
* used again.
*/
void dynstr_free(struct dynstr *d);