[lnkForumImage]
TotalShareware - Download Free Software

Confronta i prezzi di migliaia di prodotti.
Asp Forum
 Home | Login | Register | Search 


 

Forums >

comp.lang.c++

strings and NULL argument passing

sanjay

11/13/2008 7:01:00 AM

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
18 Answers

Fred Zwarts

11/13/2008 8:50:00 AM

0

"sanjay" <sanjay.raghani@gmail.com> wrote in message news:e662e2dd-af14-4245-b26e-0400ad134306@h23g2000prf.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.

Rolf Magnus

11/13/2008 10:20:00 AM

0

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.

sanjay

11/13/2008 10:37:00 AM

0

On Nov 13, 3:19 pm, Rolf Magnus <ramag...@t-online.de> 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?
>
> 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

Bo Persson

11/13/2008 5:07:00 PM

0

sanjay wrote:
> On Nov 13, 3:19 pm, Rolf Magnus <ramag...@t-online.de> 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?
>>
>> 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


Jeff Schwab

11/13/2008 7:05:00 PM

0

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;
}

Juha Nieminen

11/13/2008 7:37:00 PM

0

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()).

Jeff Schwab

11/13/2008 8:01:00 PM

0

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.

James Kanze

11/13/2008 9:37:00 PM

0

On Nov 13, 11:19 am, Rolf Magnus <ramag...@t-online.de> 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?

> 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:james.kanze@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

James Kanze

11/13/2008 9:46:00 PM

0

On Nov 13, 8:05 pm, Jeff Schwab <j...@schwabcenter.com> wrote:
> 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:james.kanze@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

James Kanze

11/13/2008 9:49:00 PM

0

On Nov 13, 9:00 pm, Jeff Schwab <j...@schwabcenter.com> wrote:
[...]
> > 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:james.kanze@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