Showing posts with label "safe string". Show all posts
Showing posts with label "safe string". Show all posts

Tuesday, June 22, 2010

Dealing with "deprecated" std::copy()

Although the issue of Microsoft having chosen, by the agency of its so-called "safe string" library, to deprecate large parts of the standard library in general, and std::copy() in particular, is hardly new, there's still some confusion in the community about what to do about it, and how to work around it if that's what you choose to do.

If, as I do on occasion, you choose not to suppress the warnings (by ignoring 4996, or by defining _SCL_SECURE_NO_WARNINGS) but instead want to ignore specifically for std::copy(), you can do it as follows:

#include <stlsoft/algorithms/std/alt.hpp>
#include <stlsoft/internal/safestr.h>

#if defined(STLSOFT_USING_SAFE_STR_FUNCTIONS) && \
    defined(STLSOFT_COMPILER_IS_MSVC)
namespace std
{
  using ::stlsoft::std_copy;
# define copy    std_copy
}
#endif // compiler

By all means it's breaking the rules and introducing names into the std namespace. Nonetheless, we can sleep easy, since, hey, we didn't start the undermining of the standard!

The other problem is that, currently, stlsoft::std_copy() has no specialisations. It always uses long-hand iteration; no memcpy()-based block copying for random access iterators to POD types.

I hope you know me well enough by now for this to not need to be said. But it's important enough for me to have to say it explicitly, just in case: do not use this technique in library headers!

Friday, May 22, 2009

Safely abstracting use of strerror / strerror_s (part 1)

A recent bug report on the STLSoft SourceForge project site reported that the stlsoft::error_desc component did not use the new "safe string" library function strerror_s(). This was actually a surprise, because I thought I'd already taken care of that.

Since I hadn't, I decided that I should. The change in implementation to use strerror_s() (when in the presence of the "safe string" library) goes along the lines of the following



char buff[1001];
if(0 != ::strerror_s(buff,
STLSOFT_NUM_ELEMENTS(buff) - 1, errno))
{
buff[0] = '\0';
}
else
{
buff[STLSOFT_NUM_ELEMENTS(buff) - 1] = '\0';
}



The dumb part of the strerror_s() function is that it doesn't tell you how many characters were received. This means that you cannot rely on having elicited the full message unless ::strlen() over the returned string is less than (buffer size - 1). So, the above code could return a partial error string (although the likelihood of that is, of course, vanishingly small).

Instead, what I've done is used an auto_buffer to provide resizable storage, and then strerror_s() is called in a loop until either it fails, or no more storage can be allocated, or the length of the returned string is less than (buffer size - 1). The code looks like the following:



stlsoft::auto_buffer buff(128);

for(;;)
{
int n = ::strerror_s(&buff[0], buff.size() - 1, error);

buff[buff.size() - 1u] = '\0';

if(0 == n)
{
size_t cch = ::strlen(buff.data());

if(cch < buff.size() - 2u)
{
m_length = cch;
buff.resize(cch + 1u);
break;
}
}

if(!buff.resize(1u + buff.size() * 2u))
{
buff.resize(1u);
break;
}
}



There's another problem with the class template, but that'll have to wait until a later time to discuss ...