0
\$\begingroup\$

I wrote a bunch of functions and macros for safer and simpler allocation. They are documented in their files.


Disclaimer:

The library requires POSIX extensions, GCC extensions, and LIBBSD extensions, but if someone wants to make it portable, it shouldn't be difficult to get rid of the extensions I used. But to me, they are of great help and make my life much easier.

I use goto because it is cleaner to me (the way I use it), but if someone wants to get rid of it, it should be easy.

I acknowledge some inconsistency in the parameter nmemb which is sometimes ptrdiff_t and some others size_t. I would like it to always be ptrdiff_t, but in the macros that call standard functions (instead of mine) those standard functions accept size_t, and the code to reject negative values of ptrdiff_t inside a macro would make the macros too long and dangerous. In functions and in macros that call functions of mine, I consistently use ptrdiff_t.


libalx/base/stdlib/alloc/README.rst:

<libalx/base/stdlib/alloc/>
============================

The following functions are added to the C / POSIX / GNU / BSD allocation
functions (some of them may be implemented as macros; only when necessary).


1) Headers
----------

.. code-block:: c

        <libalx/base/stdlib/alloc/callocs.h>
        <libalx/base/stdlib/alloc/callocs.hpp>
        <libalx/base/stdlib/alloc/mallocarray.h>
        <libalx/base/stdlib/alloc/mallocarray.hpp>
        <libalx/base/stdlib/alloc/mallocarrays.h>
        <libalx/base/stdlib/alloc/mallocarrays.hpp>
        <libalx/base/stdlib/alloc/mallocs.h>
        <libalx/base/stdlib/alloc/mallocs.hpp>
        <libalx/base/stdlib/alloc/reallocarrayf.h>
        <libalx/base/stdlib/alloc/reallocarrayf.hpp>
        <libalx/base/stdlib/alloc/reallocarrayfs.h>
        <libalx/base/stdlib/alloc/reallocarrayfs.hpp>
        <libalx/base/stdlib/alloc/reallocarrays.h>
        <libalx/base/stdlib/alloc/reallocarrays.hpp>
        <libalx/base/stdlib/alloc/reallocfs.h>
        <libalx/base/stdlib/alloc/reallocfs.hpp>
        <libalx/base/stdlib/alloc/reallocs.h>
        <libalx/base/stdlib/alloc/reallocs.hpp>

2) Functions
------------

.. code-block:: c

        [[gnu::nonnull]]
        int     callocs(type **restrict ptr, size_t nmemb);

        [[gnu::malloc]] [[gnu::warn_unused_result]]
        void    *mallocarray(ptrdiff_t nmemb, size_t size);

        [[gnu::nonnull]] [[gnu::warn_unused_result]]
        int     mallocarrays(type **restrict ptr, ptrdiff_t nmemb);

        [[gnu::nonnull]] [[gnu::warn_unused_result]]
        int     mallocs(void **restrict ptr, size_t size);

        [[gnu::warn_unused_result]]
        void    *reallocarrayf(void *ptr, ptrdiff_t nmemb, size_t size);

        [[gnu::nonnull]] [[gnu::warn_unused_result]]
        int     reallocarrayfs(type **restrict ptr, ptrdiff_t nmemb);

        [[gnu::nonnull]][[gnu::warn_unused_result]]
        int     reallocarrays(type **restrict ptr, ptrdiff_t nmemb);

        [[gnu::nonnull]] [[gnu::warn_unused_result]]
        int     reallocfs(void **restrict ptr, size_t size);

        [[gnu::nonnull]] [[gnu::warn_unused_result]]
        int     reallocs(void **restrict ptr, size_t size);

To be able to use any of those functions, the corresponding headers should be
included.

3) When to use each of the functions
------------------------------------

Functions ending in ``s`` should always be preferred.

Reallocation functions ending in ``f`` (or ``fs``) free the memory upon
failure to ease error handling.

Functions containing ``array`` (and ``callocs()``) should be used when
allocating arrays, or single elements (arrays of size 1).  The other funtions
should be used when and only when dealing with buffers of bytes.

4) More info
------------

For more detailed documentation about each of the functions, read the
corresponding headers.

libalx/base/stdlib/alloc/callocs.h:

/******************************************************************************
 ******* include guard ********************************************************
 ******************************************************************************/
#pragma once    /* libalx/base/stdlib/alloc/callocs.h */


/******************************************************************************
 ******* about ****************************************************************
 ******************************************************************************/
/*
 * [[gnu::nonnull]]
 * int  callocs(type **restrict ptr, size_t nmemb);
 *
 * Safe & simple wrapper for `calloc()`.
 *
 * Features:
 * - Safely computes the element size (second argument to `calloc()`)
 *      so the user can't make mistakes.
 * - Returns non-zero on error.
 * - Doesn't cast
 * - The pointer stored in `*ptr` is always a valid pointer or NULL.
 *
 * example:
 *      #define ALX_NO_PREFIX
 *      #include <libalx/base/stdlib/alloc/callocs.h>
 *
 *              int *arr;
 *
 *              if (callocs(&arr, 7))           // int arr[7];
 *                      goto err;
 *
 *              // `arr` has been succesfully allocated here
 *              free(arr);
 *      err:
 *              // No memory leaks
 */


/******************************************************************************
 ******* headers **************************************************************
 ******************************************************************************/
