Much is often made of the supposedly still-born
Allocator concept in the standard library. However, one very good use of them is in tracking memory.
At the moment I'm preparing some analyses of
FastFormat's performance for an article I'm writing. One of the analyses conducting is to see how many memory allocations are involved in a formatting statement, for each of the comparison libraries. The standard way to achieve something like this is to overload the global operators new, as in:
// NOTE: this code is only valid for single-threaded operationextern int s_nallocs = 0;#ifdef OVERLOAD_OPNEWvoid* counting_malloc(size_t cb){ ++s_nallocs; return ::malloc(cb);}void counting_free(void* pv){ ::free(pv);}void* operator new(size_t cb){ return counting_malloc(cb);}void operator delete(void* pv){ counting_free(pv);}void* operator new[](size_t cb){ return counting_malloc(cb);}void operator delete[](void* pv){ counting_free(pv);}#endif /* OVERLOAD_OPNEW */Unfortunately, some components with some compilers - the exact permutations escape me at this point - don't go through operator new. This might be because they use a per-class operator new, or it might be because they use an allocator that doesn't use new. (Being under a publishing deadline, I didn't have the time - nor the inclination, if I'm honest - to find out which it was in each case.)
So, in order to get a fighting chance at an accurate depiction of how much memory each library is using I decided to force the issue, by requiring all the strings used to be an instance of the following specialisation, rather than
std::string:
typedef std::basic_string<
char
, std::char_traits<char>
,
stlsoft::new_allocator<char>
> string_t;Of course, things aren't ever that simple. Such a string type is not compatible with the
IOStreams default specialisations, requiring:
typedef std::basic_stringstream<
char
, std::char_traits<char>
,
stlsoft::new_allocator<char>
> stringstream_t;And the same thing applies for
Boost.Format, requiring:
typedef boost::basic_format<
char
, std::char_traits<char>
,
stlsoft::new_allocator<char>
> format_t;Unfortunately,
Loki's
SafeFormat library does not allow for the specification of allocators (or character traits, for that matter), and only uses
std::string. So a little horrifying trickery was required.
Step 1: Introduce
string_t into the
std namespace.
namespace std{ using ::string_t;}Now, if you've been paying attention this last decade or so you'll know that adding to the
std namespace is strictly controlled. I won't go over the rules now; you can look it up. Suffice to say that this action is not allowed.
Of course, needs must, and in this case there's no choice. Since it's just a perf-test program, it's ok. Just don't go using this tactic in production code.
Step 2: Make Loki (and any other code, for that matter) think that
std::string_t is
std::string.
#define string string_tI warned you it was horrid!
Step 3: #include the Loki.SafeFormat header
#include <loki/safeformat.h>
Obviously, this has to be done after steps 1 & 2, otherwise it won't work.
There were a few other dodgy things I had to do to get it to work with some really stupid compilers, but that'll have to wait until another day.