By using this site, you agree to our updated Privacy Policy and our Terms of Use. Manage your Cookies Settings.
443,973 Members | 1,854 Online
Bytes IT Community
+ Ask a Question
Need help? Post your question and get tips & solutions from a community of 443,973 IT Pros & Developers. It's quick & easy.

strings and NULL argument passing

P: n/a
Hi,

I have a doubt about passing values to a function accepting string.

======================================
#include <iostream>
using namespace std;

int main()
{
void print(const string&);
print("hi");
print(NULL);
return 0;
}
void print(const string& s)
{
cout<<"s is "<<s<<endl;
}
======================================

The above program compiles successfully but fails at run time because
NULL is passed as argument in the second call to print.

Why doesn't the compiler give an error on passing of NULL value?

How could we check for such arguments in our program when we are using
strings?

Regards
Sanjay Raghani
Nov 13 '08 #1
Share this Question
Share on Google+
18 Replies


P: n/a
"sanjay" <sa************@gmail.comwrote in message news:e6**********************************@h23g2000 prf.googlegroups.com...
Hi,

I have a doubt about passing values to a function accepting string.

======================================
#include <iostream>
using namespace std;

int main()
{
void print(const string&);
print("hi");
print(NULL);
return 0;
}
void print(const string& s)
{
cout<<"s is "<<s<<endl;
}
======================================

The above program compiles successfully but fails at run time because
NULL is passed as argument in the second call to print.
I don't think so. Did you check it?
You can check it by printing the address of s in the function print.
I think that the error occurs before print is called.
The compiler tries to create a temporary string and passes the address of that string to print.
print ("hi"); is compiled as print (string ("hi"));.
Depending on you definition of NULL,
print (NULL); may be compiled as print (string (NULL));.
This may fail during the creation of the temporary, not in the function print.

Why doesn't the compiler give an error on passing of NULL value?

How could we check for such arguments in our program when we are using
strings?
You can't. The error occurs before your function starts.
You should check the argument earlier.
Nov 13 '08 #2

P: n/a
sanjay wrote:
======================================
#include <iostream>
using namespace std;

int main()
{
void print(const string&);
print("hi");
print(NULL);
return 0;
}
void print(const string& s)
{
cout<<"s is "<<s<<endl;
}
======================================

The above program compiles successfully but fails at run time because
NULL is passed as argument in the second call to print.

Why doesn't the compiler give an error on passing of NULL value?
Well, semantically, NULL can be converted to std::string, just as "hi" can.
How could the compiler know that NULL isn't a valid value?
How could we check for such arguments in our program when we are using
strings?
I don't see a way to do that, except for providing an overload of the
function for const char*. The problem is that the error already manifests
before print is even entered, in the constructor of std::string.
BTW: The implementation I use throws an exception of type std::logic_error
in this case, but I don't think that this is required by the standard.
Nov 13 '08 #3

P: n/a
On Nov 13, 3:19 pm, Rolf Magnus <ramag...@t-online.dewrote:
sanjay wrote:
======================================
#include <iostream>
using namespace std;
int main()
{
void print(const string&);
print("hi");
print(NULL);
return 0;
}
void print(const string& s)
{
cout<<"s is "<<s<<endl;
}
======================================
The above program compiles successfully but fails at run time because
NULL is passed as argument in the second call to print.
Why doesn't the compiler give an error on passing of NULL value?

Well, semantically, NULL can be converted to std::string, just as "hi" can.
How could the compiler know that NULL isn't a valid value?
How could we check for such arguments in our program when we are using
strings?

I don't see a way to do that, except for providing an overload of the
function for const char*. The problem is that the error already manifests
before print is even entered, in the constructor of std::string.
BTW: The implementation I use throws an exception of type std::logic_error
in this case, but I don't think that this is required by the standard.
Hi Rolf,

Thanks for the reply..

Can you elaborate bit more where exactly do you throw the exception
for handling such situation?

Regards
Sanjay Raghani
Nov 13 '08 #4

P: n/a
sanjay wrote:
On Nov 13, 3:19 pm, Rolf Magnus <ramag...@t-online.dewrote:
>sanjay wrote:
>>======================================
#include <iostream>
using namespace std;
>>int main()
{
void print(const string&);
print("hi");
print(NULL);
return 0;
}
void print(const string& s)
{
cout<<"s is "<<s<<endl;
}
======================================
>>The above program compiles successfully but fails at run time
because NULL is passed as argument in the second call to print.
>>Why doesn't the compiler give an error on passing of NULL value?