#include <stdlib.h>


/******************************************************************************
 ******* macros ***************************************************************
 ******************************************************************************/
/*
 * callocs()
 *
 * ptr:         Memory will be allocated, and a pointer to it will be stored
 *              in *ptr.
 * nmemb:       Number of elements in the array.
 *
 * return:
 *      0:              OK.
 *      != 0:           Failed.
 */
#define alx_callocs(ptr, nmemb) (                                       \
{                                                                       \
        __auto_type     ptr_    = (ptr);                                \
                                                                        \
        *ptr_   = calloc(nmemb, sizeof(**ptr_));                        \
                                                                        \
        !(*ptr_);                                                       \
}                                                                       \
)


/* Rename without alx_ prefix */
#if defined(ALX_NO_PREFIX)
#define callocs(ptr, nmemb)     alx_callocs(ptr, nmemb)
#endif


/******************************************************************************
 ******* enum *****************************************************************
 ******************************************************************************/


/******************************************************************************
 ******* struct / union *******************************************************
 ******************************************************************************/


/******************************************************************************
 ******* prototypes ***********************************************************
 ******************************************************************************/


/******************************************************************************
 ******* inline ***************************************************************
 ******************************************************************************/


/******************************************************************************
 ******* end of file **********************************************************
 ******************************************************************************/

libalx/base/stdlib/alloc/mallocarray.h:

/******************************************************************************
 ******* include guard ********************************************************
 ******************************************************************************/
#pragma once    /* libalx/base/stdlib/alloc/mallocarray.h */


/******************************************************************************
 ******* about ****************************************************************
 ******************************************************************************/
/*
 * [[gnu::malloc]] [[gnu::warn_unused_result]]
 * void *mallocarray(ptrdiff_t nmemb, size_t size);
 *
 * Almost equivalent to `reallocarray(NULL, nmemb, size)`.
 *
 * Features:
 * - It fails safely if (nmemb < 0).  With `reallocarray()` the array would be
 *      be allocated (it uses `size_t` instead of `ptrdiff_t`), and it's usage
 *      would likely produce undefined behavior.
 *
 * example:
 *      #define ALX_NO_PREFIX
 *      #include <libalx/base/stdlib/alloc/mallocarray.h>
 *
 *              int *arr;
 *
 *              arr     = mallocarray(7, sizeof(*arr));         // int arr[7];
 *              if (!arr)
 *                      goto err;
 *
 *              // `arr` has been succesfully allocated here
 *              free(arr);
 *      err:
 *              // No memory leaks
 */


/******************************************************************************
 ******* headers **************************************************************
 ******************************************************************************/
#include <errno.h>
#include <stddef.h>
#include <stdlib.h>


/******************************************************************************
 ******* macros ***************************************************************
 ******************************************************************************/


/******************************************************************************
 ******* enum *****************************************************************
 ******************************************************************************/


/******************************************************************************
 ******* struct / union *******************************************************
 ******************************************************************************/


/******************************************************************************
 ******* prototypes ***********************************************************
 ******************************************************************************/
/*
 * mallocarray()
 *
 * nmemb:       Number of elements in the array.
 * size:        Size of each element in the array.
 *
 * return:
 *      != NULL:        OK.
 *      NULL:           Failed.
 */
__attribute__((malloc, warn_unused_result))
inline
void    *alx_mallocarray        (ptrdiff_t nmemb, size_t size);


/******************************************************************************
 ******* static inline ********************************************************
 ******************************************************************************/
/* Rename without alx_ prefix */
#if defined(ALX_NO_PREFIX)
__attribute__((always_inline, malloc, warn_unused_result))
static inline
void    *mallocarray            (ptrdiff_t nmemb, size_t size)
{
        return  alx_mallocarray(nmemb, size);
}
#endif


/******************************************************************************
 ******* inline ***************************************************************
 ******************************************************************************/
inline
void    *alx_mallocarray        (ptrdiff_t nmemb, size_t size)
{

        if (nmemb < 0)
                goto ovf;

        return  reallocarray(NULL, nmemb, size);
ovf:
        errno   = ENOMEM;
        return  NULL;
}


/******************************************************************************
 ******* end of file **********************************************************
 ******************************************************************************/

libalx/base/stdlib/alloc/mallocarrays.h:

/******************************************************************************
 ******* include guard ********************************************************
 ******************************************************************************/
#pragma once    /* libalx/base/stdlib/alloc/mallocarrays.h */


/******************************************************************************
 ******* about ****************************************************************
 ******************************************************************************/
/*
 * [[gnu::nonnull]] [[gnu::warn_unused_result]]
 * int  mallocarrays(type **restrict ptr, ptrdiff_t nmemb);
 *
 * Safe & simple wrapper for `mallocarray()`.
 *
 * Features:
 * - Safely computes the element size (second argument to `mallocarray()`)
 *      so the user can't make mistakes.
 * - Returns non-zero on error.
 * - Doesn't cast
 * - The pointer stored in `*ptr` is always a valid pointer or NULL.
 *
 * example:
 *      #define ALX_NO_PREFIX
 *      #include <libalx/base/stdlib/alloc/mallocarrays.h>
 *
 *              int *arr;
 *
 *              if (mallocarrays(&arr, 7))              // int arr[7];
 *                      goto err;
 *
 *              // `arr` has been succesfully allocated here
 *              free(arr);
 *      err:
 *              // No memory leaks
 */


