Skip to content

Memory allocator macros aren't as customizable as they appear #472

Open
@AmityWilder

Description

@AmityWilder

Description

The placement of

// Allow custom memory allocators
#ifndef RAYGUI_MALLOC
    #define RAYGUI_MALLOC(sz)       malloc(sz)
#endif
#ifndef RAYGUI_CALLOC
    #define RAYGUI_CALLOC(n,sz)     calloc(n,sz)
#endif
#ifndef RAYGUI_FREE
    #define RAYGUI_FREE(p)          free(p)
#endif

outside of the RAYGUI_IMPLEMENTATION region denies raygui the ability to conditionally exclude stdlib.h.

// main.c
#define RAYGUI_MALLOC(sz) MyEpicMalloc(sz)
#define RAYGUI_CALLOC(n,sz) MyEpicCalloc(n,sz)
#define RAYGUI_FREE(p) MyEpicFree(p)
#define RAYGUI_IMPLEMENTATION
#include "raygui.h"
// ...
// raygui.h

// ...

#ifndef RAYGUI_MALLOC
  // RAYGUI_MALLOC is defined, so this block is ignored
#endif

// ...

// this line is completely unconditional, it gets included even if RAYGUI_MALLOC, RAYGUI_CALLOC, nor RAYGUI_FREE \
   use their default stdlib.h definitions
#include <stdlib.h>             // Required for: malloc(), calloc(), free() [GuiLoadStyle(), GuiLoadIcons()]

// ...

This also creates the possibility for RAYGUI_MALLOC, RAYGUI_CALLOC, and RAYGUI_FREE to be unintentionally misused by creating different definitions for them in separate places, assuming raygui will either somehow magically know to locally use a different allocator depending on which file the header is being included in, or simply forgetting--because some definition of them is accessible by the mere inclusion of raygui.h--that the macros were given a custom definition elsewhere.

// my_cool_module.h
#define RAYGUI_MALLOC(sz) MyEpicMalloc(sz)
#include "raygui.h"  // raygui does not actually use RAYGUI_MALLOC yet
int ImTotallyAllocatingTheSameWayAsRayGUI();
// my_cool_module.c
#include "my_cool_module.h"

int ImTotallyAllocatingTheSameWayAsRayGUI()
{
    // RAYGUI_MALLOC uses MyEpicMalloc here
    void* myDefinitelyRayguiCompatibleAllocation = RAYGUI_MALLOC(...);
    // ...
}
// my_other_module.h
#define RAYGUI_MALLOC(sz) MyOtherMalloc(sz)
#include "raygui.h"  // raygui does not actually use RAYGUI_MALLOC yet

int ImAlsoForRealAllocatingTheSameWayAsRayGUI();
// my_other_module.c
#include "my_other_module.h"

int ImAlsoForRealAllocatingTheSameWayAsRayGUI()
{
    // RAYGUI_MALLOC uses MyOtherMalloc here
    void* my100PercentRayguiCompatibleAllocation = RAYGUI_MALLOC(...);
    // ...
}
// main.c
#include "my_cool_module.h"
#include "my_other_module.h"  // shadows my_cool_module.h definition of RAYGUI_MALLOC

#define RAYGUI_IMPLEMENTATION
#include "raygui.h"  // uses most recent RAYGUI_MALLOC definition, MyEpicMalloc, here; \
                        completely bamboozling ImTotallyAllocatingTheSameWayAsRayGUI()

Proposal

Because the raygui allocator macros probably aren't intended to be used outside of raygui, they could be moved into the RAYGUI_IMPLEMENTATION region of the header. This way, the inclusion of stdlib.h could be guarded behind the requirement that at least one allocator function must be undefined (thus requiring stdlib.h for the default definition) because the default definitions would be guaranteed not to have been automatically provided by raygui itself yet at that location.

#ifdef RAYGUI_IMPLEMENTATION

// ...

#if !defined(RAYGUI_MALLOC) && !defined(RAYGUI_CALLOC) && !defined(RAYGUI_FREE)
    #include <stdlib.h>             // Required for: malloc(), calloc(), free() [GuiLoadStyle(), GuiLoadIcons()]
#endif

// Allow custom memory allocators
#ifndef RAYGUI_MALLOC
    #define RAYGUI_MALLOC(sz)       malloc(sz)
#endif
#ifndef RAYGUI_CALLOC
    #define RAYGUI_CALLOC(n,sz)     calloc(n,sz)
#endif
#ifndef RAYGUI_FREE
    #define RAYGUI_FREE(p)          free(p)
#endif

// ...

Furthermore, this would prevent the macros from being defined by header-only uses of raygui.h, which causes them to appear in intellisense and other suggestion tools. Not having them defined by the header would mean users of raygui won't be able to see/use them without a custom definition.

By having the definitions publicly visible in the header-only version of raygui.h, a user might assume they are available and think macros are "many declares, one definition" like functions--not realizing the header definitions might not match the implementation definitions, if they decide to redefine them later on without reviewing each usage of the macros in their code.

#include "raygui.h"

int MyCoolMemoryAllocatingFunction()
{
    // ERROR: RAYGUI_MALLOC is not defined.
    void* myAwesomeMemoryAllocation = RAYGUI_MALLOC(...);

    // this teaches the user that a macro isn't available   \
       *everywhere* just because it is defined *somewhere*

    // the user won't be able to use RAYGUI_MALLOC without  \
       also including whatever file defines their custom    \
       definition of it, or making duplicative definitions  \
       everywhere and eventually realizing on their own how \
       silly that probably is.

    // ...
}

Secondary Proposal

It might also make sense to provide greater clarity by requiring definitions of all three allocation functions if any one is defined, to ensure someone doesn't define a custom RAYGUI_MALLOC without defining a custom RAYGUI_FREE, causing raygui to invalidly call free() on a stack poiner.

#ifdef RAYGUI_IMPLEMENTATION

// ...

// Allow custom memory allocators
#if defined(RAYGUI_MALLOC) || defined(RAYGUI_CALLOC) || defined(RAYGUI_FREE)
    #if !defined(RAYGUI_MALLOC) || !defined(RAYGUI_CALLOC) || !defined(RAYGUI_FREE)
        #error RAYGUI: if RAYGUI_MALLOC, RAYGUI_CALLOC, or RAYGUI_FREE is customized, all three must be customized
    #endif
#else
    #include <stdlib.h>             // Required for: malloc(), calloc(), free() [GuiLoadStyle(), GuiLoadIcons()]

    #define RAYGUI_MALLOC(sz)       malloc(sz)
    #define RAYGUI_CALLOC(n,sz)     calloc(n,sz)
    #define RAYGUI_FREE(p)          free(p)
#endif

// ...

Metadata

Metadata

Assignees

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions