Board index » cppbuilder » Re: char* literal strings are the devil's work...

Re: char* literal strings are the devil's work...


2007-11-07 10:52:12 PM
cppbuilder17
"Vladimir Grigoriev" < XXXX@XXXXX.COM >wrote in message
Quote

Well, another example

#include <iostream>

int * const & f( bool del_pointer = false )

{

static int *ip = new int( 10 );


if ( del_pointer )

{

delete ip;

ip = 0;

}


return ip;

}



int _tmain(int argc, _TCHAR* argv[])

{

int * const & ip = f();


...

std::cout << "*ip = " << *ip << std::endl; // Are you sure that this
code is safe?


...

return 0;

}



Vladimir Grigoriev


In my example a have a side effect that I can change a value in the memory
that pointed by ip. However I am able to make my example even more strict
int const * const & f( bool del_pointer = false )
...
int const * const & ip = f();
But in any case the code is not safe.
Vladimir Grigoriev
 
 

Re:Re: char* literal strings are the devil's work...

"Vladimir Grigoriev" < XXXX@XXXXX.COM >writes:
[snip]
That's arguably a contrived example. But how is that different from
this:
or
int * x = new int(10);
f(x);
*x = 999; // safe??
or
std::vector<int>vs(10);
f(vs);
vs[5] = 999; // safe??
Or
...
You just don't do that. It's a bad/terrible design. However, there's
nothing inherently wrong about returning a reference to an object from
a function, but if the function may later modify/delete it on
subsequent calls, it should at least be documented.
If you store a reference to *anything* and you must be careful when
you 1) reliquish control to code that you don't control, and 2) it can
access a modifiable reference to the object to which you're referring,
then YOU MUST ASSUME the object/iterator/pointer/reference/whatever
has been invalidated. Anything else is a logic bug.
--
Chris (TeamB);
 

Re:Re: char* literal strings are the devil's work...

"Vladimir Grigoriev" < XXXX@XXXXX.COM >writes:
Quote
int const * const & f( bool del_pointer = false )
int const * const & ip = f();

But in any case the code is not safe.
I agree it's not safe in the sense that it could be used incorrectly.
But it all boils down to simply keeping a cache to something that can
change, and assuming the cached value is still valid.
It is exactly the same problem as
std::vector<int>x;
x.push_back(1);
std::vector<int>::iterator i = x.begin();
x.push_back(2); // invalidates i
*i = 999; // !!! (Bad: may cause problems, but may not)
Of course, in a vector, push_back may or may not invalidate the
iterator, depending on the number of allocated values remaining before
it must resize, but without explicitly reserving a size and counting
the insertions, one is forced to code as if the iterator is
invalidated after every push_back.
It's not a bug with vector, it's a bug with how it's used.
--
Chris (TeamB);
 

{smallsort}

Re:Re: char* literal strings are the devil's work...

Vladimir Grigoriev wrote:
Quote

"Vladimir Grigoriev" < XXXX@XXXXX.COM >wrote in message
news:47330222$ XXXX@XXXXX.COM ...
>The effect of the code as if the static variable j is initialized
>at each call to g() though it is not true. The function can be
>changed as follows
>
>void g( const int & jj = 10 );
>
>
>
>Vladimir Grigoriev
>
>

Also I'd like to note that we can get two different results depending
on how we will call g() the first time.

For example

either

g( j );
j++
g();

or

g();
j++;
g();

Also I' d like to ask does Borland C++ issue a message for the second
case that a temporary value is used for initializing the reference?
Visual C++ 2005 EE does not issue any message!
You are not allowed to bind a temporary to a non-constant reference.
That should be an error, and is reported by our current compiler (and I
think Microsoft too).
A temporary CAN be bound to a const reference though, for example when
passing arguments to functions. The language also has rules that the
lifetime of a temporary bound to such a reference is extended to the
lifetime of the reference, so perhaps it is not as dangerous as you
think? We do not give a warning in these cases.
--
Alisdair Meredith
C++Builder Product Manager - CodeGear
 

Re:Re: char* literal strings are the devil's work...

Another interesting example of a code that C++ allows to exist
#include <iostream>
void g( const int & jj )
{
static int const & j = jj;
std::cout << "In g() j = " << j << std::endl;
}
int main(int argc, char* argv[])
{
int const * const & ip = f();
int j = 1;
g( j );
j++;
g( j );
return 0;
}
the oitput is
In g() j = 1
In g() j = 2
By the way should a static variable be initialized only once?
Vladimir Grigoriev
 