/******************************************************************************
 ******* headers **************************************************************
 ******************************************************************************/
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>

#include "libalx/base/stdlib/alloc/mallocarray.h"


/******************************************************************************
 ******* macros ***************************************************************
 ******************************************************************************/
/*
 * mallocarrays()
 *
 * ptr:         Memory will be allocated, and a pointer to it will be stored
 *              in *ptr.
 * nmemb:       Number of elements in the array.
 *
 * return:
 *      0:              OK.
 *      != 0:           Failed.
 */
#define alx_mallocarrays(ptr, nmemb)    (                               \
{                                                                       \
        __auto_type     ptr_    = (ptr);                                \
                                                                        \
        *ptr_   = alx_mallocarray(nmemb, sizeof(**ptr_));               \
                                                                        \
        !(*ptr_);                                                       \
}                                                                       \
)


/* Rename without alx_ prefix */
#if defined(ALX_NO_PREFIX)
#define mallocarrays(ptr, nmemb)        alx_mallocarrays(ptr, nmemb)
#endif


/******************************************************************************
 ******* enum *****************************************************************
 ******************************************************************************/


/******************************************************************************
 ******* struct / union *******************************************************
 ******************************************************************************/


/******************************************************************************
 ******* function prototypes **************************************************
 ******************************************************************************/


/******************************************************************************
 ******* inline functions *****************************************************
 ******************************************************************************/


/******************************************************************************
 ******* end of file **********************************************************
 ******************************************************************************/

libalx/base/stdlib/alloc/mallocs.h:

/******************************************************************************
 ******* include guard ********************************************************
 ******************************************************************************/
#pragma once    /* libalx/base/stdlib/alloc/mallocs.h */


/******************************************************************************
 ******* about ****************************************************************
 ******************************************************************************/
/*
 * [[gnu::nonnull]]  [[gnu::warn_unused_result]]
 * int  mallocs(void **restrict ptr, size_t size);
 *
 * Safe & simple wrapper for `malloc()`.
 * To be used for generic buffers of bytes, and not for arrays (use
 * `mallocarray()` family of functions for that purpose).
 *
 * Features:
 * - Returns non-zero on error.
 * - Doesn't cast
 * - The pointer stored in `*ptr` is always a valid pointer or NULL.
 *
 * example:
 *      #define ALX_NO_PREFIX
 *      #include <libalx/base/stdlib/alloc/mallocs.h>
 *
 *              char *buf;
 *
 *              if (mallocs(&buf, 7))           // char buf[7];
 *                      goto err;
 *
 *              // `buf` has been succesfully allocated here
 *              free(buf);
 *      err:
 *              // No memory leaks
 */


/******************************************************************************
 ******* headers **************************************************************
 ******************************************************************************/
#include <stddef.h>
#include <stdlib.h>


/******************************************************************************
 ******* macros ***************************************************************
 ******************************************************************************/


/******************************************************************************
 ******* enum *****************************************************************
 ******************************************************************************/


/******************************************************************************
 ******* struct / union *******************************************************
 ******************************************************************************/


/******************************************************************************
 ******* prototypes ***********************************************************
 ******************************************************************************/
/*
 * mallocs()
 *
 * ptr:         Memory will be allocated, and a pointer to it will be stored
 *              in *ptr.
 * size:        Size of the buffer (in bytes).
 *
 * return:
 *      0:              OK.
 *      != 0:           Failed.
 */
__attribute__((nonnull, warn_unused_result))
inline
int     alx_mallocs     (void **restrict ptr, size_t size);


/******************************************************************************
 ******* static inline ********************************************************
 ******************************************************************************/
/* Rename without alx_ prefix */
#if defined(ALX_NO_PREFIX)
__attribute__((always_inline, nonnull, warn_unused_result))
static inline
int     mallocs         (void **restrict ptr, size_t size)
{
        return  alx_mallocs(ptr, size);
}
#endif


/******************************************************************************
 ******* inline ***************************************************************
 ******************************************************************************/
inline
int     alx_mallocs     (void **restrict ptr, size_t size)
{

        *ptr    = malloc(size);

        return  !(*ptr);
}


/******************************************************************************
 ******* end of file **********************************************************
 ******************************************************************************/

libalx/base/stdlib/alloc/reallocarrayf.h:

/******************************************************************************
 ******* include guard ********************************************************
 ******************************************************************************/
#pragma once    /* libalx/base/stdlib/alloc/reallocarrayf.h */


/******************************************************************************
 ******* about ****************************************************************
 ******************************************************************************/
/*
 * [[gnu::warn_unused_result]]
 * void *reallocarrayf(void *ptr, ptrdiff_t nmemb, size_t size);
 *
 * Almost equivalent to `reallocarray()`.
 *
 * Features:
 * - Upon failure, the passed pointer is freed, to ease error handling and to
 *      avoid memory leaks.
 * - It fails safely if (nmemb < 0).  With `reallocarray()` the array would be
 *      be allocated (it uses `size_t` instead of `ptrdiff_t`), and it's usage
 *      would likely produce undefined behavior.
 *
 * example:
 *      #define ALX_NO_PREFIX
 *      #include <libalx/base/stdlib/alloc/reallocarrayf.h>
 *
 *              int *arr;
 *
 *              arr     = mallocarray(5, sizeof(*arr);          // int arr[5];
 *              arr     = reallocarrayf(arr, 7, sizeof(*arr));  // int arr[7];
 *              if (!arr)
 *                      goto err;
 *
 *              // `arr` has been succesfully reallocated here
 *              free(arr);
 *      err:
 *              // No memory leaks
 */


