Topic: const and global scope


Author: remove.haberg@matematik.su.se (Hans Aberg)
Date: Sat, 30 Jun 2001 23:05:32 GMT
Raw View
In article <t3u20zxssa.fsf@watts.itp.tuwien.ac.at>, Christopher Eltschka
<celtschk@web.de> wrote:
>> >A self-contained function could be denoted by adding the
>> >funciton name to the mutable clause:

>> >int count() mutable(count);
>> >  // may give different results on different calls
...
>> However, if one really needs to indicate that a function is mutable,
>> without further specifications, why not simply:
>>     int count() mutable;

>Ah, NOW I see: You obviously completely erased from your memory what I
>wrote in earlier postings (especially what I've used the term
>"self-contained" for!

No, I merely suggested that a function with self-contained mutations
should be written as
    int count() mutable;
instead of
    int count() mutable(count);
as it is unnecessary to repeat the function name twice. Here,
"self-contained mutations" would mean that different calls with the same
input may produce different results at different times, but that the
states that are manipulated are only shared between different copies of
count() and no other functions. Thus there is no difference between
whether these states are defined within the function body or external,
only that no other function instantiation may manipulate these states.

>a function which can mutate _anything_ (except if otherwise
>forbidden) would be specified as

>      void foo();

>as is today.

Right.

>That is, the compiler should not reject the following code:

>void foo(int& i) mutable()
>  // the lvalue referenced by i is implicitly assumed to be changed
>  // despite the function being declared mutable() - i.e. the
>  // referenced lvalue - which may be local - is implicitly added
>  // to the mutable clause
>{
>  ++i; // no problem
>}

I think this should not be considered to be a pure function as it mutates
the state of its arguments. For example, if h, g are pure functions, then
f(h(), g()) is well-defined in C/C++, but f(++i, ++i) or f(foo(i), foo(i))
are not as they mutate the arguments.

So I think that in the above case one would have to write
  void foo(int& i) mutable(i) { ++i; }
Compare this with
  int foo(int& i) mutable() { int n = i; ++n; return n; }

>> If a sequence of operations each are considered to be pure, then the
>> combined action is also considered to be pure. If a sequence of operations
>> contains one operation which is not explicitly known as pure, then the
>> combined action is to be considered as mutable, even though in reality it
>> may happen that it is pure.

>Yes, certainly. But it must be explicitly defined what is pure and
>what not - the legality of a program may not depend on the abilities
>of the optimizer.

Right.

>> So if one has a call by reference, it is possible to detect if the
>> function is pure, if all the actions is performs with the argument are
>> marked as pure.

>But then, you wouldn't have needed the reference at all ;-) A function
>taking a non-const reference is not pure in probably at least 99% of
>all cases.

It depends: I think this must be considered as programming styles. Also,
it is possible to override the "const" by the use of "mutable" elements,
and it is not possible for the compiler to know whether this should
considered as a mutation or is a ref count that should be not.

In the absence of a sufficiently exact analysis, one would have to accept
as a matter of programming styles.

>> Then one would be able to write better libraries, because the kind of
>> issue "to not design the library in this or other way because the compiler
>> does not know how to optimize away unused feature" that we have seen
>> discussed here would go away.

>Note that there is already one place in the standard where such an
>assumption is implicitly made: The rules for optimizing away copies of
>temporaries are a special case of just assuming that copy constructors
>are pure (it the new object constructed is considered the function
>result of the constructor).

>If we could _declare_ copy constructors pure, then this rule could be
>just removed, and we could rely on the same optimization for true pure
>constructors, but on the program behaving correctly even in cases
>where the copy constructor deliberately breaks that semantics (and
>would not be declared as pure, of course).

Right. This would be better. For upwards compatibility, the copy
constructor might be assumed to have a mutable() declaration unless
otherwise is explicitly stated.

By the way, I think that this idea might be important in the case of
threading, because if a function is pure execution in different threads
will not produce clashes. Further, if one know what is mutated, that could
be used in determine in what should be locked, namely what is within the
"mutable(...)" clause.

  Hans Aberg      * Anti-spam: remove "remove." from email address.
                  * Email: Hans Aberg <remove.haberg@member.ams.org>
                  * Home Page: <http://www.matematik.su.se/~haberg/>
                  * AMS member listing: <http://www.ams.org/cml/>

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Christopher Eltschka <celtschk@web.de>
Date: Mon, 2 Jul 2001 20:02:31 GMT
Raw View
remove.haberg@matematik.su.se (Hans Aberg) writes:

> In article <t3u20zxssa.fsf@watts.itp.tuwien.ac.at>, Christopher Eltschka
> <celtschk@web.de> wrote:
> >> >A self-contained function could be denoted by adding the
> >> >funciton name to the mutable clause:
>
> >> >int count() mutable(count);
> >> >  // may give different results on different calls
> ...
> >> However, if one really needs to indicate that a function is mutable,
> >> without further specifications, why not simply:
> >>     int count() mutable;
>
> >Ah, NOW I see: You obviously completely erased from your memory what I
> >wrote in earlier postings (especially what I've used the term
> >"self-contained" for!
>
> No, I merely suggested that a function with self-contained mutations
> should be written as
>     int count() mutable;
> instead of
>     int count() mutable(count);
> as it is unnecessary to repeat the function name twice.

OK, then I mis-read your text ("mutable, without further
specifications" for me is "may mutate anything and everything").

But with your suggestion, how would you recode

  int foo() mutable(foo, some_var)

which manipulates both some_var and internal state (so you cannot
assume it to have the same value if some_var has the same value at
entry)?

> Here,
> "self-contained mutations" would mean that different calls with the same
> input may produce different results at different times, but that the
> states that are manipulated are only shared between different copies of
> count() and no other functions. Thus there is no difference between
> whether these states are defined within the function body or external,
> only that no other function instantiation may manipulate these states.

And that - and this is important - no other function instantiation may
_read_ this state (except by calling count itself). Especially two
different self-contained functions may be reordered at will, and they
may be reordered in any respect with lvalue accesses.

>
> >a function which can mutate _anything_ (except if otherwise
> >forbidden) would be specified as
>
> >      void foo();
>
> >as is today.
>
> Right.
>
> >That is, the compiler should not reject the following code:
>
> >void foo(int& i) mutable()
> >  // the lvalue referenced by i is implicitly assumed to be changed
> >  // despite the function being declared mutable() - i.e. the
> >  // referenced lvalue - which may be local - is implicitly added
> >  // to the mutable clause
> >{
> >  ++i; // no problem
> >}
>
> I think this should not be considered to be a pure function as it mutates
> the state of its arguments.

Well, it's irrelevant if you call it a pure function or not; it's
about syntax. My suggestion is basically that arguments taken per
non-const reference are implicitly added to the mutable clause. As I
wrote, if it should not be mutated, you shouldn't have taken a
non-const reference to it as argument.

> For example, if h, g are pure functions, then
> f(h(), g()) is well-defined in C/C++, but f(++i, ++i) or f(foo(i), foo(i))
> are not as they mutate the arguments.

Well, f(foo(i), foo(i)) actually is well defined (except that foo
returns void, and you cannot define a function taking two void
arguments). Only the call order is unspecified.

>
> So I think that in the above case one would have to write
>   void foo(int& i) mutable(i) { ++i; }

Well, and my suggestion is that you don't write the i there (after
all, it isn't really in scope at that point; although one could make a
rule about it), but the compiler enters it implicitly (or more
exactly, the actual argument passed). This is, however, a question of
style more than functionality.

> Compare this with
>   int foo(int& i) mutable() { int n = i; ++n; return n; }

Looks like a design error to me. If the argument should not be
modified, it should not be taken per non-const reference. Period.

Of course, with my suggestion it would mean "may modify i", and i
would just accientally not be changed in the current version (but it
will be in the final version).

In your interpretation, would it be allowed to write

void bar(int const& j)
{
  foo(j);
}

because the mutable clause gives, after all, the same guarantee which
const would give? Looks quite wrong to me.

After all, IMHO mutable clauses should be orthogonal to the rest of
hte language as much as possible, and that includes that they don't
re-state information which is already stated in current C++, like
especially the constness of a reference argument.

>
> >> If a sequence of operations each are considered to be pure, then the
> >> combined action is also considered to be pure. If a sequence of operations
> >> contains one operation which is not explicitly known as pure, then the
> >> combined action is to be considered as mutable, even though in reality it
> >> may happen that it is pure.
>
> >Yes, certainly. But it must be explicitly defined what is pure and
> >what not - the legality of a program may not depend on the abilities
> >of the optimizer.
>
> Right.
>
> >> So if one has a call by reference, it is possible to detect if the
> >> function is pure, if all the actions is performs with the argument are
> >> marked as pure.
>
> >But then, you wouldn't have needed the reference at all ;-) A function
> >taking a non-const reference is not pure in probably at least 99% of
> >all cases.
>
> It depends: I think this must be considered as programming styles. Also,
> it is possible to override the "const" by the use of "mutable" elements,
> and it is not possible for the compiler to know whether this should
> considered as a mutation or is a ref count that should be not.

If you use mutable, it should not change the logical state of the
object. Therefore the compiler should be allowed to assume that it
doesn't. If it does, it's your fault.

After all, you can break const correctness with const cast. You can
write copy constructors which break if the allowed optimizations are
done.

>
> In the absence of a sufficiently exact analysis, one would have to accept
> as a matter of programming styles.

No, one has to set rules which the compiler is allowed to assume.
That's the point of (most) undefined behaviour, after all: The
compiler makes assumtions which it cannot check (or only with
unacceptable overhead). If those assumtions are broken, you lost. All
that has to be done is to explicitly state those assumptions in the
standard.

Of course this assumes that the benefit (i.e. the possible
optimizations) outweight the possible undefined behaviour. This point
is to a large amount a matter of opinion, of course.

>
> >> Then one would be able to write better libraries, because the kind of
> >> issue "to not design the library in this or other way because the compiler
> >> does not know how to optimize away unused feature" that we have seen
> >> discussed here would go away.
>
> >Note that there is already one place in the standard where such an
> >assumption is implicitly made: The rules for optimizing away copies of
> >temporaries are a special case of just assuming that copy constructors
> >are pure (it the new object constructed is considered the function
> >result of the constructor).
>
> >If we could _declare_ copy constructors pure, then this rule could be
> >just removed, and we could rely on the same optimization for true pure
> >constructors, but on the program behaving correctly even in cases
> >where the copy constructor deliberately breaks that semantics (and
> >would not be declared as pure, of course).
>
> Right. This would be better. For upwards compatibility, the copy
> constructor might be assumed to have a mutable() declaration unless
> otherwise is explicitly stated.

But if you write your own copy constructor, you cannot not state it
explicitly because not writing a mutable clause is an explicit
statement. For trivial copy constructors, it's clear that the compiler
should generate explicitly pure ones (the compiler generated trivial
copy constructor is pure anyway, and the compiler knows it in
optimizations, but for program correctness tests, it would have to be
made explicit). For non-trivial compiler-generated copy constructors,
the rule should IMHO be that the mutable clause is the union of the
mutable clauses of all members and base classes.

>
> By the way, I think that this idea might be important in the case of
> threading, because if a function is pure execution in different threads
> will not produce clashes. Further, if one know what is mutated, that could
> be used in determine in what should be locked, namely what is within the
> "mutable(...)" clause.

Good point.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: remove.haberg@matematik.su.se (Hans Aberg)
Date: Tue, 3 Jul 2001 17:00:41 GMT
Raw View
In article <t3u20vebp1.fsf@watts.itp.tuwien.ac.at>, Christopher Eltschka
<celtschk@web.de> wrote:
>> ...I merely suggested that a function with self-contained mutations
>> should be written as
>>     int count() mutable;
>> instead of
>>     int count() mutable(count);
>> as it is unnecessary to repeat the function name twice.
>
>OK, then I mis-read your text ("mutable, without further
>specifications" for me is "may mutate anything and everything").

Unfortunate formulation by me.

>But with your suggestion, how would you recode
>
>  int foo() mutable(foo, some_var)
>
>which manipulates both some_var and internal state (so you cannot
>assume it to have the same value if some_var has the same value at
>entry)?

I do not know: I think that perhaps one should scrap the internal state
notation idea, and always be forced to indicate explicitly which states
are manipulated.

>> Here,
>> "self-contained mutations" would mean that different calls with the same
>> input may produce different results at different times, but that the
>> states that are manipulated are only shared between different copies of
>> count() and no other functions. Thus there is no difference between
>> whether these states are defined within the function body or external,
>> only that no other function instantiation may manipulate these states.
>
>And that - and this is important - no other function instantiation may
>_read_ this state (except by calling count itself). Especially two
>different self-contained functions may be reordered at will, and they
>may be reordered in any respect with lvalue accesses.

There is a generalization, that one indicates a group of functions in the
mutable declaration, meaning that instantiations of these functions
manipulates a state, but no others.

But how should the function name overloading be resolved? And is one not
back at square one then, where one explicitly tells which state is
manipulated?

>> I think this should not be considered to be a pure function as it mutates
>> the state of its arguments.
>
>Well, it's irrelevant if you call it a pure function or not; it's
>about syntax. My suggestion is basically that arguments taken per
>non-const reference are implicitly added to the mutable clause. As I
>wrote, if it should not be mutated, you shouldn't have taken a
>non-const reference to it as argument.

Well, one cannot be too sure that is how it will be used. So I am sceptic
to this in the absence of a full analysis of the situation. (Like, how
does it work out in actual programming?)

My suggestion is that if one has a reference argument, then the compiler
can look at all the operations made on that argument, and if they all are
pure, then the function is also pure. If all the operations onto the
argument have been declared using a "mutable" clause, then the function
will know that the function has at the union of those mutable clause
arguments in its mutable clause.

>> For example, if h, g are pure functions, then
>> f(h(), g()) is well-defined in C/C++, but f(++i, ++i) or f(foo(i), foo(i))
>> are not as they mutate the arguments.
>
>Well, f(foo(i), foo(i)) actually is well defined (except that foo
>returns void, and you cannot define a function taking two void
>arguments). Only the call order is unspecified.

Sorry, I thought about your foo() as something like
  int foo(int& i) { ++i; return i; }
It is then just a wrap around the ++ prefix function. Then f(foo(i),
foo(i)) is not well-defined as C/C++ does not specify the computation
order and as the function is not pure because it makes mutations of its
arguments.

-- In general though, I think that the rules should be so that it helps
the compiler do the optimizations. I do not know much about such
optimizations, so I cannot help out there much.

  Hans Aberg      * Anti-spam: remove "remove." from email address.
                  * Email: Hans Aberg <remove.haberg@member.ams.org>
                  * Home Page: <http://www.matematik.su.se/~haberg/>
                  * AMS member listing: <http://www.ams.org/cml/>

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Christopher Eltschka <celtschk@web.de>
Date: Wed, 27 Jun 2001 17:59:15 GMT
Raw View
remove.haberg@matematik.su.se (Hans Aberg) writes:

> In article <t3y9qg3brp.fsf@watts.itp.tuwien.ac.at>, Christopher Eltschka
> <celtschk@web.de> wrote:
> >AFAIK a pure function is a function which doesn't change _any_ state,
> >not just any external state (pure functions of course allow even more
> >optimizations). For example, the following function is _not_ pure, but
> >would qualify for the "const" definition above (and allow the specific
> >optimization mentioned):
> >
> >int count() self_contained // ad-hoc keyword
> >{
> >  static int i;
> >  return ++i;
> >}
>
> It depends what one puts into the word "external": The above is just a
> syntactic construct to make the state "i" look as though it is internal to
> the function count().

In my view i is internal to the function. The point is that nothing
but count itself can access this variable, therefore the compiler can
assume any other variable unmodified.

>
> But this function is clearly not pure.

Exactly. But I think the self_contained attribute is useful in it's
own right (of course all pure functions are self-contained). If allows
the compiler to better optimize the code around this function (while
for pure functions, the compiler may optimize the function itself).

>
> I put in the word "external", because I figure a function must always
> manipulate some states in the course of its manipulations, only that in
> the case of a pure function, one expects that the same call with the same
> input but at different times should be produce the same result.

Then the concept you mean is better expressed as "permanent": The
function may not modify any permanent state (it may modify transient
state, like non-static function variables, or objects it creates with
new and destroys again).

[...]

> One generalization of a pure function could be that one indicates which
> states it manipulates. For example, one might declare
>   double sqrt(const double&) impure errno;
> Then the compiler will know that if errno is not used in the loop above,
> then sqrt can be moved out.

Interesting idea. This is similar to throw clauses.

One could re-use the keyword mutable for this:

double sqrt(const double&) mutable(errno);

Then a function without a mutable clause would be allowed
to modify everything (as is now). A pure funciton would be
written as

double foo(double x) mutable();

A self-contained function could be denoted by adding the
funciton name to the mutable clause:

int count() mutable(count);
  // may give different results on different calls

This would include both functions with local static variables and
functions depending on external global variables (especially those
they modify themselves).

Probably functions taking non-const references to objects should
always be consodered to modify the referenced objects (why else should
the reference be non-const?). Also, non-const member functions should
always be considered to modify the object they are called on
(otherwise they had been made const).

If that attribute should be enforced by the compiler, there also would
have to be a construct to tell the compiler "this may not look safe,
but it really is" (just like with const-cast), so f.ex. actually pure
functions could be called even if not marked pure, or functions which
may not be pure in general, but are pure with certain arguments, may
be called as pure (f.ex. std::sqrt is pure for arguments >= 0, so if
you know you have such an argument, you want to tell the compiler
"hey, I know this call is pure, so shut up!")

Also one has to consider what to do with non-variable modifiable
lvalues (note that errno might - and in MT environments usually will -
not be a variable).

>
> Anyway, compilers can do a lot of such optimizations for basic data types,
> and I think it might be interesting if C++ is somehow augmented so that
> the same kind of optimizations can be applied to class defined data.

I actually like the idea of a mutable clause.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: remove.haberg@matematik.su.se (Hans Aberg)
Date: Thu, 28 Jun 2001 21:30:05 GMT
Raw View
In article <t3els60x7m.fsf@watts.itp.tuwien.ac.at>, Christopher Eltschka
<celtschk@web.de> wrote:
>> One generalization of a pure function could be that one indicates which
>> states it manipulates. For example, one might declare
>>   double sqrt(const double&) impure errno;
>> Then the compiler will know that if errno is not used in the loop above,
>> then sqrt can be moved out.

>Interesting idea. This is similar to throw clauses.

>One could re-use the keyword mutable for this:

>double sqrt(const double&) mutable(errno);

>Then a function without a mutable clause would be allowed
>to modify everything (as is now). A pure funciton would be
>written as

>double foo(double x) mutable();

I think that this idea is interesting. Of course, I like the word "pure"
instead of "mutable()", but as one can avoid adding extra key-words with
the latter, it is probably better.

>A self-contained function could be denoted by adding the
>funciton name to the mutable clause:

>int count() mutable(count);
>  // may give different results on different calls

Because of the name-overloading, this would be ambiguous. Also, if it
should be any use of the compiler to make more exact optimization, I
figure it would need to know what is mutated. In addition, what is not
marked with mutable will anyway be treated as mutable by the compiler, so
no special markup will be needed.

However, if one really needs to indicate that a function is mutable,
without further specifications, why not simply:
    int count() mutable;
This is then syntactically different from
    int count() mutable();
It is also easy to remember if one thinks of "()" as nothing: The first
form reads a "mutable" and the second as "mutable -- nothing".

>Probably functions taking non-const references to objects should
>always be consodered to modify the referenced objects (why else should
>the reference be non-const?). Also, non-const member functions should
>always be considered to modify the object they are called on
>(otherwise they had been made const).

I think this falls into the category of compiler optimization:

If a sequence of operations each are considered to be pure, then the
combined action is also considered to be pure. If a sequence of operations
contains one operation which is not explicitly known as pure, then the
combined action is to be considered as mutable, even though in reality it
may happen that it is pure.

So if one has a call by reference, it is possible to detect if the
function is pure, if all the actions is performs with the argument are
marked as pure.

>> Anyway, compilers can do a lot of such optimizations for basic data types,
>> and I think it might be interesting if C++ is somehow augmented so that
>> the same kind of optimizations can be applied to class defined data.

>I actually like the idea of a mutable clause.

I think it would be great if one could achieve that compilers can do the
same kind of optimizations with class defined types as is possible with
the basic data types:

Then one would be able to write better libraries, because the kind of
issue "to not design the library in this or other way because the compiler
does not know how to optimize away unused feature" that we have seen
discussed here would go away.

  Hans Aberg      * Anti-spam: remove "remove." from email address.
                  * Email: Hans Aberg <remove.haberg@member.ams.org>
                  * Home Page: <http://www.matematik.su.se/~haberg/>
                  * AMS member listing: <http://www.ams.org/cml/>

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: remove.haberg@matematik.su.se (Hans Aberg)
Date: Fri, 29 Jun 2001 19:41:31 GMT
Raw View
In article <t3els60x7m.fsf@watts.itp.tuwien.ac.at>, Christopher Eltschka
<celtschk@web.de> wrote:
>> One generalization of a pure function could be that one indicates which
>> states it manipulates. For example, one might declare
>>   double sqrt(const double&) impure errno;
>> Then the compiler will know that if errno is not used in the loop above,
>> then sqrt can be moved out.

>Interesting idea. This is similar to throw clauses.

>From the theoretical point of view, this similarity can be described by
the use of monads: One can construct a state monad, exception monad, and
also IO monads. (Wadler described this in his papers.) The simplest way to
understand the use of the IO monads in Haskell is that it is a way to tell
the type system that IO takes place. -- I'm not suggesting that one should
use explicit monads in C++, because one thing that makes it convenient in
Haskell is its polymorphic type system, which C++ does not have.

But if one want to understand the underlying theoretical mechanism, one
might use monads for that: For example, monads do not commute, so a
mutative function that throws exceptions is not the same thing as a
exception function that does mutations, at least from the monadic
perspective.

It could mean that one has to think something about how "mutative"
declarations work together with "throw" declarations.

  Hans Aberg      * Anti-spam: remove "remove." from email address.
                  * Email: Hans Aberg <remove.haberg@member.ams.org>
                  * Home Page: <http://www.matematik.su.se/~haberg/>
                  * AMS member listing: <http://www.ams.org/cml/>

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Christopher Eltschka <celtschk@web.de>
Date: Fri, 29 Jun 2001 19:48:32 GMT
Raw View
remove.haberg@matematik.su.se (Hans Aberg) writes:

> In article <t3els60x7m.fsf@watts.itp.tuwien.ac.at>, Christopher Eltschka
> <celtschk@web.de> wrote:
> >> One generalization of a pure function could be that one indicates which
> >> states it manipulates. For example, one might declare
> >>   double sqrt(const double&) impure errno;
> >> Then the compiler will know that if errno is not used in the loop above,
> >> then sqrt can be moved out.
>
> >Interesting idea. This is similar to throw clauses.
>
> >One could re-use the keyword mutable for this:
>
> >double sqrt(const double&) mutable(errno);
>
> >Then a function without a mutable clause would be allowed
> >to modify everything (as is now). A pure funciton would be
> >written as
>
> >double foo(double x) mutable();
>
> I think that this idea is interesting. Of course, I like the word "pure"
> instead of "mutable()", but as one can avoid adding extra key-words with
> the latter, it is probably better.
>
> >A self-contained function could be denoted by adding the
> >funciton name to the mutable clause:
>
> >int count() mutable(count);
> >  // may give different results on different calls
>
> Because of the name-overloading, this would be ambiguous.

Because it's a special case which needs to explicitly defined, it is
not ambiguous (at least it would be silly to give it an ambiguous
definition). Also, a function name as expression results in an rvalue
(the pointer to the function), and since normally only lvalues make
sense in a mutable clause (extreme example: "void f() mutable(0)" -
does this mutate the value of the constant 0?), I would expect rvalues
in mutable clauses to be explicitly forbidden, with the exception of
the explicit special case of hte function name, which has the explicit
special meaning of "permanent internal state".

> Also, if it
> should be any use of the compiler to make more exact optimization, I
> figure it would need to know what is mutated.

It knows it exactly enough: Permanent state which is not accessible
outside the function itself, so while the compiler cannot make any
assumptions about the function itself, it can make the assumption that
no state accessible directly or indirectly to the caller except
through call to this function changed.

> In addition, what is not
> marked with mutable will anyway be treated as mutable by the compiler, so
> no special markup will be needed.

Yes, but just as the throw clause indeed specifies what is not thrown
(namely everything not mentioned), the mutable clause would also
specify what is not mutable (again, by not mentioning it).  In the
case of a selt-contained function, this is external state (with my
definition of "external", not your's, of course).

>
> However, if one really needs to indicate that a function is mutable,
> without further specifications, why not simply:
>     int count() mutable;

Ah, NOW I see: You obviously completely erased from your memory what I
wrote in earlier postings (especially what I've used the term
"self-contained" for!

No, a function which can mutate _anything_ (except if otherwise
forbidden) would be specified as

      void foo();

as is today.

But "int count()" as previously defined does explicitly _not_ change
anything. Indeed, it gives a very stringent guarantee: Whatever lvalue
you may access, the value of it will _not_ be modified by count(). The
_only_ accessible value which is affected by calls to count() is the
return value of count. For example, it guarantees that all the
following functions are equivalent:

int f1(int* p)
{
  int i = count();
  int tmp = *p;
  int j = count();
  (*p) += i+j;
  int k = count();
  return tmp + k;
}

int f2(int* p)
{
  int i = count();
  int j = count();
  int k = count();
  int tmp = *p;
  (*p) += i+j;
  return tmp + k;
}

int f3(int* p)
{
  int i = count();
  int tmp = *p;
  (*p) += i + count();
  return tmp + count();
}

That is, despite the fact that the compiler doesn't know what p points
to, it knows that neither does a call to count affect *p, nor does
assignment to *p affect the result of a count call. This is what I
called "self-contained": The compiler has to preserve the order of the
calls, but it can move this around freely otherwise (of course, when
calling a non-self-contained function, the compiler must still assume
that that function may call count(), so it must not reorder those
calls).

> This is then syntactically different from
>     int count() mutable();
> It is also easy to remember if one thinks of "()" as nothing: The first
> form reads a "mutable" and the second as "mutable -- nothing".

The first one is strictly unneeded, since not having a mutable clause
has just the semantics you thought of.

>
> >Probably functions taking non-const references to objects should
> >always be consodered to modify the referenced objects (why else should
> >the reference be non-const?). Also, non-const member functions should
> >always be considered to modify the object they are called on
> >(otherwise they had been made const).
>
> I think this falls into the category of compiler optimization:

If "mutable" is to be a compiler-enforced attribute (just as const
is), then the question of what may be mutated is _not_ a question of
compiler optimization, but of code correctness.

That is, the compiler should not reject the following code:

void foo(int& i) mutable()
  // the lvalue referenced by i is implicitly assumed to be changed
  // despite the function being declared mutable() - i.e. the
  // referenced lvalue - which may be local - is implicitly added
  // to the mutable clause
{
  ++i; // no problem
}

void bar() mutable() // pure function, since it takes no references
{
  int k;
  foo(k); // no problem either, since the modified variable is local
}

>
> If a sequence of operations each are considered to be pure, then the
> combined action is also considered to be pure. If a sequence of operations
> contains one operation which is not explicitly known as pure, then the
> combined action is to be considered as mutable, even though in reality it
> may happen that it is pure.

Yes, certainly. But it must be explicitly defined what is pure and
what not - the legality of a program may not depend on the abilities
of the optimizer.

>
> So if one has a call by reference, it is possible to detect if the
> function is pure, if all the actions is performs with the argument are
> marked as pure.

But then, you wouldn't have needed the reference at all ;-) A function
taking a non-const reference is not pure in probably at least 99% of
all cases. But you can consider the referenced variable as part of the
result, and then the function may do anything with it, as long as it
doesn't change anything else, and still be considered "pure" in that
sense. Especially a "true pure" function (like "bar" above) may call
it, provided it only binds transient lvalues to those references.

>
> >> Anyway, compilers can do a lot of such optimizations for basic data types,
> >> and I think it might be interesting if C++ is somehow augmented so that
> >> the same kind of optimizations can be applied to class defined data.
>
> >I actually like the idea of a mutable clause.
>
> I think it would be great if one could achieve that compilers can do the
> same kind of optimizations with class defined types as is possible with
> the basic data types:
>
> Then one would be able to write better libraries, because the kind of
> issue "to not design the library in this or other way because the compiler
> does not know how to optimize away unused feature" that we have seen
> discussed here would go away.

Note that there is already one place in the standard where such an
assumption is implicitly made: The rules for optimizing away copies of
temporaries are a special case of just assuming that copy constructors
are pure (it the new object constructed is considered the function
result of the constructor).

If we could _declare_ copy constructors pure, then this rule could be
just removed, and we could rely on the same optimization for true pure
constructors, but on the program behaving correctly even in cases
where the copy constructor deliberately breaks that semantics (and
would not be declared as pure, of course). Not that I would have any
use for such now-forbidden copy constructors - but it would remove
some special-casing from the language and replace it with a general
feature not only applicable to copy constructors, but to all functions.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Christopher Eltschka <celtschk@web.de>
Date: Mon, 25 Jun 2001 17:35:10 GMT
Raw View
remove.haberg@matematik.su.se (Hans Aberg) writes:

> In article <387bc.nw.scottm@toast.net>, scottm@toast.net (Scott) wrote:
> >>> What is wrong with
> >>> inline void foo() const
> ...
> >>> I'd like to explain to the compiler that calling foo() has no side effects
> >>> on any variables it doesn't own...
> >>> What are the objections?
> ...
> >>But 'const' already has a meaning there and it is
> >>indeed not very intuitive (IMHO) and it's definitely not consistent. I'd
> >>prefer a new keyword here, but I wonder if it's up to the programmer to
> >>state this property of a function. Why not leave it up to the compiler
> >>to find out which functions have no side-effect?
> >
> >But it can't. In my example foo() was inline, but it could have been
> >(and in "real life", often would be)
>
> A function with you side effects is commonly called pure, so I suggested
> that a keyword or feature called "pure" might be added to C++. (A function
> that is known to manipulate external states might be called mutative, but
> a combination of mutative functions may end up becoming pure if their
> external state actions cancel.)

AFAIK a pure function is a function which doesn't change _any_ state,
not just any external state (pure functions of course allow even more
optimizations). For example, the following function is _not_ pure, but
would qualify for the "const" definition above (and allow the specific
optimization mentioned):

int count() self_contained // ad-hoc keyword
{
  static int i;
  return ++i;
}

int j;

void foo()
{
  j = 5;
  int new_num = count();
  assert(j == 5); // compiler knows it will not fail
}

However, the following code cannot be optimized for this function (but
could be optimized with a pure one):

void bar()
{
  int i(count(), count());
}

Of course, pure functions are always "self-contained" (and pure member
functions in addition are always const).

Probably a member function which only changes members of its own
class should be considered "self-contained", too.

However, this new feature, as well as real pure, could bear some
surprises for people not knowing the details of C++:

double foo(double x) pure
{
  return x + sqrt(x);
  // error: sqrt(x) may change errno!
}

>
> The semantic meaning of the pure functions would be hint to the compiler
> that the computational order may be rearranged, as if the functions where
> pure.

This is again something which can only be done with pure functions,
not with "self-contained" ones:

void baz()
{
  int i = count();
  int j = count(); // the compiler better not exchanges those!
  assert(i<j);
}

>
> As for letting the compiler to infer which functions that are pure, it
> might be possible to do that to some degree: Say functions whose all
> arguments are const or by value, and which do not manipulate any external
> variable.

int i;

class X
{
  X(X const&);
};

// this definition is in another translation unit
X::X(X const&)
{
  ++i;
}

// and back to the current one:

void foo(X x)
{
  X y(x);
}

According to your rules, the compiler would declare foo pure.

BTW, even without this, your rule would only make "self-contained",
not pure.

One could automatically infer self-contained as all of your rules and
additionally only calling self-contained functions (including
constructors and overloaded operators).

For pure functions, local statics would be forbidden either.

>
> But some cases might be impossible to catch, for example if the function
> does manipulate an external state which is not considered (to the
> programmer) part of the semantics of the function: Say a ref count, or
> something.

Or, for pure functions, the code might cache function results in a
static variable to speedup future calls. The compiler cannot generally
distinguish that from a general state change (think of a map of
function results, implemented through a user-defined type using hash
tables).

>
> Then again, a ref count might be caught in the pure analysis by that it
> declared "mutable".

Hmmm...

double f(double x)
{
  static mutable double value, result;
  ...
}

Might even make sense.

>
> It appears to be important to know that a function is pure in functional
> language compilers, because one then can do various optimizations by
> rearranging the computational order of the lambda expressions.
>
> But I do not know how useful it is to a C++ compiler (that is, if it can
> be used in optimizations) to know that certain functions are pure.

What about the following:

for(int i = 0; i < std::sqrt(some_var); ++i)
{
  // some_var is not used here
}

If the compiler knows that sqrt is pure, it can move it out of the
loop. (Well, actually C++'s sqrt is _not_ pure, since it may change
errno; OTOH I guess that using the result is undefined behaviour in
that case, so the optimization could be done anyway.)

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: remove.haberg@matematik.su.se (Hans Aberg)
Date: Tue, 26 Jun 2001 15:38:41 GMT
Raw View
In article <t3y9qg3brp.fsf@watts.itp.tuwien.ac.at>, Christopher Eltschka
<celtschk@web.de> wrote:
>AFAIK a pure function is a function which doesn't change _any_ state,
>not just any external state (pure functions of course allow even more
>optimizations). For example, the following function is _not_ pure, but
>would qualify for the "const" definition above (and allow the specific
>optimization mentioned):
>
>int count() self_contained // ad-hoc keyword
>{
>  static int i;
>  return ++i;
>}

It depends what one puts into the word "external": The above is just a
syntactic construct to make the state "i" look as though it is internal to
the function count().

But this function is clearly not pure.

I put in the word "external", because I figure a function must always
manipulate some states in the course of its manipulations, only that in
the case of a pure function, one expects that the same call with the same
input but at different times should be produce the same result.

>> As for letting the compiler to infer which functions that are pure, it
>> might be possible to do that to some degree: Say functions whose all
>> arguments are const or by value, and which do not manipulate any external
>> variable.

>int i;

>class X
>{
>  X(X const&);
>};

>// this definition is in another translation unit
>X::X(X const&)
>{
>  ++i;
>}

>// and back to the current one:

>void foo(X x)
>{
>  X y(x);
>}

>According to your rules, the compiler would declare foo pure.

Yes, in this case I overlooked that the copy constructor might be impure.
In my world it is always pure. -- If one is using a ref count, then that
does not change the appearance of the copy constructor as pure, even
though I am not exactly sure what happens if one start to pure optimize
around such a function.

>BTW, even without this, your rule would only make "self-contained",
>not pure.

The rule must be that all involved function calls, implicit or not must be
pure, in addition to that no "external" or "global" state is manipulated.

>One could automatically infer self-contained as all of your rules and
>additionally only calling self-contained functions (including
>constructors and overloaded operators).
>
>For pure functions, local statics would be forbidden either.

But be aware of that functions that should be considered to be pure might
make use of ref counts and the like.

This makes the subject of automate finding pure functions even trickier.

>What about the following:
>
>for(int i = 0; i < std::sqrt(some_var); ++i)
>{
>  // some_var is not used here
>}
>
>If the compiler knows that sqrt is pure, it can move it out of the
>loop. (Well, actually C++'s sqrt is _not_ pure, since it may change
>errno; OTOH I guess that using the result is undefined behaviour in
>that case, so the optimization could be done anyway.)

One generalization of a pure function could be that one indicates which
states it manipulates. For example, one might declare
  double sqrt(const double&) impure errno;
Then the compiler will know that if errno is not used in the loop above,
then sqrt can be moved out.

Anyway, compilers can do a lot of such optimizations for basic data types,
and I think it might be interesting if C++ is somehow augmented so that
the same kind of optimizations can be applied to class defined data.

  Hans Aberg      * Anti-spam: remove "remove." from email address.
                  * Email: Hans Aberg <remove.haberg@member.ams.org>
                  * Home Page: <http://www.matematik.su.se/~haberg/>
                  * AMS member listing: <http://www.ams.org/cml/>

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: remove.haberg@matematik.su.se (Hans Aberg)
Date: Wed, 13 Jun 2001 17:43:28 GMT
Raw View
In article <387bc.nw.scottm@toast.net>, scottm@toast.net (Scott) wrote:
>>> What is wrong with
>>> inline void foo() const
...
>>> I'd like to explain to the compiler that calling foo() has no side effects
>>> on any variables it doesn't own...
>>> What are the objections?
...
>>But 'const' already has a meaning there and it is
>>indeed not very intuitive (IMHO) and it's definitely not consistent. I'd
>>prefer a new keyword here, but I wonder if it's up to the programmer to
>>state this property of a function. Why not leave it up to the compiler
>>to find out which functions have no side-effect?
>
>But it can't. In my example foo() was inline, but it could have been
>(and in "real life", often would be)

A function with you side effects is commonly called pure, so I suggested
that a keyword or feature called "pure" might be added to C++. (A function
that is known to manipulate external states might be called mutative, but
a combination of mutative functions may end up becoming pure if their
external state actions cancel.)

The semantic meaning of the pure functions would be hint to the compiler
that the computational order may be rearranged, as if the functions where
pure.

As for letting the compiler to infer which functions that are pure, it
might be possible to do that to some degree: Say functions whose all
arguments are const or by value, and which do not manipulate any external
variable.

But some cases might be impossible to catch, for example if the function
does manipulate an external state which is not considered (to the
programmer) part of the semantics of the function: Say a ref count, or
something.

Then again, a ref count might be caught in the pure analysis by that it
declared "mutable".

It appears to be important to know that a function is pure in functional
language compilers, because one then can do various optimizations by
rearranging the computational order of the lambda expressions.

But I do not know how useful it is to a C++ compiler (that is, if it can
be used in optimizations) to know that certain functions are pure.

  Hans Aberg      * Anti-spam: remove "remove." from email address.
                  * Email: Hans Aberg <remove.haberg@member.ams.org>
                  * Home Page: <http://www.matematik.su.se/~haberg/>
                  * AMS member listing: <http://www.ams.org/cml/>

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: scottm@toast.net (Scott)
Date: Wed, 13 Jun 2001 02:25:28 GMT
Raw View
What is wrong with

inline void foo() const
{...;}

?

I'd like to explain to the compiler that calling foo() has no side effects
on any variables it doesn't own; I can think of situations where that leads
to a valuable set of optimizations:

static int i;

{
    i = 0;
    foo();
    //compiler can *know* i is still 0 here
}


What are the objections?

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Daniel Frey <daniel.frey@aixigo.de>
Date: Wed, 13 Jun 2001 12:17:42 GMT
Raw View
Scott wrote:
>=20
> What is wrong with
>=20
> inline void foo() const
> {...;}
>=20
> ?
>=20
> I'd like to explain to the compiler that calling foo() has no side effe=
cts
> on any variables it doesn't own; I can think of situations where that l=
eads
> to a valuable set of optimizations:
>=20
> static int i;
>=20
> {
>     i =3D 0;
>     foo();
>     //compiler can *know* i is still 0 here
> }
>=20
> What are the objections?

If you could do this for 'normal' function, you want to do this for
member functions, too. But 'const' already has a meaning there and it is
indeed not very intuitive (IMHO) and it's definitely not consistent. I'd
prefer a new keyword here, but I wonder if it's up to the programmer to
state this property of a function. Why not leave it up to the compiler
to find out which functions have no side-effect? In fact, this allows
even better optimizations as the compiler can find out if foo() changes
'i', not just any variable. In fact I think that there are already some
compilers that do this kind of optimization today.

Regards, Daniel

--
Daniel Frey

aixigo AG - financial training, research and technology
Schlo=DF-Rahe-Stra=DFe 15, 52072 Aachen, Germany
fon: +49 (0)241 936737-42, fax: +49 (0)241 936737-99
eMail: daniel.frey@aixigo.de, web: http://www.aixigo.de

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: scottm@toast.net (Scott)
Date: Wed, 13 Jun 2001 14:01:17 GMT
Raw View
In comp.std.c++, Daniel Frey <daniel.frey@aixigo.de> wrote:
>Scott wrote:

>> What is wrong with
>>
>> inline void foo() const
>> {...;}
>>
>> ?
>>
>> I'd like to explain to the compiler that calling foo() has no side effects
>> on any variables it doesn't own...
>> What are the objections?

>If you could do this for 'normal' function, you want to do this for
>member functions, too. But 'const' already has a meaning there and it is
>indeed not very intuitive (IMHO) and it's definitely not consistent. I'd
>prefer a new keyword here, but I wonder if it's up to the programmer to
>state this property of a function. Why not leave it up to the compiler
>to find out which functions have no side-effect?

But it can't. In my example foo() was inline, but it could have been
(and in "real life", often would be)

//I *promise* this 3rd party function isn't going to affect any of my
// dozens of high performance, carefully optimized variable references...
extern void foo() const;

and the compiler could have no information about the content of foo, beyond
the declarations.

You're right, though.. a class member function that wanted to declare
it was free of side effects at both the class and global scope, would have
a semantic problem trying to use const for both. I suppose ::const ... no, that's
getting weird.

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: Daniel Frey <daniel.frey@aixigo.de>
Date: Wed, 13 Jun 2001 14:31:03 GMT
Raw View
Scott wrote:
>=20
> In comp.std.c++, Daniel Frey <daniel.frey@aixigo.de> wrote:
> >Scott wrote:
>=20
> >> What is wrong with
> >>
> >> inline void foo() const
> >> {...;}
> >>
> >> ?
> >>
> >> I'd like to explain to the compiler that calling foo() has no side e=
ffects
> >> on any variables it doesn't own...
> >> What are the objections?
>=20
> >If you could do this for 'normal' function, you want to do this for
> >member functions, too. But 'const' already has a meaning there and it =
is
> >indeed not very intuitive (IMHO) and it's definitely not consistent. I=
'd
> >prefer a new keyword here, but I wonder if it's up to the programmer t=
o
> >state this property of a function. Why not leave it up to the compiler
> >to find out which functions have no side-effect?
>=20
> But it can't. In my example foo() was inline, but it could have been
> (and in "real life", often would be)
>=20
> //I *promise* this 3rd party function isn't going to affect any of my
> // dozens of high performance, carefully optimized variable references.=
..
> extern void foo() const;

This is a different example, as you already noticed :) 'inline' implied
for me that the compiler knows the function definition, not just the
declaration, at any time it's using it. But for external functions, your
suggestion looks useful, except the use of the 'const' keyword..

> You're right, though.. a class member function that wanted to declare
> it was free of side effects at both the class and global scope, would h=
ave
> a semantic problem trying to use const for both. I suppose ::const ... =
no, that's
> getting weird.

If you'd like to specify such properties, I'd suggest a new keyword as
none of the existing keywords looks promising to me. 'const' is a very
bad idea here, as it will confuse people too much. 'no_side_effects'
looks weird at first, but at least it should be relatively intuitive.
OTOH I doubt that such a change will make it to a new version of C++ as
the expected benefits are probably very small...

Regards, Daniel

--
Daniel Frey

aixigo AG - financial training, research and technology
Schlo=DF-Rahe-Stra=DFe 15, 52072 Aachen, Germany
fon: +49 (0)241 936737-42, fax: +49 (0)241 936737-99
eMail: daniel.frey@aixigo.de, web: http://www.aixigo.de

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]





Author: "Balog Pal" <pasa@lib.hu>
Date: Wed, 13 Jun 2001 14:55:29 GMT
Raw View
"Daniel Frey" <daniel.frey@aixigo.de> wrote in message news:3B271C81.A09464D9@aixigo.de...

> inline void foo() const
> {...;}
> I'd like to explain to the compiler that calling foo() has no side effects
> on any variables it doesn't own; I can think of situations where that leads
> to a valuable set of optimizations:

The idea is quite interesting. However if the function is inlined, the compiler can figure out the 'no side effects' part without help, so it will work well with non-inline functions. And the objection about syntax overloading is valid too.

>I'd prefer a new keyword here

It could go like the exception spec in a fixed form. The restriction would be statically enforced, and inherited.

One step forward and maybe fe hit adding real functions to C++? ;-)  Meaning a thing that has no global side effects, and produce identical output for the same input.  Well, that would reach too far I guess. ;->

Paul

---
[ comp.std.c++ is moderated.  To submit articles, try just posting with ]
[ your news-reader.  If that fails, use mailto:std-c++@ncar.ucar.edu    ]
[              --- Please see the FAQ before posting. ---               ]
[ FAQ: http://www.research.att.com/~austern/csc/faq.html                ]