Anything wrong with this function?

A

Andrey Tarasevich

Kai-Uwe Bux said:
...
Hm, you could use

void foo ( const int &, const int & );
void bar ( const int &, const int & );

and the compiler could detect that ints are small and not inheritable and
then decide to put copies on the stack that surely will remain unchanged. I
am not sure whether current C++ compilers would do this kind of
optimization or whether they will blindly push addresses onto the stack.
...

Without some non-trivial analysis of the implementation of the function,
the compiler has no other choice but to push addresses onto the stack.
It has to keep in mind the possibility of user using 'const_cast' in
order to cast away the constness and modify the object the reference is
referring to.
 
K

Kai-Uwe Bux

Andrey said:
Without some non-trivial analysis of the implementation of the function,
the compiler has no other choice but to push addresses onto the stack.
It has to keep in mind the possibility of user using 'const_cast' in
order to cast away the constness and modify the object the reference is
referring to.

True, but isn't that more or less the same data flow analysis through which
the compiler has to go anyway to issue errors for const violations? After
all, a const_cast<> just has the compiler shut up about something that
otherwise would require a diagnostic message. If so, I don't see a reason
for not using the information obtained in this analysis for optimization.


Best

Kai-Uwe Bux
 
A

Andrey Tarasevich

Kai-Uwe Bux said:
True, but isn't that more or less the same data flow analysis through which
the compiler has to go anyway to issue errors for const violations? After
all, a const_cast<> just has the compiler shut up about something that
otherwise would require a diagnostic message. If so, I don't see a reason
for not using the information obtained in this analysis for optimization.
...

I don't see how it could be the same. Errors for const violations are
always based on immediate compile-time context limited to a single
expression. There's no serious "data flow" involved.

But in order to determine whether a 'const-reference' parameter can be
replaced with a 'value' parameter the compiler has to analyze the entire
function in order to find out if there are any modification attempts
there. It is safe to say that an exhaustive analysis is simply
impossible in general case, since it can easily depend on run-time context.
 
G

Grizlyk

Kai-Uwe Bux said:
True, but isn't that more or less the same data flow analysis through
which
the compiler has to go anyway to issue errors for const violations? After
all, a const_cast<> just has the compiler shut up about something that
otherwise would require a diagnostic message. If so, I don't see a reason
for not using the information obtained in this analysis for optimization.


When you are use const_cast<> you must be shure, that variable after cast is
allowed to write (not placed in ROM or readonly segment), and compiler can
and I think must to control it. In other words, compiler can be "shut up-ed"
if only const modifier is provide logic protection, not hadware protection.

For most cases you no need to use memory of alredy unnecessary const
variable to store new data, it is enough other memory: registers and stack.
 
A

Andrey Tarasevich

Kai-Uwe Bux said:

There's also another detail that might prevent compiler from replacing '
"const-reference' parameter with a 'value' parameter. A reference that
is direct-bound has to preserve the address-identity of the object. For
example, in the following code

const int a;

void foo(const int& i) {
if (&i == a) {
...
}
}

foo(a);

the condition in the 'if' must be satisfied. This will not work
correctly if the compiler looses the attachment between the parameter
and the actual argument.
 
K

Kai-Uwe Bux

Andrey said:
I don't see how it could be the same. Errors for const violations are
always based on immediate compile-time context limited to a single
expression. There's no serious "data flow" involved.

But in order to determine whether a 'const-reference' parameter can be
replaced with a 'value' parameter the compiler has to analyze the entire
function in order to find out if there are any modification attempts
there.

Exactly. But I think, the compiler has to check the very same thing anyway.
Consider

void f ( int const & x ) {
...
x = 3;
...
}

I thaught, that supposed to raise an error. So, the compiler has to go
through the body an flag each possible modifications of x as a const
violation.

the said:
It is safe to say that an exhaustive analysis is simply
impossible in general case, since it can easily depend on run-time
context.

Huh?


Best

Kai-Uwe Bux
 
K

Kai-Uwe Bux

Grizlyk said:
When you are use const_cast<> you must be shure, that variable after cast
is allowed to write (not placed in ROM or readonly segment), and compiler
can and I think must to control it. In other words, compiler can be "shut
up-ed" if only const modifier is provide logic protection, not hadware
protection.

The standard says otherwise. See [7.1.5.1/4]:

Except that any class member declared mutable (7.1.1) can be modified, any
attempt to modify a const object during its lifetime (3.8) results in
undefined behavior.

Since it is undefined behavior, no diagnostic is required.

Also, the standard does not say anything about ROM segments or hardware
protection.


[snip]


Best

Kai-Uwe Bux
 