/******************************************************************************
 ******* headers **************************************************************
 ******************************************************************************/
#include <errno.h>
#include <stddef.h>
#include <stdint.h>
#include <stdlib.h>


/******************************************************************************
 ******* _Static_assert *******************************************************
 ******************************************************************************/
_Static_assert(sizeof(size_t) == sizeof(ptrdiff_t),
                                "sizeof(size_t) != sizeof(ptrdiff_t)");


/******************************************************************************
 ******* macros ***************************************************************
 ******************************************************************************/


/******************************************************************************
 ******* enum *****************************************************************
 ******************************************************************************/


/******************************************************************************
 ******* struct / union *******************************************************
 ******************************************************************************/


/******************************************************************************
 ******* prototypes ***********************************************************
 ******************************************************************************/
/*
 * reallocarrayf()
 *
 * ptr:         Pointer to allocated memory (or NULL).
 * nmemb:       Number of elements in the array.
 * size:        Size of each element in the array.
 *
 * return:
 *      != NULL:        OK.
 *      NULL:           Failed.
 */
__attribute__((warn_unused_result))
inline
void    *alx_reallocarrayf      (void *ptr, ptrdiff_t nmemb, size_t size);


/******************************************************************************
 ******* static inline ********************************************************
 ******************************************************************************/
/* Rename without alx_ prefix */
#if defined(ALX_NO_PREFIX)
__attribute__((always_inline, warn_unused_result))
static inline
void    *reallocarrayf          (void *ptr, ptrdiff_t nmemb, size_t size)
{
        return  alx_reallocarrayf(ptr, nmemb, size);
}
#endif


/******************************************************************************
 ******* inline ***************************************************************
 ******************************************************************************/
inline
void    *alx_reallocarrayf      (void *ptr, ptrdiff_t nmemb, size_t size)
{

        if (!size)
                goto out;
        if (nmemb < 0)
                goto ovf;
        if ((size_t)nmemb  >  (SIZE_MAX / size))
                goto ovf;

        return  reallocf(ptr, size * (size_t)nmemb);
ovf:
        errno   = ENOMEM;
out:
        free(ptr);
        return  NULL;
}


/******************************************************************************
 ******* end of file **********************************************************
 ******************************************************************************/

libalx/base/stdlib/alloc/reallocarrayfs.h:

/******************************************************************************
 ******* include guard ********************************************************
 ******************************************************************************/
#pragma once    /* libalx/base/stdlib/alloc/reallocarrayfs.h */


/******************************************************************************
 ******* about ****************************************************************
 ******************************************************************************/
/*
 * [[gnu::nonnull]] [[gnu::warn_unused_result]]
 * int  reallocarrayfs(type **restrict ptr, ptrdiff_t nmemb);
 *
 * Safe & simple wrapper for `reallocarrayf()`.
 *
 * Features:
 * - Safely computes the element size (second argument to `reallocarrayf()`)
 *      so the user can't make mistakes.
 * - Returns non-zero on error.
 * - Doesn't cast.
 * - Upon failure, the passed pointer is freed, to ease error handling and to
 *      avoid memory leaks.
 * - It fails safely if (nmemb < 0).  With `reallocarray()` the array would be
 *      be allocated (it uses `size_t` instead of `ptrdiff_t`), and it's usage
 *      would likely produce undefined behavior.
 * - The pointer stored in `*ptr` is always a valid pointer or NULL.
 *
 * example:
 *      #define ALX_NO_PREFIX
 *      #include <libalx/base/stdlib/alloc/reallocarrayfs.h>
 *
 *              int *arr;
 *
 *              if (mallocarrays(&arr, 5))      // int arr[5];
 *                      goto err;
 *              if (reallocarrayfs(&arr, 7))    // int arr[7];
 *                      goto err;
 *
 *              // `arr` has been succesfully reallocated here
 *              free(arr);
 *      err:
 *              // No memory leaks
 */


/******************************************************************************
 ******* headers **************************************************************
 ******************************************************************************/
#include "libalx/base/stdlib/alloc/reallocarrayf.h"


/******************************************************************************
 ******* macros ***************************************************************
 ******************************************************************************/
/*
 * reallocarrayfs()
 *
 * ptr:         Pointer to a pointer to the memory to be reallocated.
 *              A pointer to the reallocated memory will be stored
 *              in *ptr.
 * nmemb:       Number of elements in the array.
 *
 * return:
 *      0:              OK.
 *      != 0:           Failed.
 */
#define alx_reallocarrayfs(ptr, nmemb)  (                               \
{                                                                       \
        __auto_type     ptr_    = (ptr);                                \
                                                                        \
        *ptr_   = alx_reallocarrayf(*ptr_, nmemb, sizeof(**ptr_));      \
                                                                        \
        !(*ptr_);                                                       \
}                                                                       \
)


/* Rename without alx_ prefix */
#if defined(ALX_NO_PREFIX)
#define reallocarrayfs(ptr, nmemb)      alx_reallocarrayfs(ptr, nmemb)
#endif