Well, semantically, NULL can be converted to std::string, just as
"hi" can. How could the compiler know that NULL isn't a valid
value?
>>How could we check for such arguments in our program when we are
using strings?

I don't see a way to do that, except for providing an overload of
the function for const char*. The problem is that the error
already manifests before print is even entered, in the constructor
of std::string.
BTW: The implementation I use throws an exception of type
std::logic_error in this case, but I don't think that this is
required by the standard.

Hi Rolf,

Thanks for the reply..

Can you elaborate bit more where exactly do you throw the exception
for handling such situation?
The constructor for std::string might do that.

For performance reasons, the std::string constructor taking a const
char* is NOT required to verify that the pointer is not null. If it
doesn't, bad things will happen when it tries to determine the length
of the (non-existant) char sequence pointed to.

Bo Persson
Nov 13 '08 #5

P: n/a
Rolf Magnus wrote:
sanjay wrote:
>#include <iostream>
using namespace std;

int main()
{
void print(const string&);
print("hi");
print(NULL);
return 0;
}
void print(const string& s)
{
cout<<"s is "<<s<<endl;
}
>The above program compiles successfully but fails at run time because
NULL is passed as argument in the second call to print.

Why doesn't the compiler give an error on passing of NULL value?
I don't see a way to do that, except for providing an overload of the
function for const char*. The problem is that the error already manifests
before print is even entered, in the constructor of std::string.
Overloading for char const* is a fine option. Using a string type that
performs the run-time check is also OK. The problem with either of
those approaches is that it imposes the run-time check, even for C-style
string literals (e.g. "hi") whose type cannot be null, but which decay
to the pointer type.

A function template can be defined to avoid the overhead of the check
for character array literals. Another, "catch-all" function template
can generate a compile-time error for any other argument type that could
otherwise be inadvertently converted to a string.

#include <iostream>
#include <string>

/* Print a standard string. */
void print(std::string const& s) {
std::cout << "s is " << s << '\n';
}

/* Print a C-style string literal. */
template<std::size_t Size>
void print(char const (&c_str)[Size]) {
print(std::string( c_str ));
}

/* Generate a compile time error for unacceptable types. */
template<typename String>
void print(String const& s) {
s.is_not_of_an_acceptable_string_type();
}

int main() {
print("hi"); // OK
// print(NULL); // Compile-time error.
return 0;
}
Nov 13 '08 #6

P: n/a
sanjay wrote:
Why doesn't the compiler give an error on passing of NULL value?
Short answer: Because std::string has a constructor which takes a
const char* as parameter, and a null pointer is a perfectly valid
pointer for it. Technically speaking the compiler cannot know if that
constructor handles a null pointer correctly or not, so it has no reason
to issue any error.
How could we check for such arguments in our program when we are using
strings?
Make a version of print() which takes a const char* as parameter and
performs the check. If a non-null pointer is passed, it simply calls the
print() taking a std::string as parameter, else it performs whatever
error termination you want (eg. assert()).
Nov 13 '08 #7

P: n/a
Juha Nieminen wrote:
sanjay wrote:
>Why doesn't the compiler give an error on passing of NULL value?

Short answer: Because std::string has a constructor which takes a
const char* as parameter, and a null pointer is a perfectly valid
pointer for it. Technically speaking the compiler cannot know if that
constructor handles a null pointer correctly or not, so it has no reason
to issue any error.
>How could we check for such arguments in our program when we are using
strings?

Make a version of print() which takes a const char* as parameter and
performs the check. If a non-null pointer is passed, it simply calls the
print() taking a std::string as parameter, else it performs whatever
error termination you want (eg. assert()).
"assert" means "I know this is true." It's enforced documentation, not
a general-purpose way to terminate a program.

Anyway, there's nothing here that implies termination. An exception can
be thrown, or the null pointer can just be accepted as an empty string,
or a C-style error code can be returned. In my experience, the
exception is usually the right way to go.
Nov 13 '08 #8