J

Jerry Coffin

Came across some code summarized as follows:

char const* MyClass::errToText(int err) const
{
switch (err)
{
case 0: return "No error";
case 1: return "Not enough";
case 2: return "Too much";
default: return "Unknown error";
}
}

Someone else said it was fine - was she right?

If you mean "is is all right for a function to return the address of a
string literal?", then the answer is yes. A string literal has static
storage duration.

Personally, I'd probably do something more like:

char const *MyClass::errToText(unsigned err) const {
char *names[]={
"No Error", "Not Enough", "Too Much", "Unknown Error"
};

return names[std::min(err, 3)];
}

But that may just indicate my distaste for switch statements.
 
K

Kai-Uwe Bux

Andrey said:
There's also another detail that might prevent compiler from replacing '
"const-reference' parameter with a 'value' parameter. A reference that
is direct-bound has to preserve the address-identity of the object. For
example, in the following code

const int a;

void foo(const int& i) {
if (&i == a) {
...
}
}

foo(a);

the condition in the 'if' must be satisfied. This will not work
correctly if the compiler looses the attachment between the parameter
and the actual argument.

I think, that still could be taken care of by an analysis of the function
body.


Unfortunately, there is yet another problem preventing compilers from doing
this optimization: namely that this operation affects the code used to do
the function call; i.e., before control is transfered to the function
parameters are pushed onto the stack and what is pushed is exactly at issue
with this optimization. But this might be a problem when you use function
pointers. E.g., suppose you have a function

void f ( const int & x );

for which the compiler wants to do this optimization. How should the
compiler treat code like

void (*g) ( const int & );
g = &f;
int i = 0;
g(i);

Now, the compiler simply cannot predict whether f is going to be used like
that without knowing the program as a whole. In particular, it might be
impossible to do this in the presence of separate compilation.


Oh well, too bad.


Best

Kai-Uwe Bux
 
A

Andrey Tarasevich

Kai-Uwe Bux said:
Exactly. But I think, the compiler has to check the very same thing anyway.
Consider

void f ( int const & x ) {
...
x = 3;
...
}

I thaught, that supposed to raise an error. So, the compiler has to go
through the body an flag each possible modifications of x as a const
violation.

Well, this alone already hints at the formally different "structure" of
these two issues. A simple const-violation is a purely _local_ issue:
it's just a report-and-forget type of thing. But for the purpose of
parameter replacement, this information has to be collected and turned
into a function-wide (i.e. more _global_) property attached to each
relevant parameter.

Yes, I understand that this alone is still very easy to do. But that is
not all.
I think, if the body compiles cleanly, and there is no const_cast<>, the
compiler can push the value of x onto the stack instead of its address.

Not enough. The compiler also has to watch for things like this

void f ( int const & x ) {
...
int const& y = x;
...
const_cast<int&>(y) = 3
...
}

It could be something like

void f ( int const & x ) {
...
int a = 0;
int const& y = rand() % 2 == 0 ? x : a;
...
const_cast<int&>(y) = 3
...
}

Of course, in this case the compiler can (or has to) follow the "better
safe the sorry" route and assume that 'y' can be bound to 'x'.

Finally, in addition ot the address problem mentioned in my neighboring
message, there's also the following issue. It is possible that the
parameters of the following function

void foo(const int& a, int& b)

will be direct-bound to the same referent at the time of call (that is
also run-time context, BTW)

int x = 0;
foo(x, x);

In that case inside the function the following relationships shall hold

1) &a == &b shall be true
2) if we do 'b = 5' then a == 5 shall be true

Once the reference parameters are replaced with values, these
requirements will become hard to satisfy, even with a very smart
compiler. Because they depend on the run-time context which is external
to the function itself.
 
K

Kai-Uwe Bux

Andrey Tarasevich wrote:
[about replacing const & parameters with values]
Finally, in addition ot the address problem mentioned in my neighboring
message, there's also the following issue. It is possible that the
parameters of the following function

void foo(const int& a, int& b)

will be direct-bound to the same referent at the time of call (that is
also run-time context, BTW)

int x = 0;
foo(x, x);

In that case inside the function the following relationships shall hold

1) &a == &b shall be true
2) if we do 'b = 5' then a == 5 shall be true

True, but if you do b = 5 the compiler has to refrain from optimizing const
& parameters to values anyway. So I don't think this introduces an
additional difficulty.
Once the reference parameters are replaced with values, these
requirements will become hard to satisfy, even with a very smart
compiler. Because they depend on the run-time context which is external
to the function itself.