/******************************************************************************
 ******* enum *****************************************************************
 ******************************************************************************/


/******************************************************************************
 ******* struct / union *******************************************************
 ******************************************************************************/


/******************************************************************************
 ******* function prototypes **************************************************
 ******************************************************************************/


/******************************************************************************
 ******* inline functions *****************************************************
 ******************************************************************************/


/******************************************************************************
 ******* end of file **********************************************************
 ******************************************************************************/

libalx/base/stdlib/alloc/reallocarrays.h:

/******************************************************************************
 ******* include guard ********************************************************
 ******************************************************************************/
#pragma once    /* libalx/base/stdlib/alloc/reallocarrays.h */


/******************************************************************************
 ******* about ****************************************************************
 ******************************************************************************/
/*
 * [[gnu::nonnull]][[gnu::warn_unused_result]]
 * int  reallocarrays(type **restrict ptr, size_t nmemb);
 *
 * Safe & simple wrapper for `reallocarray()`.
 *
 * Features:
 * - Safely computes the element size (second argument to `reallocarray()`)
 *      so the user can't make mistakes.
 * - Returns non-zero on error.
 * - Doesn't cast.
 * - Upon failure, the pointer is untouched (no memory leak).
 * - The pointer stored in `*ptr` is always a valid pointer or NULL.
 *
 * example:
 *      #define ALX_NO_PREFIX
 *      #include <libalx/base/stdlib/alloc/reallocarrays.h>
 *
 *              int *arr;
 *
 *              if (mallocarrays(&arr, 5))      // int arr[5];
 *                      goto err;
 *              if (reallocarrays(&arr, 7))     // int arr[7];
 *                      goto err;
 *
 *              // `arr` has been succesfully reallocated here
 *      err:
 *              free(arr);
 *              // No memory leaks
 */


/******************************************************************************
 ******* headers **************************************************************
 ******************************************************************************/
#include <stdlib.h>

#include "libalx/base/stdlib/alloc/reallocs.h"


/******************************************************************************
 ******* macros ***************************************************************
 ******************************************************************************/
/*
 * reallocarrays()
 *
 * ptr:         Pointer to a pointer to the memory to be reallocated.
 *              A pointer to the reallocated memory will be stored
 *              in *ptr.
 * nmemb:       Number of elements in the array.
 *
 * return:
 *      0:              OK.
 *      != 0:           Failed.
 */
#define alx_reallocarrays(ptr, nmemb)   (                               \
{                                                                       \
        __auto_type     ptr_    = (ptr);                                \
        __auto_type     nmemb_  = (nmemb);                              \
        void            *vp_;                                           \
                                                                        \
        vp_     = reallocarray(*ptr_, nmemb_, sizeof(**ptr_));          \
                                                                        \
        alx_reallocs__(ptr_, vp_, nmemb_);                              \
}                                                                       \
)


/* Rename without alx_ prefix */
#if defined(ALX_NO_PREFIX)
#define reallocarrays(ptr, nmemb)       alx_reallocarrays(ptr, nmemb)
#endif


/******************************************************************************
 ******* enum *****************************************************************
 ******************************************************************************/


/******************************************************************************
 ******* struct / union *******************************************************
 ******************************************************************************/


/******************************************************************************
 ******* function prototypes **************************************************
 ******************************************************************************/


/******************************************************************************
 ******* inline functions *****************************************************
 ******************************************************************************/


/******************************************************************************
 ******* end of file **********************************************************
 ******************************************************************************/

libalx/base/stdlib/alloc/reallocfs.h:

/******************************************************************************
 ******* include guard ********************************************************
 ******************************************************************************/
#pragma once    /* libalx/base/stdlib/alloc/reallocfs.h */


/******************************************************************************
 ******* about ****************************************************************
 ******************************************************************************/
/*
 * [[gnu::nonnull]] [[gnu::warn_unused_result]]
 * int  reallocfs(void **restrict ptr, size_t size);
 *
 * Safe & simple wrapper for `reallocf()`.
 * To be used for generic buffers of bytes, and not for arrays (use
 * `reallocarray()` family of functions for that purpose).
 *
 * Features:
 * - Returns non-zero on error.
 * - Doesn't cast.
 * - Upon failure, the passed pointer is freed, to ease error handling and to
 *      avoid memory leaks.
 * - The pointer stored in `*ptr` is always a valid pointer or NULL.
 *
 * example:
 *      #define ALX_NO_PREFIX
 *      #include <libalx/base/stdlib/alloc/reallocfs.h>
 *
 *              char *buf;
 *
 *              if (mallocs(&buf, 5))   // char buf[5];
 *                      goto err;
 *              if (reallocfs(&buf, 7)) // char buf[7];
 *                      goto err;
 *
 *              // `buf` has been succesfully reallocated here
 *              free(buf);
 *      err:
 *              // No memory leaks
 */


/******************************************************************************
 ******* headers **************************************************************
 ******************************************************************************/
#include <stddef.h>
#include <stdlib.h>


/******************************************************************************
 ******* macros ***************************************************************
 ******************************************************************************/


/******************************************************************************
 ******* enum *****************************************************************
 ******************************************************************************/


/******************************************************************************
 ******* struct / union *******************************************************
 ******************************************************************************/


/******************************************************************************
 ******* prototypes ***********************************************************
 ******************************************************************************/
