Custom Error Handler Library in C++ Is this code clean and optimized? When taking memory snapshots, it seems a bit high.
Anything I have done wrong or can do to optimize it?
#pragma once
#include &lt;iostream&gt;
#include &lt;windows.h&gt;
#include &lt;sstream&gt;
#include &lt;Psapi.h&gt;

// Author: https://github.com/TheRacc2
// Other Help: https://github.com/dgoguerra/, for the bytes_to_size function (modified from the &quot;humanSize&quot; function found in https://gist.github.com/dgoguerra/7194777)
// Repository: https://github.com/TheRacc2/error_handler/
// Report issues here: https://github.com/TheRacc2/error_handler/issues

constexpr const char* name = &quot;Error Handler&quot;;

namespace helpers {
    // Convert bytes into readable sizes, E.G: 1024B -&gt; 1KB, 1024000B -&gt; 1MB
    //
    // call as such:
    // const char* sign = bytes_to_size(1024);
    const char* bytes_to_size(int bytes) {
        const char* units[] = { &quot;B&quot;, &quot;KB&quot;, &quot;MB&quot;, &quot;GB&quot;, &quot;TB&quot; };
        const int size = sizeof(units) / sizeof(units[0]) - 1;

        int i = 0;
        double outside_bytes = bytes;

        if (bytes &gt; 1024)
            for (; (bytes / 1024) &gt; 0 &amp;&amp; i &lt; size; i++, bytes /= 1024)
                outside_bytes = bytes / 1024;

        static char final[12];
        snprintf(final, 12, &quot;%.01f %s&quot;, outside_bytes, units[i]);
        return final;
    }
}

namespace error_handler {
    // Use this in a &quot;try, catch&quot; situation, the send to developers function doesn't need to be provided.
    // 
    // call as such:
    // try {
    //     your_function_or_code();
    // } CATCH(send_log_to_developer(ex))
    #define CATCH(send_to_developers_function) \
    catch (const std::exception&amp; ex) { \
        std::stringstream error; \
        error &lt;&lt; ex.what(); \
        error &lt;&lt; &quot;\n\n&quot;; \
        error &lt;&lt; &quot;Would you like to report this to the developers? Should your request be seen, this crash may be prevented.&quot;; \
        \
        if (MessageBoxA(nullptr, error.str().c_str(), name, MB_YESNO) == IDYES) { \
            send_to_developers_function; \
            MessageBoxA(nullptr, &quot;Thank you for reporting this!&quot;, name, MB_OK); \
        } \
    }

    // Call this when something needs to go wrong, formatted like &quot;printf&quot;. The second argument doesn't need to be provided.
    // 
    // call as such:
    // THROW(&quot;You didn't use this right! You used this like: %s&quot;, &quot;throw()&quot;)
    // 
    // output:
    // &quot;You didnt use this right! You used this like: throw()&quot;
    #define THROW(text, ...) \
    error_handler::throw_ex(text, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__);

    // You shouldn't manually call this function, as you have to provide the file, function and line.
    // Use the &quot;THROW&quot; macro instead.
    template &lt;typename... Args&gt;
    void throw_ex(const char* text, const char* file, const char* function, const int&amp; line, Args... args) {
        char message[128];
        snprintf(message, 128, text, args...);

        std::stringstream stream;
        stream &lt;&lt; message &lt;&lt; &quot;\n\n&quot;;

        PROCESS_MEMORY_COUNTERS_EX pmc;
        K32GetProcessMemoryInfo(GetCurrentProcess(), reinterpret_cast&lt;PROCESS_MEMORY_COUNTERS*&gt;(&amp;pmc), sizeof(pmc));

        stream &lt;&lt;
            &quot;{\n&quot; &lt;&lt;
            &quot;    File: \&quot;&quot; &lt;&lt; file &lt;&lt; &quot;\&quot;,\n&quot;
            &quot;    Function: \&quot;&quot; &lt;&lt; function &lt;&lt; &quot;\&quot;,\n&quot;
            &quot;    Line: &quot; &lt;&lt; line &lt;&lt; &quot;,\n&quot;
            &quot;    Memory: \&quot;&quot; &lt;&lt; helpers::bytes_to_size(pmc.PrivateUsage) &lt;&lt; &quot;\&quot;\n&quot;
            &quot;}&quot;
        ;

        throw std::exception(stream.str().c_str());
    }
}
Make bytes_to_size() reentrant
Since bytes_to_size returns a pointer to a static buffer, it is not reentrant. This will be a problem if you have multiple threads throwing exceptions at the same time. There are two solutions:

Have bytes_to_sign() take a pointer to a buffer as an argument, and have it write the text into that.
Have it return a std::string.

Avoid mixing C and C++ string formatting
It's very weird to see both snprintf() and std::stringstream used at the same time. Now you will get the worst of both worlds: type unsafety and possible truncation from snprintf(), and the overhead of dynamically growing memory from std::stringstream. Pick one and stick with it.
Consider using std::format
C++20 introduced std::format, which gives you type-safe string formatting. If you want to be compatible with older versions of C++, you can use the {fmt} library.
About units
The SI prefixes are powers of 1000, if you want to make sure people are not confused, use the IEC binary prefixes, so KiB, MiB, GiB and so on. I would also add all the defined prefixes, so up to YiB.
Consider allowing different exception types to be thrown
Your THROW() adds helpful information to the exception message, but the exception type is fixed to std::exception. That means that it is hard for any code that wants to catch exceptions to determine what exactly went wrong has very little information. Maybe you can add the exception type as an argument to the macro?
#define THROW(type, text, ...) \
    error_handler::throw_ex&lt;type&gt;(text, __FILE__, __FUNCTION__, __LINE__, __VA_ARGS__);

template &lt;typename Exception, typename... Args&gt;
void throw_ex(const char* text, ...) {
    ...
    throw Exception(stream.str().c_str());
}

Then it can be used like so:
if (value &gt; MAX_VALUE) {
    THROW(std::out_of_range, &quot;value %d larger than maximum %d&quot;, value, MAX_VALUE);
}

C++20 will avoid the need for macros
C++20 introduces std::source_location which avoids the need to use macros to get the filename and line number at the place where the exception is thrown.