I still think it wouldn't be too hard to make a static conservative analysis
of the function body. If the compiler cannot prove in a straight forward
way that the values remain unmodified and the addresses are not needed,
then the optimization is forbidden.


Best

Kai-Uwe Bux
 
G

Grizlyk

Kai-Uwe Bux said:
Grizlyk said:
When you are use const_cast<> you must be shure, that variable after cast
is allowed to write (not placed in ROM or readonly segment), and compiler
can and I think must to control it. In other words, compiler can be "shut
up-ed" if only const modifier is provide logic protection, not hadware
protection.

The standard says otherwise. See [7.1.5.1/4]:

Except that any class member declared mutable (7.1.1) can be modified,
any attempt to modify a const object during its lifetime (3.8) results in
undefined behavior.

Since it is undefined behavior, no diagnostic is required.

I think the words "attempt to modify a const object during its lifetime"
means what if you are accessing to object violating constness, for example
with the help of reinterpret_cast<>. But in the case of const_cast<>
compiler "know" what you are doing and can trace you actions, so compiler
_must_ check - is the object placed in writable memory or not - if not or
not sure, it can make temporary or print error/warning.

We are speaking about parameter passed by value. I do not insist on using
const parameter as non-const with the help of const_cast<> (the usage is
safe for example, for C-linkage functions).

Also, the standard does not say anything about ROM segments or hardware
protection.

I think it is wrong standard behaviour, because most systems has readonly &
writable data segments, and it is good, if programmer will get control for
placement of data. Systems, that have no the segments, can silently ignore
the explicit declared segments because the segments just more limited than
ordinary memory.

My example with my_auto_ptr shows, that we can not avoid dangers of
unspecified memory usage even by creating simple memory wrapper, to avoid
the dangers we need exclude usage of new/new[] operators or extend template
syntax.

But evev if C++ will can do it in general avoiding new/new[] operators looks
like bad idea.
 
K

Kai-Uwe Bux

Grizlyk said:
Kai-Uwe Bux said:
Grizlyk said:
Kai-Uwe Bux wrote:

After all, a const_cast<> just has the compiler shut up about something
that otherwise would require a diagnostic message.

When you are use const_cast<> you must be shure, that variable after
cast is allowed to write (not placed in ROM or readonly segment), and
compiler can and I think must to control it. In other words, compiler
can be "shut up-ed" if only const modifier is provide logic protection,
not hadware protection.

The standard says otherwise. See [7.1.5.1/4]:

Except that any class member declared mutable (7.1.1) can be modified,
any attempt to modify a const object during its lifetime (3.8) results
in undefined behavior.

Since it is undefined behavior, no diagnostic is required.

I think the words "attempt to modify a const object during its lifetime"
means what if you are accessing to object violating constness, for example
with the help of reinterpret_cast<>. But in the case of const_cast<>
compiler "know" what you are doing and can trace you actions, so compiler
_must_ check - is the object placed in writable memory or not - if not or
not sure, it can make temporary or print error/warning.

You have it slightly backwards: the compiler will issue an error if a
reinterpret_cast<> is used to cast away constness. From the standard:

[5.2.10/2] The reinterpret_cast operator shall not cast away constness...

On the other hand, const_cast<> has the only purpose of telling the compiler
not to bitch when an object is modified that, within the local context, is
considered const (e.g., a parameter passed as const &). If the actual
object was declared const to begin with, you get undefined behavior.
We are speaking about parameter passed by value. I do not insist on using
const parameter as non-const with the help of const_cast<> (the usage is
safe for example, for C-linkage functions).

But if you say, that compiler do not support safe const_cast<> in general,
it is bad.

By and large, there is no such thing as a "safe cast" in C++. Whenever a
cast pops up, chances are you doing something whicked. That's why it is
good that C++ casts are grepable, so that you can audit the code.
I think it is wrong standard behaviour, because most systems has readonly
& writable data segments, and it is good, if programmer will get control
for placement of data. Systems, that have no the segments, can silently
ignore the explicit declared segments because the segments just more
limited than ordinary memory.

I think the idea is that those provision vary from platform to platform and
that it is not a good idea for the standard to be prescriptive in this
area.

My example with my_auto_ptr shows, that we can not avoid dangers of
unspecified memory usage even by creating simple memory wrapper, to avoid
the dangers we need exclude usage of new/new[] operators or extend
template syntax.

But evev if C++ will can do it in general avoiding new/new[] operators
looks like bad idea.

Here, I don't understand what you mean.


Best

Kai-Uwe Bux
 
G

Grizlyk