/*
 * reallocfs()
 *
 * ptr:         Pointer to a pointer to the memory to be reallocated.
 *              A pointer to the reallocated memory will be stored
 *              in *ptr.
 * size:        Size of the buffer (in bytes).
 *
 * return:
 *      0:              OK.
 *      != 0:           Failed.
 */
__attribute__((nonnull, warn_unused_result))
inline
int     alx_reallocfs   (void **restrict ptr, size_t size);


/******************************************************************************
 ******* static inline ********************************************************
 ******************************************************************************/
/* Rename without alx_ prefix */
#if defined(ALX_NO_PREFIX)
__attribute__((always_inline, nonnull, warn_unused_result))
static inline
int     reallocfs       (void **restrict ptr, size_t size)
{
        return  alx_reallocfs(ptr, size);
}
#endif


/******************************************************************************
 ******* inline ***************************************************************
 ******************************************************************************/
inline
int     alx_reallocfs   (void **restrict ptr, size_t size)
{

        *ptr    = reallocf(*ptr, size);

        return  !*ptr;
}


/******************************************************************************
 ******* end of file **********************************************************
 ******************************************************************************/

libalx/base/stdlib/alloc/reallocs.h:

/******************************************************************************
 ******* include guard ********************************************************
 ******************************************************************************/
#pragma once    /* libalx/base/stdlib/alloc/reallocs.h */


/******************************************************************************
 ******* about ****************************************************************
 ******************************************************************************/
/*
 * [[gnu::nonnull]] [[gnu::warn_unused_result]]
 * int  reallocs(void **restrict ptr, size_t size);
 *
 * Safe & simple wrapper for `reallocf()`.
 * To be used for generic buffers of bytes, and not for arrays (use
 * `reallocarray()` family of functions for that purpose).
 *
 * Features:
 * - Returns non-zero on error.
 * - Doesn't cast.
 * - Upon failure, the pointer is untouched (no memory leak).
 * - The pointer stored in `*ptr` is always a valid pointer or NULL.
 *
 * example:
 *      #define ALX_NO_PREFIX
 *      #include <libalx/base/stdlib/alloc/reallocs.h>
 *
 *              char *buf;
 *
 *              if (mallocs(&buf, 5))   // char buf[5];
 *                      goto err;
 *              if (reallocs(&buf, 7))  // char buf[7];
 *                      goto err;
 *
 *              // `buf` has been succesfully reallocated here
 *      err:
 *              free(buf);
 *              // No memory leaks
 */


/******************************************************************************
 ******* headers **************************************************************
 ******************************************************************************/
#include <stdbool.h>
#include <stddef.h>
#include <stdlib.h>


/******************************************************************************
 ******* macros ***************************************************************
 ******************************************************************************/


/******************************************************************************
 ******* enum *****************************************************************
 ******************************************************************************/


/******************************************************************************
 ******* struct / union *******************************************************
 ******************************************************************************/


/******************************************************************************
 ******* prototypes ***********************************************************
 ******************************************************************************/
/*
 * reallocs()
 *
 * ptr:         Pointer to a pointer to the memory to be reallocated.
 *              A pointer to the reallocated memory will be stored
 *              in *ptr.
 * size:        Size of the buffer (in bytes).
 *
 * return:
 *      0:              OK.
 *      != 0:           Failed.
 */
__attribute__((nonnull, warn_unused_result))
inline
int     alx_reallocs    (void **restrict ptr, size_t size);

/*
 * alx_reallocs__()
 *
 * This function safely assigns the result of a reallocation to the pointer
 * provided.  If the reallocation failed, this function doesn't assign the
 * pointer, and keeps the old one.  This function is only to be used within
 * this library, and should not be called directly by the user.
 *
 * ptr:         Pointer to a pointer to the memory to be reallocated.
 *              A pointer to the reallocated memory will be stored
 *              in *ptr.
 * vp:          Result of `realloc()`.
 * size:        `size` passed to `realloc()`.
 *
 * return:
 *      0:              OK.
 *      != 0:           Reallocation failed.
 */
__attribute__((nonnull(1), warn_unused_result))
inline
int     alx_reallocs__  (void **restrict ptr, void *restrict vp, size_t size);


/******************************************************************************
 ******* static inline ********************************************************
 ******************************************************************************/
/* Rename without alx_ prefix */
#if defined(ALX_NO_PREFIX)
__attribute__((always_inline, nonnull, warn_unused_result))
static inline
int     reallocs        (void **restrict ptr, size_t size)
{
        return  alx_reallocs(ptr, size);
}
#endif


/******************************************************************************
 ******* inline ***************************************************************
 ******************************************************************************/
inline
int     alx_reallocs    (void **restrict ptr, size_t size)
{
        void    *vp;

        vp      = realloc(*ptr, size);

        return  alx_reallocs__(ptr, vp, size);
}

inline
int     alx_reallocs__  (void **restrict ptr, void *restrict vp, size_t size)
{
        bool    failed;

        failed  = !vp && size;
        if (!failed)
                *ptr    = vp;

        return  failed;
}


/******************************************************************************
 ******* end of file **********************************************************
 ******************************************************************************/

Any comments?

\$\endgroup\$

3 Answers 3

4
\$\begingroup\$

Use Doxygen to document your code