Re:Re: char* literal strings are the devil's work...

Sorry, the statement
int const * const & ip = f();
must be comment. It is from the previous example.
Vladimir Grigoriev
 

Re:Re: char* literal strings are the devil's work...

The effect of the code as if the static variable j is initialized at each
call to g() though it is not true.
The function can be changed as follows
void g( const int & jj = 10 );
Vladimir Grigoriev
 

Re:Re: char* literal strings are the devil's work...

"Vladimir Grigoriev" < XXXX@XXXXX.COM >wrote in message
Quote
The effect of the code as if the static variable j is initialized at each
call to g() though it is not true.
The function can be changed as follows

void g( const int & jj = 10 );



Vladimir Grigoriev


Also I'd like to note that we can get two different results depending on how
we will call g() the first time.
For example
either
g( j );
j++
g();
or
g();
j++;
g();
Also I' d like to ask does Borland C++ issue a message for the second case
that a temporary value is used for initializing the reference? Visual C++
2005 EE does not issue any message!
Vladimir Grigoriev
 

Re:Re: char* literal strings are the devil's work...

Thanks, Alisdairr.
It is obvious that C++ is too compound.
Vladimir Grigoriev
"Alisdair Meredith[CodeGear]" < XXXX@XXXXX.COM >wrote in message
Quote

You are not allowed to bind a temporary to a non-constant reference.
That should be an error, and is reported by our current compiler (and I
think Microsoft too).

A temporary CAN be bound to a const reference though, for example when
passing arguments to functions. The language also has rules that the
lifetime of a temporary bound to such a reference is extended to the
lifetime of the reference, so perhaps it is not as dangerous as you
think? We do not give a warning in these cases.

--
Alisdair Meredith
C++Builder Product Manager - CodeGear
 

Re:Re: char* literal strings are the devil's work...

Chris Uzdavinis (TeamB) wrote:
Quote
I think it is as dangerous as he thinks. The problem is that the
original reference is the only one that keeps the temporary alive. If
you pass a temporary to a function, and the function receives it in a
reference parameter, then the parameter is the one that keeps it
alive.

There is no "handing it off" to another reference to extend the
lifetime.
Yes - I probably should have been clearer.
We cannot warn at the call site that you are binding a temporary to a
const reference as that is a perfectly good and common thing to do.
The problem with this code is binding the reference-parameter to a
locally scoped static reference. That will almost never be a good
idea, and we might consider a warning in those cases.
Does anyone have a strong use-case for locally scoped static
*references*? Wondering if it is easier to simply warn on any use of
the construct (cheap!), or if we need to analyse the way it is used
(more expensive, so lower down priority list)? For instance, it would
be safe to initialize such a static reference with a global variable,
but is there any motivating idiom/pattern to do so, rather than use
that global variable directly?
--
Alisdair Meredith
C++Builder Product Manager - CodeGear
 

Re:Re: char* literal strings are the devil's work...

"Vladimir Grigoriev" < XXXX@XXXXX.COM >writes:
Quote
Another interesting example of a code that C++ allows to exist

#include <iostream>

void g( const int & jj )
{
static int const & j = jj;
std::cout << "In g() j = " << j << std::endl;
}
Wow, what a good example of an incredibly dangerous function! :)
It *cannot* possibly guarantee the lifetime of jj, so storing a
reference to it violates my rules. For any 2 calls to g(), user code
*must* run between the caching and the using of the reference. It
must therefore treat the reference as if it were invalidated before
the function exits, which negates the whole point of caching it in the
first place.
Quote
By the way should a static variable be initialized only once?
Not necessarily. Unless by "should" you mean "it would be nice." The
language currently does not define program behavior under multiple
threads, so you could construct the same static more than once if
several threads all hit that function at approximately the same time,
and it appears uninitialized to all of them.
Also, if the constructor of a static throws, then the next time the
function is invoked it will try to construct it again. (Not sure if
this technically counts as more than one initialization, but certainly
more than one attempt.)
--
Chris (TeamB);
 

Re:Re: char* literal strings are the devil's work...