Kai-Uwe Bux said:
You have it slightly backwards: the compiler will issue an error if a
reinterpret_cast<> is used to cast away constness. From the standard:
I want to say, that you can get access to const data over pointer to
non-const data, and C++ will not try to protect you from the actions. You
By and large, there is no such thing as a "safe cast" in C++. Whenever a
cast pops up, chances are you doing something whicked. That's why it is
good that C++ casts are grepable, so that you can audit the code.

We are not speaking about "safe cast", but writing to readonly memory and
other actions like this is wrong idea, especialy when C++ can easy test it.
I think the idea is that those provision vary from platform to platform
and
that it is not a good idea for the standard to be prescriptive in this
area.

Describe at least one real or theoretical system, where decaring data as
readonly will be error.
My example with my_auto_ptr shows, that we can not avoid dangers of
unspecified memory usage even by creating simple memory wrapper, to avoid
the dangers we need exclude usage of new/new[] operators or extend
template syntax.

But even if C++ will can do it in general avoiding new/new[] operators
looks like bad idea.

Here, I don't understand what you mean.

Search "// C-style danger here - we can pass not only new Tobj" string in
"auto_ptr" topic.
 
G

Grizlyk

Andrey said:
It has to keep in mind the possibility of user using 'const_cast' in
order to cast away the constness and modify the object the reference is
referring to.

Compiler at compile time can link any variable with some attributes, as
"accsessed", "written" and so on. Based on values of the attributes,
compiler can detect "unused variables", for example and can detect
const_cast<> applyed to variable.
 
K

Kai-Uwe Bux

Grizlyk said:
I want to say, that you can get access to const data over pointer to
non-const data, and C++ will not try to protect you from the actions. You


We are not speaking about "safe cast", but writing to readonly memory and
other actions like this is wrong idea, especialy when C++ can easy test
it.

It can test this only at run time. The standard, however, does not require
the implementations to perform this test because it does not want to
mandate this overhead.
Describe at least one real or theoretical system, where decaring data as
readonly will be error.

I cannot relate this statement to the talk about ROM and hardware from
before. If you just want to indicate that the compiler is free to put a
piece of data into a read-only segment of memory, you can already use
const. I thaught, you had something in mind that would, say, specify an
explicit address range. Then, the compiler is not required to know whether
the data is read-only.

[snip]

Kai-Uwe Bux
 
G

Grizlyk

Kai-Uwe Bux said:
It can test this only at run time. The standard, however, does not require
the implementations to perform this test because it does not want to
mandate this overhead.

No, test can be done at compile time, because cast between
"T heap*" and "T readonly code*" must be disabled.
I cannot relate this statement to the talk about ROM and hardware from
before. If you just want to indicate that the compiler is free to put a
piece of data into a read-only segment of memory, you can already use
const.

-1-

Better to say, that compiler is ignoring existance such of special and
useful memory areas, as some kind of readonly/writeonly memory. The using
unqualified C-style pointers can lead to runtime errors, when pointers from
readonly segments will be mixed with ordinary memory.

I am repeating, that to avoid the possible errors:
1. we can refuse using C-style pointers without explicit memory qualifiers
and new/new[]
2. or new/new[] must return user defined classes instead of C-style pointers
3. or we must invent user-defined operators instead of new/new[] to create
dynamic data

-2-
Maybe explicit memory specifiers can be useful as itself (without concerning
dynamic data problems), they do not disturb systems allocating only one
segment, but allow to other systems manualy place data into concrete
segments, "switching on" hardware memory control.

Let you have (it is real sysytem)

- code1 code segment as can not rw
- code2 code segment as readonly
- data1 data segment as readonly
- data2 data segment as rw
- data3 data segment as writeonly (for your process)

where "Tobj" and "ptr" will be placed in following example?

const Tobj *const ptr;
const Tobj * ptr;
Tobj *const ptr;
Tobj * ptr;

but with explicit memory specification

default segments:

data1 - for const T
data2 - for T

// ! not C++

//Tobj -> code2
//ptr -> data2
code Tobj *ptr; //error code2 is const
code const Tobj *ptr;

//Tobj -> data1
//ptr -> data2
const Tobj *ptr;
const_cast<Tobj&>(ptr); //error - data1 readonly
//or compiler will remove Tobj into data2 and print warning
//or compiler will make temporary and print warning

//Tobj -> data2
//ptr -> data2
writable const Tobj *ptr; //software constness
const_cast<Tobj&>(ptr); //ok

//Tobj -> data2
//ptr -> data2
const Tobj dseg * ptr; //software constness for Tobj

//Tobj -> data2
//ptr -> data1
Tobj *const ptr;

//Tobj -> data2
//ptr -> data2
Tobj *const writable ptr; //software constness for ptr