It's already mentioned by others, but you have a lot of comments. It's good to document all the functions, but you are not using a standard code documentation language as far as I can tell. I recomment that you use Doxygen. The advantage is that it understands C and C++, and will perform checks on your documentation, such as that you documented all the parameters and the return value. Furthermore, it allows the creation of a manual in PDF and HTML formats (amongs others), which are much nicer for a developer to read than having to read the source files.

Avoid (complex) macros

It's always better to use functions than macros. If you cannot avoid macros, for example to allow arbitrary type arguments to be passed, it is better to create a simple macro to deal with the parameter type, and then call a regular function to do the main work. For example:

static inline int alx_callocs_impl(void **ptr, size_t nmemb, size_t size) {
    *ptr = calloc(nmemb, size);
    return !*ptr;
}

#define alx_callocs(ptr, nmemb) alx_callocs_impl((ptr), (nmemb), sizeof(ptr))

About the disclaimers

The library requires POSIX extensions, GCC extensions, and LIBBSD extensions

That's a lot of requirements for such a low-level library, making it much less useful. Even if this library is only for your personal use, consider that in the future you might want to port your projects to platforms that don't support POSIX, libbsd or GCC.

I use goto because it is cleaner to me (the way I use it)

I think a lot of these uses of goto are unnecessary. For example, you can rewrite alx_mallocarray() like this:

inline void *alx_mallocarray(ptrdiff_t nmemb, size_t size) {
    if (nmemb < 0) {
        errno = ENOMEM
        return NULL;
    }

    return reallocarray(NULL, nmemb, size);
}

I acknowledge some inconsistency in the parameter nmemb which is sometimes ptrdiff_t and some others size_t. I would like it to always be ptrdiff_t, but in the macros that call standard functions (instead of mine) those standard functions accept size_t, and the code to reject negative values of ptrdiff_t inside a macro would make the macros too long and dangerous. In functions and in macros that call functions of mine, I consistently use ptrdiff_t.

There is a reason that standard library and POSIX functions use unsigned types like size_t sometimes, and signed types like int or ssize_t at other times, and there is a consistency to it. When a parameter should never be a negative number, size_t is used. Doing so will make it easier to catch errors; and avoids you having to write checks like if (nmemb < 0).

Another reason not to use different parameter types than those used by standard library functions is that it goes against users' expectations.

\$\endgroup\$
7
  • \$\begingroup\$ There are good reasons to use ptrdiff_t. I'm searching for an article I read a long time ago, but while I find it, I can link you to this one, which isn't as extense, but is also good (it is C++, but it's meaningful in C too): aristeia.com/Papers/C++ReportColumns/sep95.pdf \$\endgroup\$ Dec 30, 2019 at 16:08
  • \$\begingroup\$ "Avoid (complex) macros": Good solution :) \$\endgroup\$ Dec 30, 2019 at 16:09
  • 1
    \$\begingroup\$ Some words from Bjarne Stroustrup: youtube.com/watch?v=Puio5dly9N8&t=42m41s And from Google's C++ Coding Style: google.github.io/styleguide/cppguide.html#Integer_Types \$\endgroup\$ Dec 30, 2019 at 16:34
  • 1
    \$\begingroup\$ "There is a reason the standard functions use size_t sometimes, and ssize_t at other times" --> There a no standard C library function that uses ssize_t as ssize_t is not in the C spec. The point of the paragraph based on a false premise. OP's code used ptrdiff_t for nmemb. \$\endgroup\$ Jan 1, 2020 at 7:45
  • \$\begingroup\$ @chux-ReinstateMonica You are correct for the standard library. I was thinking about POSIX functions like read() that return ssize_t. I updated the answer. I don't know of any standard library or POSIX function that takes a ptrdiff_t argument or returns a ptrdiff_t; it's either int or ssize_t. C99's calloc() and reallocarray() functions use size_t nmemb, so that's why I pointed out it might be confusing to wrap those functions but use different argument types. \$\endgroup\$
    – G. Sliepen
    Jan 1, 2020 at 12:09
5
\$\begingroup\$

Portability

While #pragma once is widely supported it is not part of the C programming standard. To make this library more portable, use include guards such as shown in the example below.

Ease of Use

Don't force the users of the library to include files in the proper order, functions that are written using other functions in the library need to include the header for the other function. There is no guarantee that the user of the library wants to include one header file that includes all the other header files.

Useless Comments

In the comments after another answer there is a statement that the is 47 KLOC (K lines of code). Comments don't count as lines of code and it appears that each file is about 70% comments. I agree with the author of the other answer that this could all be in a single file.

There are a lot of boiler plate comments that are not useful, comments such as include guard really don't help anyone. The About comments don't need a header comment and should include the comment block that describes the entry point, here is one example:

#ifndef MALLOCARRAY_H
#define MALLOCARRAY_H
/*
 *  About:
 * [[gnu::malloc]] [[gnu::warn_unused_result]]
 * void *mallocarray(ptrdiff_t nmemb, size_t size);
 *
 * Almost equivalent to `reallocarray(NULL, nmemb, size)`.
 *
 * Features:
 * - It fails safely if (nmemb < 0).  With `reallocarray()` the array would be
 *      be allocated (it uses `size_t` instead of `ptrdiff_t`), and it's usage
 *      would likely produce undefined behavior.
 *
 * example:
 *      #define ALX_NO_PREFIX
 *      #include <libalx/base/stdlib/alloc/mallocarray.h>
 *
 *              int *arr;
 *
 *              arr     = mallocarray(7, sizeof(*arr));         // int arr[7];
 *              if (!arr)
 *                      goto err;
 *
 *              // `arr` has been succesfully allocated here
 *              free(arr);
 *      err:
 *              // No memory leaks
 *
 * Parameters:
 * nmemb:       Number of elements in the array.
 * size:        Size of each element in the array.
 *
 * return:
 *      != NULL:        OK.
 *      NULL:           Failed.
*/