P: n/a
On Nov 13, 11:19*am, Rolf Magnus <ramag...@t-online.dewrote:
sanjay wrote:
======================================
#include <iostream>
using namespace std;
int main()
{
* * void print(const string&);
* * print("hi");
* * print(NULL);
* * return 0;
}
void print(const string& s)
{
*cout<<"s is "<<s<<endl;
}
======================================
The above program compiles successfully but fails at run
time because NULL is passed as argument in the second call
to print.
Why doesn't the compiler give an error on passing of NULL
value?
Well, semantically, NULL can be converted to std::string, just
as "hi" can. How could the compiler know that NULL isn't a
valid value?
Maybe we understand "semantically" differently, but I would say
that his problem is precisely that NULL can't be semantically
converted to an std::string. His code only compiles because it
is syntactically correct; he provides a value which can be
converted to type char const*, and that's all the compiler
checks. Semantically, of course, NULL isn't a string, in any
sense of the word, and it doesn't make sense to try to convert
it to a string. (In C, one might use it to indicate the absense
of a string; std::string doesn't support that concept, however.)
How could we check for such arguments in our program when we
are using strings?
I don't see a way to do that, except for providing an overload
of the function for const char*.
An implementation of std::string could easily cause it to
trigger a compiler error; just provide a private constructor
which takes some other type of pointer (which would make
std::string(NULL) ambiguous).
The problem is that the error already manifests before print
is even entered, in the constructor of std::string. BTW: The
implementation I use throws an exception of type
std::logic_error in this case, but I don't think that this is
required by the standard.
It's undefined behavior. With a good implementation of
std::sttring, it won't compile. (Regretfully, I don't know of
any implementations which are good by that definition:-).

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Nov 13 '08 #9

P: n/a
On Nov 13, 8:05*pm, Jeff Schwab <j...@schwabcenter.comwrote:
Rolf Magnus wrote:
sanjay wrote:
#include <iostream>
using namespace std;
[...]
Overloading for char const* is a fine option. *Using a string
type that performs the run-time check is also OK. *The problem
with either of those approaches is that it imposes the
run-time check, even for C-style string literals (e.g. "hi")
whose type cannot be null, but which decay to the pointer
type.
Compared to the rest of what the constructor has to do, I rather
suspect that the run-time cost of checking isn't measurable.
A function template can be defined to avoid the overhead of
the check for character array literals. *Another, "catch-all"
function template can generate a compile-time error for any
other argument type that could otherwise be inadvertently
converted to a string.
#include <iostream>
#include <string>
/* Print a standard string. */
void print(std::string const& s) {
* * *std::cout << "s is " << s << '\n';
}
/* 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.

Of course, this fails if the string literal was "a\0b", or
something of the sort. It also doesn't work (but nor does your
suggestion) when interfacing with C (where all you've got is a
char const*).
}
/* Generate a compile time error for unacceptable types. */
template<typename String>
void print(String const& s) {
* * *s.is_not_of_an_acceptable_string_type();
}
As pointed out earlier, this trick (with some adaption) could be
used directly in std::string.

It's not a panacea, however. You really do have to support
constructing strings from char const*, which can be a null
pointer, even if it isn't a literal. (Of course, it can also be
an invalid pointer, and there's no way you can check for that.)

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Nov 13 '08 #10

P: n/a
On Nov 13, 9:00*pm, Jeff Schwab <j...@schwabcenter.comwrote:
[...]
Make a version of print() which takes a const char* as
parameter and performs the check. If a non-null pointer is
passed, it simply calls the print() taking a std::string as
parameter, else it performs whatever error termination you
want (eg. assert()).
"assert" means "I know this is true." *It's enforced
documentation, not a general-purpose way to terminate a
program.
Anyway, there's nothing here that implies termination. *An
exception can be thrown, or the null pointer can just be
accepted as an empty string, or a C-style error code can be
returned. *In my experience, the exception is usually the
right way to go.
A null pointer is not a string (in the general sense), nor is it
something which can be converted into a string. If his
interface requires a string, then passing it a null pointer
should cause an assertion failure. If his interface supports
the idea of a nullable string (e.g. something you might get when
reading a VARCHAR field from a database), then it has to support
something more than std::string.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34

Nov 13 '08 #11

P: n/a
James Kanze wrote:
On Nov 13, 8:05 pm, Jeff Schwab <j...@schwabcenter.comwrote:
>Rolf Magnus wrote:
>>sanjay wrote:
[snipped code that (oops) initialized a std::string from NULL]
>Overloading for char const* is a fine option. Using a string
type that performs the run-time check is also OK. The problem
with either of those approaches is that it imposes the
run-time check, even for C-style string literals (e.g. "hi")
whose type cannot be null, but which decay to the pointer
type.

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. 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.
>/* 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.
Nice catch.
Of course, this fails if the string literal was "a\0b", or
something of the sort. It also doesn't work (but nor does your
suggestion) when interfacing with C (where all you've got is a
char const*).
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++.
>/* Generate a compile time error for unacceptable types. */
template<typename String>
void print(String const& s) {
s.is_not_of_an_acceptable_string_type();
}

