[snipped code that (oops) initialized a std::string from NULL]
Compared to the rest of what the constructor has to do, I
rather suspect that the run-time cost of checking isn't
measurable.
Which constructor? std::string? If the OP's std::string
isn't detecting null initializers, the decision apparently was
made by the implementor that the check was expensive enough to
avoid.
Or that it wasn't necessary, because the underlying system would
take care of it in the call to strlen (which dereferences the
pointer). If the system you're running on guarantees a core
dump or its equivalent in the case of a dereferenced null
pointer, you've got the check, even if you didn't want it
.
If the library is only designed for use on Windows and Unix
based systems, there's no point in doing more.
It may also be possible (and is, in the OP's case) to avoid
the std::string altogether, by working directly with the
c-style string.
That's a different issue; if profiling shows that he's spending
too much time constructing the string, such an alternative
should surely be considered.
/* Print a C-style string literal. */
template<std::size_t Size>
void print(char const (&c_str)[Size]) {
print(std::string( c_str ));
Or better yet:
print( std::string( c_str, Size - 1 ) ) ;
No need to count the characters if you already know how many
there are.
Except as I go on to point out, it doesn't work:-(.
The template cannot even be declared in a C header, so it is
clearly not meant to be a C-language interface function. I'm
not sure why you bring that up; I don't see it as relevant
here. If the function is to be callable from C, it also
cannot be overloaded for char const* and std::string, since it
must be declared extern "C". This is possible only in C++.
I didn't mean that the function itself would be called from C.
I was wondering about the more general issue---how you handle a
string in the form of a char const* which you got from a C
interface.
Of course, the simplest and the safest is just to convert it to
an std::string immediately. So you're right that my comments
really aren't that relevant. Except that such interfaces could
easily be a source of null pointers.
It is only (intended to be) an optimization of the run-time
code.
I thought that the original problem was to catch NULL pointers,
so that they wouldn't be used to create an std::string.
I don't have to support any such thing. Of course, if the
client writes something like print(std::string(0)), there's
not much I can do. There is a definite trade-off between
convenience and safety.
I was considering std::string, not this particular function.
I don't know of a fool-proof way, but you can sometimes detect
nonsense if you control the memory allocation. You can check
that the pointer value is within (or outside) some range. You
can also catch a bunch of bugs by stomping on all deallocated
memory with a magic byte pattern (I like 0xDeadBeef) and
checking for that byte pattern in the addressed memory.
Certainly. One's debugging operator new and operator delete
already take care of much of that. And potentially could do
more; under Solaris or Linux, for example, there are a number of
additional controls you could make on a pointer; something like:
bool
isValid( void const* userPtr )
{
extern int end ;
int stack ;
return (char*)userPtr < (char*)&end // static
|| (char*)userPtr > (char*)(&stack) // stack
|| MemoryChecker::isAllocated( userPtr ) ;
}
The MemoryChecker::isAllocated can be as simple or as complex as
you want. (For the simplest solution, just drop this term, and
replace "(char*)&end" with "(char*)sbrk()" in the first term.
But you should be able to do better than that with a specialized
function, even at reasonable cost.)
I believe that similar system dependent solutions are possible
on most systems. (And this solution doesn't work in a
multithreaded environment, where you have more than one stack.)
You could (and probably should?) make the isValid function a
template, and verify pointer alignment as well.
In the end, of course, it's probably simpler and more effective
to just use Purify, valgrind or something similar. (In the
past, I developed a lot of such solutions, because Purify was
the only existing product, and it's expensive. Today, I doubt
that I'd bother, but I continue to use my older tools because my
test harnesses are built around them.)