PHWinfo banniere

Titres
PORTAIL ANNUAIRE ARTICLES COMPARATEUR HÉBERGEURS DEVIS FORUMS RÉDUCTEUR D'URL
Précédent   PHWinfo > Autres forums > Forum Programmation & Conception > comp.lang.cplus > strings and NULL argument passing
S'inscrire FAQ Membres Recherche Messages du jour Marquer les forums comme lus
strings and NULL argument passing

Réponse
 
LinkBack Outils de la discussion
Vieux 13/11/2008, 08h00   #1
sanjay
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut strings and NULL argument passing

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
  Réponse avec citation
Vieux 13/11/2008, 09h50   #2
Fred Zwarts
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: strings and NULL argument passing

"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.
  Réponse avec citation
Vieux 13/11/2008, 11h19   #3
Rolf Magnus
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: strings and NULL argument passing

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.
  Réponse avec citation
Vieux 13/11/2008, 11h36   #4
sanjay
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: strings and NULL argument passing

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
  Réponse avec citation
Vieux 13/11/2008, 18h07   #5
Bo Persson
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: strings and NULL argument passing

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


  Réponse avec citation
Vieux 13/11/2008, 20h05   #6
Jeff Schwab
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: strings and NULL argument passing

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;
}
  Réponse avec citation
Vieux 13/11/2008, 20h36   #7
Juha Nieminen
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: strings and NULL argument passing

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()).
  Réponse avec citation
Vieux 13/11/2008, 20h36   #8
Juha Nieminen
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: strings and NULL argument passing

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()).
  Réponse avec citation
Vieux 13/11/2008, 21h00   #9
Jeff Schwab
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: strings and NULL argument passing

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.
  Réponse avec citation
Vieux 13/11/2008, 22h37   #10
James Kanze
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: strings and NULL argument passing

On Nov 13, 11:19am, 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
  Réponse avec citation
Vieux 13/11/2008, 22h46   #11
James Kanze
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: strings and NULL argument passing

On Nov 13, 8:05pm, 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
  Réponse avec citation
Vieux 13/11/2008, 22h48   #12
James Kanze
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: strings and NULL argument passing

On Nov 13, 9:00pm, 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

  Réponse avec citation
Vieux 13/11/2008, 22h48   #13
James Kanze
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: strings and NULL argument passing

On Nov 13, 9:00pm, 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

  Réponse avec citation
Vieux 14/11/2008, 00h02   #14
Jeff Schwab
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: strings and NULL argument passing

James Kanze wrote:
> On Nov 13, 8:05 pm, Jeff Schwab <j...@schwabcenter.com> wrote:
>> 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.
  Réponse avec citation
Vieux 14/11/2008, 00h14   #15
Jeff Schwab
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: strings and NULL argument passing

James Kanze wrote:
> 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.


> 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.
  Réponse avec citation
Vieux 14/11/2008, 01h14   #16
Rolf Magnus
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: strings and NULL argument passing

Jeff Schwab wrote:

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

  Réponse avec citation
Vieux 14/11/2008, 01h25   #17
Rolf Magnus
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: strings and NULL argument passing

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?


  Réponse avec citation
Vieux 14/11/2008, 01h30   #18
Jeff Schwab
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: strings and NULL argument passing

Rolf Magnus wrote:
> Jeff Schwab wrote:
>
>> James Kanze wrote:
>>> On Nov 13, 8:05 pm, Jeff Schwab <j...@schwabcenter.com> wrote:
>>>> 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.
  Réponse avec citation
Vieux 14/11/2008, 10h24   #19
James Kanze
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: strings and NULL argument passing

On Nov 14, 12:02 am, Jeff Schwab <j...@schwabcenter.com> wrote:
> James Kanze wrote:
> > On Nov 13, 8:05 pm, Jeff Schwab <j...@schwabcenter.com> wrote:
> >> 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: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
  Réponse avec citation
Vieux 14/11/2008, 10h33   #20
James Kanze
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: strings and NULL argument passing

On Nov 14, 1:25 am, Rolf Magnus <ramag...@t-online.de> wrote:
> 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: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
  Réponse avec citation
Vieux 14/11/2008, 10h41   #21
James Kanze
Aucun Avatar
 
Messages: n/a
Hébergeur:
Par défaut Re: strings and NULL argument passing

On Nov 14, 12:14 am, Jeff Schwab <j...@schwabcenter.com> wrote:
> 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: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
  Réponse avec citation
Réponse


Outils de la discussion

Règles de messages
Vous ne pouvez pas créer de nouvelles discussions
Vous ne pouvez pas envoyer des réponses
Vous ne pouvez pas envoyer des pièces jointes
Vous ne pouvez pas modifier vos messages

Les