As pointed out earlier, this trick (with some adaption) could be
used directly in std::string.

It's not a panacea, however.
It is only (intended to be) an optimization of the run-time code.
You really do have to support
constructing strings from char const*, which can be a null
pointer, even if it isn't a literal.
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.
(Of course, it can also be
an invalid pointer, and there's no way you can check for that.)
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.
Nov 13 '08 #12

P: n/a
James Kanze wrote:
On Nov 13, 9:00 pm, Jeff Schwab <j...@schwabcenter.comwrote:
[...]
>>Make a version of print() which takes a const char* as
parameter and performs the check. If a non-null pointer is
passed, it simply calls the print() taking a std::string as
parameter, else it performs whatever error termination you
want (eg. assert()).
>"assert" means "I know this is true." It's enforced
documentation, not a general-purpose way to terminate a
program.
A null pointer is not a string (in the general sense), nor is it
something which can be converted into a string.
Agreed. Passing null to a function expecting a std::string is an error.
If his
interface requires a string, then passing it a null pointer
should cause an assertion failure.
That does not follow. I consider it an abuse of assertions to use them
as detectors of contract violation. Assertions are often appropriate
for post-conditions, but rarely for pre-conditions.

Exceptions should, in my opinion, not be part of the interface
definition of functions; exceptions are best reserved, for
error-reporting, and that specifically includes run-time contract
violations. In the case at hand, std::invalid_argument (or a
derivative) seems obviously to be the best choice.
If his interface supports
the idea of a nullable string (e.g. something you might get when
reading a VARCHAR field from a database), then it has to support
something more than std::string.
Yes.
Nov 13 '08 #13

P: n/a
Jeff Schwab wrote:
James Kanze wrote:
>On Nov 13, 8:05 pm, Jeff Schwab <j...@schwabcenter.comwrote:
>>Rolf Magnus wrote:
sanjay wrote:

[snipped code that (oops) initialized a std::string from NULL]
>>Overloading for char const* is a fine option. Using a string
type that performs the run-time check is also OK. The problem
with either of those approaches is that it imposes the
run-time check, even for C-style string literals (e.g. "hi")
whose type cannot be null, but which decay to the pointer
type.

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?
Yes. The memory allocation alone would outweigh that simple check by far.
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.
The problem is that the C++ standard doesn't require it, so I guess some
library implementor could think that there is no point in doing such a
check, since the user can't rely on it anyway.

Nov 14 '08 #14

P: n/a
James Kanze wrote:

>Well, semantically, NULL can be converted to std::string, just
as "hi" can. How could the compiler know that NULL isn't a
valid value?

Maybe we understand "semantically" differently, but I would say
that his problem is precisely that NULL can't be semantically
converted to an std::string. His code only compiles because it
is syntactically correct;
Yes, I guess you're right.
How could we check for such arguments in our program when we
are using strings?
>I don't see a way to do that, except for providing an overload
of the function for const char*.

An implementation of std::string could easily cause it to
trigger a compiler error; just provide a private constructor
which takes some other type of pointer (which would make
std::string(NULL) ambiguous).
What I meant was that I see no way without altering standard headers.
>The problem is that the error already manifests before print
is even entered, in the constructor of std::string. BTW: The
implementation I use throws an exception of type
std::logic_error in this case, but I don't think that this is
required by the standard.

It's undefined behavior. With a good implementation of
std::sttring, it won't compile.
Would it actually be allowed by the standard to have an additional
constructor in std::string?
Nov 14 '08 #15

P: n/a
Rolf Magnus wrote:
Jeff Schwab wrote:
>James Kanze wrote:
>>On Nov 13, 8:05 pm, Jeff Schwab <j...@schwabcenter.comwrote:
Rolf Magnus wrote:
sanjay wrote:
[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?

Yes. The memory allocation alone would outweigh that simple check by far.
For small strings, there may not even be any dynamic allocation.
>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.

The problem is that the C++ standard doesn't require it,
I'm not sure that's a "problem." I agree that it would be a good idea
for std::string to detect null initializers, though.
so I guess some
library implementor could think that there is no point in doing such a
check, since the user can't rely on it anyway.
Mabye, but the typical implementor isn't setting out to provide the
"lowest common denominator;" in fact, the opposite tends to be true.
Each implementor typically provides features the others don't, both as a
matter of QoI, and to achieve lock-in.
Nov 14 '08 #16

P: n/a
On Nov 14, 12:02 am, Jeff Schwab <j...@schwabcenter.comwrote:
James Kanze wrote:
On Nov 13, 8:05 pm, Jeff Schwab <j...@schwabcenter.comwrote:
Rolf Magnus wrote:
sanjay wrote:
[snipped code that (oops) initialized a std::string from NULL]
Overloading for char const* is a fine option. Using a
string type that performs the run-time check is also OK.
The problem with either of those approaches is that it
imposes the run-time check, even for C-style string
literals (e.g. "hi") whose type cannot be null, but which
decay to the pointer type.
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.
Nice catch.
Except as I go on to point out, it doesn't work:-(.
Of course, this fails if the string literal was "a\0b", or
something of the sort. It also doesn't work (but nor does
your suggestion) when interfacing with C (where all you've
got is a char const*).
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.
/* Generate a compile time error for unacceptable types. */
template<typename String>
void print(String const& s) {
s.is_not_of_an_acceptable_string_type();
}
As pointed out earlier, this trick (with some adaption)
could be used directly in std::string.
It's not a panacea, however.
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.
You really do have to support constructing strings from char
const*, which can be a null pointer, even if it isn't a
literal.
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.
(Of course, it can also be an invalid pointer, and there's
no way you can check for that.)
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.)

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Nov 14 '08 #17