#include <errno.h>
#include <stddef.h>
#include <stdlib.h>
#include "reallocArray.h"
__attribute__((malloc, warn_unused_result))
inline
void    *alx_mallocarray        (ptrdiff_t nmemb, size_t size);

#if defined(ALX_NO_PREFIX)
__attribute__((always_inline, malloc, warn_unused_result))
static inline
void    *mallocarray            (ptrdiff_t nmemb, size_t size)
{
        return  alx_mallocarray(nmemb, size);
}
#endif

inline
void    *alx_mallocarray        (ptrdiff_t nmemb, size_t size)
{

    if (nmemb < 0)
        goto ovf;

    return  reallocarray(NULL, nmemb, size);
    ovf:
    errno   = ENOMEM;
    return  NULL;
}

#endif // MALLOCARRAY_H

Opinionated Summary

It is not clear who would benefit from this library, the C programming language has been around for almost 50 years. If a library like this was beneficial it would have been written before this.

\$\endgroup\$
10
  • \$\begingroup\$ "Portability": I've had many issues with header guards (more than once I forgot to change the name after copy&paste), and as you said, #pragma once is implemented by most compilers. Even MSVC has that. \$\endgroup\$ Dec 29, 2019 at 16:32
  • \$\begingroup\$ "Ease of use": All of my headers include internally all they need. There's no proper order, and you can include any of the headers separately and it will work. If some header (or some source) needs some other header it will be included so the user doesn't have to care. \$\endgroup\$ Dec 29, 2019 at 16:35
  • 2
    \$\begingroup\$ more than once I forgot to change the name after copy&paste It's obvious you use copy and paste which in and of itself is a rather bad programming practice. \$\endgroup\$
    – pacmaninbw
    Dec 29, 2019 at 16:52
  • 3
    \$\begingroup\$ @CacahueteFrito If you don't want to accept a review, don't request one. \$\endgroup\$
    – S.S. Anne
    Dec 29, 2019 at 17:06
  • 2
    \$\begingroup\$ You want to look at the third answer on this question stackoverflow.com/questions/1143936/…. No experienced professional programmer enjoys fixing copy and paste and they will refactor it if they are allowed to because copy and paste code generally creates bugs, your experience with #include guards is just one example. Object Oriented Design and Programming and inheritance is/was in part an effort to reduce copy & paste errors. Writing a function for each memory allocation is effectively implementing the Single Responsibility Principle. \$\endgroup\$
    – pacmaninbw
    Dec 29, 2019 at 17:32
2
\$\begingroup\$

If you want people to use your library, then you want to think about how to make it easy for others to adopt it.

I would consider revamping the implementation into a Single-file library. Simply put, the more files, the more tedious it becomes to add someone else's code to your project. Which is why, single-file libraries are popular in the C/C++ world because they are easy to manage (package management facilities are lacking in these languages). Personally, I've used hashmaps, bignum libraries, red-black trees etc, all delivered as single-file C libraries. But if I have to add more than one (or two) files to use someone else's code, likely I won't bother.

For me, the other dealbreaker would be Windows compatiblity. Windows compatiblity is very important for me and I can see that you are using several features not available on MSVC.

\$\endgroup\$
5
  • \$\begingroup\$ I would like to add Windows (x64) compatibility in the future, but that's something I may need help and time; mainly because I don't use Windows. \$\endgroup\$ Dec 29, 2019 at 11:48
  • 1
    \$\begingroup\$ For the single-file library: My library currently has over 47 k lines of code, so I don't see it fitting in one header & one source. What I did some time ago was to have a dummy header <libalx.h> to include all of the headers, but as I did it when I started writing the library, I realized that I had to continuously update that file and sometimes I forgot it. However, when I release the first stable version of the library, I could do that; it's a nice addition. I still prefer to include each header, because then I know where to go for documentation, but I can see the benefit of having that. :) \$\endgroup\$ Dec 29, 2019 at 11:53
  • 1
    \$\begingroup\$ The idea I have for this library is to install it in the system (via apt-get or similar) and use it just by including the headers. That way, I ease the installation part. The separation into tiny files helps me organize and maintain the code and easily find a function. But then if I have a dummy header to include them all, the user could also be happy by ignoring the details of the library. \$\endgroup\$ Dec 29, 2019 at 12:00
  • \$\begingroup\$ @CacahueteFrito So these allocation functions are just one part of a much larger "libalx" library containing lots of other utility routines? \$\endgroup\$ Dec 29, 2019 at 18:16
  • \$\begingroup\$ Exactly. It's a big library where I put all the code I repeatedly use. \$\endgroup\$ Dec 29, 2019 at 18:50

Your Answer

By clicking “Post Your Answer”, you agree to our terms of service and acknowledge you have read our privacy policy.

Not the answer you're looking for? Browse other questions tagged or ask your own question.