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 operation
extern int s_nallocs = 0;
#ifdef OVERLOAD_OPNEW
void* 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_t
I warned you it was horrid!
Step 3: #include the Loki.SafeFormat header
#include
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.
No comments:
Post a Comment