P: n/a
On Nov 14, 1:25 am, Rolf Magnus <ramag...@t-online.dewrote:
James Kanze wrote:
It's undefined behavior. With a good implementation of
std::string, it won't compile.
Would it actually be allowed by the standard to have an
additional constructor in std::string?
That's a good question. If it doesn't affect the overload
resolution of legal calls to the constructor, I think so, if
only under the as if rule. Thus, if as a library implementor, I
do something like:

namespace std {
template< ... >
class basic_string
{
// ...
private:
struct _Hidden {} ;
basic_string( int _Hidden::*,
Allocator const& = Allocator() ) ;
} ;

can a legal program detect the presence of the additional
constructor?

(One issue might be whether the following program is legal:

#include <string>

void
f( bool t )
{
if ( t ) {
std::string s(0) ;
}
}

int
main()
{
f( false ) ;
}

..)

In practice, on thinking about it, I'm not sure that it's worth
the effort. I only catches the case where you initialize with a
null pointer constant (0, NULL or the like), which is, one would
hope, pretty rare. You still need a run-time check (generally
provided directly by the hardware) in case of a variable which
happens to contain a null ponter.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Nov 14 '08 #18

P: n/a
On Nov 14, 12:14 am, Jeff Schwab <j...@schwabcenter.comwrote:
James Kanze wrote:
If his interface requires a string, then passing it a null
pointer should cause an assertion failure.
That does not follow. I consider it an abuse of assertions to
use them as detectors of contract violation. Assertions are
often appropriate for post-conditions, but rarely for
pre-conditions.
Assertions are useful for detecting programming errors.
Violation of a pre-condition is a programming error.
Exceptions should, in my opinion, not be part of the interface
definition of functions; exceptions are best reserved, for
error-reporting, and that specifically includes run-time contract
violations.
I agree with the middle clause: exceptions are best reserved for
error reporting. Which means that I disagree with the other two
parts: error reporting is a vital part of the interface
definition of a function, and run-time contract violations are
programming errors: "impossible" conditions (in a correct
program) not covered by the interface, and not reported as
"errors".
In the case at hand, std::invalid_argument (or a derivative)
seems obviously to be the best choice.
If the contract says so. The contract can specify many things:

-- The caller is not allowed to pass a null pointer. Doing so
violates the contract, which results in "undefined
behavior"---an assertion failure, unless performance
considerations deem otherwise.

-- The caller is allowed to pass a null pointer, and is
guaranteed a specific type of exception. I'd consider this
case fairly rare, but there are probably cases where it is
reasonable.

-- The caller is allowed to pass a null pointer, which the
function maps into a specific string, e.g. "" or
"<<NULL>>"", or whatever.

In general (and there are exceptions), a programming error
should result in the fastest and most abrupt termination of the
program as possible.

--
James Kanze (GABI Software) email:ja*********@gmail.com
Conseils en informatique orientée objet/
Beratung in objektorientierter Datenverarbeitung
9 place Sémard, 78210 St.-Cyr-l'École, France, +33 (0)1 30 23 00 34
Nov 14 '08 #19

This discussion thread is closed

Replies have been disabled for this discussion.