"Alisdair Meredith[CodeGear]" < XXXX@XXXXX.COM >writes:
Quote
A temporary CAN be bound to a const reference though, for example
when passing arguments to functions. The language also has rules
that the lifetime of a temporary bound to such a reference is
extended to the lifetime of the reference, so perhaps it is not as
dangerous as you think? We do not give a warning in these cases.
I think it is as dangerous as he thinks. The problem is that the
original reference is the only one that keeps the temporary alive. If
you pass a temporary to a function, and the function receives it in a
reference parameter, then the parameter is the one that keeps it
alive.
There is no "handing it off" to another reference to extend the
lifetime. For example:
#include <iostream>
class MyClass
{
public:
MyClass()
{ std::cout << "Default ctor: " << this << std::endl;}
MyClass(MyClass const & rhs)
{ std::cout << "Copy ctor: " << this << std::endl;}
~MyClass()
{ std::cout << "Destructor: " << this << std::endl;}
};
void f(MyClass const & mc)
{
static MyClass const & staticmc(mc);
}
int main()
{
std::cout << "Before calling f()" << std::endl;
f(MyClass());
std::cout << "After calling f()" << std::endl;
}
Running this program, we see that the temporary is destroyed before
f() returns. That's because the parameter "mc" is the scope of its
lifetime, and the static member that copies the reference has no
bearing on that fact. It remains holding a dangling reference.
This problem shows itself frequently in classes that have members that
are references to const objects. If a constructor allows the user to
pass in an initial value for this reference, it can be a temporary and
the logic bug is that the constructor argument is the lifetime of the
temporary, and the member is left holding a dangling reference.
(That's why whenever I see a reference member to a const object, the
first thing I do is look at the constructors to see how it's
initialized. It should be initialized by the constructor without
using a parameter provided by the user, or it's not safe.) References
to non-const data do not suffer this problem and can be initialized by
user-provided values, since they must refer to objects with a longer
lifetime than the function invocation (though are still vulnerable to
it going out of scope prematurely. That's the caller's
responsibility, though.)
--
Chris (TeamB);
 

Re:Re: char* literal strings are the devil's work...

"Alisdair Meredith[CodeGear]" < XXXX@XXXXX.COM >writes:
Quote
Does anyone have a strong use-case for locally scoped static
*references*? Wondering if it is easier to simply warn on any use of
the construct (cheap!), or if we need to analyse the way it is used
(more expensive, so lower down priority list)? For instance, it would
be safe to initialize such a static reference with a global variable,
but is there any motivating idiom/pattern to do so, rather than use
that global variable directly?
I grepped through our source code and found zero instances of a static
local reference. (However, if the number were found to be nonzero, by
tomorrow it would be zero.)
--
Chris (TeamB);
 

Re:Re: char* literal strings are the devil's work...

Vladimir Grigoriev wrote:
Quote

"Alisdair Meredith[CodeGear]" < XXXX@XXXXX.COM >wrote in
message news:47331900$ XXXX@XXXXX.COM ...
>Does anyone have a strong use-case for locally scoped static
>*references*? Wondering if it is easier to simply warn on any use
>of the construct (cheap!), or if we need to analyse the way it is
>used (more expensive, so lower down priority list)? For instance,
>it would be safe to initialize such a static reference with a
>global variable, but is there any motivating idiom/pattern to do
>so, rather than use that global variable directly?
>
>-- Alisdair Meredith
>C++Builder Product Manager - CodeGear

I discussed only the original code (below) proposed by Roddy Pratt
and the consequences. At the very start I said that I consider it as
a breaking locality..

const String &ReturnAString()
{
static AnsiString s = "Hello";
return s;
}
This case is different, and generally quite safe (although you must
take care with threads)
The difference is that s is a static duratution object, not a
reference. You have a guarantee that the returned reference will be
valid at least from the first call until the main() function returns.
Potentially there are risks calling this function from destructors of
global objects, other objects with static duration, or from functions
registered with at_exit. However, there are a lot of subtle problems
for such code to worry about and this is far from the worst. Generally
you only perfom simple cleanup tasks in those cases.
--
Alisdair Meredith
C++Builder Product Manager - CodeGear
 

Re:Re: char* literal strings are the devil's work...

"Alisdair Meredith[CodeGear]" < XXXX@XXXXX.COM >wrote in message
Quote
Does anyone have a strong use-case for locally scoped static
*references*? Wondering if it is easier to simply warn on any use of
the construct (cheap!), or if we need to analyse the way it is used
(more expensive, so lower down priority list)? For instance, it would
be safe to initialize such a static reference with a global variable,
but is there any motivating idiom/pattern to do so, rather than use
that global variable directly?

--
Alisdair Meredith
C++Builder Product Manager - CodeGear
I discussed only the original code (below) proposed by Roddy Pratt and the
consequences. At the very start I said that I consider it as a breaking
locality..
const String &ReturnAString()
{
static AnsiString s = "Hello";
return s;
}