//Tobj -> data2
//ptr -> code2
Tobj *code ptr; //error - code is readonly
Tobj *const code writeable ptr; //error - no writeable code seg
Tobj *const code ptr;

We need new keywords "writeable, writeonly, readable, readonly, dseg, code,
heap, heap[] "

I know, it just "first step view" on the problem, maybe someone see how to
reduce the keywords set, but remain all we need?
 
G

Grizlyk

Grizlyk said:
//Tobj -> data2
//ptr -> data2
const Tobj dseg * ptr; //software constness for Tobj

Sorry, misplaced lines (forget delete them from draft). "dseg" is not the
same as writable.

--
Maksim A Polyanin
 
K

Kai-Uwe Bux

Grizlyk said:
No, test can be done at compile time, because cast between
"T heap*" and "T readonly code*" must be disabled.

You are clearly not talking about C++ here, you are talking about some
hypothetical language. About that, I have nothing to say.

I cannot relate this statement to the talk about ROM and hardware from
before. If you just want to indicate that the compiler is free to put a
piece of data into a read-only segment of memory, you can already use
const.

-1-

Better to say, that compiler is ignoring existance such of special and
useful memory areas, as some kind of readonly/writeonly memory. The using
unqualified C-style pointers can lead to runtime errors, when pointers
from readonly segments will be mixed with ordinary memory.

I am repeating, that to avoid the possible errors:
1. we can refuse using C-style pointers without explicit memory qualifiers
and new/new[]
2. or new/new[] must return user defined classes instead of C-style
pointers 3. or we must invent user-defined operators instead of new/new[]
to create dynamic data

-2-
Maybe explicit memory specifiers can be useful as itself (without
concerning dynamic data problems), they do not disturb systems allocating
only one segment, but allow to other systems manualy place data into
concrete segments, "switching on" hardware memory control.

Let you have (it is real sysytem)

- code1 code segment as can not rw
- code2 code segment as readonly
- data1 data segment as readonly
- data2 data segment as rw
- data3 data segment as writeonly (for your process)

where "Tobj" and "ptr" will be placed in following example?

const Tobj *const ptr;
const Tobj * ptr;
Tobj *const ptr;
Tobj * ptr;

but with explicit memory specification

default segments:

data1 - for const T
data2 - for T

// ! not C++

//Tobj -> code2
//ptr -> data2
code Tobj *ptr; //error code2 is const
code const Tobj *ptr;

//Tobj -> data1
//ptr -> data2
const Tobj *ptr;
const_cast<Tobj&>(ptr); //error - data1 readonly
//or compiler will remove Tobj into data2 and print warning
//or compiler will make temporary and print warning

//Tobj -> data2
//ptr -> data2
writable const Tobj *ptr; //software constness
const_cast<Tobj&>(ptr); //ok

//Tobj -> data2
//ptr -> data2
const Tobj dseg * ptr; //software constness for Tobj

//Tobj -> data2
//ptr -> data1
Tobj *const ptr;

//Tobj -> data2
//ptr -> data2
Tobj *const writable ptr; //software constness for ptr

//Tobj -> data2
//ptr -> code2
Tobj *code ptr; //error - code is
readonly
Tobj *const code writeable ptr; //error - no writeable code seg
Tobj *const code ptr;

We need new keywords "writeable, writeonly, readable, readonly, dseg,
code, heap, heap[] "

I never felt this need. And quite honestly, I don't think those keywords
solve any problem that comes up in my codebase.

I know, it just "first step view" on the problem, maybe someone see how to
reduce the keywords set, but remain all we need?

Maybe you should take these issues over to comp.std.c++. That is where
proposals to change the standard are highly on-topic and will get better
responses.


Best

Kai-Uwe Bux
 
G

Grizlyk

Kai-Uwe Bux said:
You are clearly not talking about C++ here, you are talking
about some hypothetical language. About that, I have nothing
to say.

I want to say, that i think that compiler can do static type checking, and
const_cast<> allow to compiler to do static type checking.

If you can not take my explanation, make your own explanation, but it must
be explanaiton, something lager than "it can not because can not".
Maybe you should take these issues over to comp.std.c++. That is where
proposals to change the standard are highly on-topic and will get better
responses.


I think pepole who are not close to standard can have own opinion about C++
memory usage :).
 

Ask a Question

Want to reply to this thread or ask your own question?

You'll need to choose a username for the site, which only take a couple of moments. After that, you can post your question and our members will help you out.

Ask a Question

Members online

No members online now.

Forum statistics

Threads
473,813
Messages
2,569,696
Members
45,480
Latest member
CrazyMember

Latest Threads

Top