Topic: Unfortunate const wierdness


Author: rridge@calum.csclub.uwaterloo.ca (Ross Ridge)
Date: 1995/07/28
Raw View
Tim Hollebeek <tim@handel.Princeton.EDU> wrote:
...
>const char *strchr(const char *s, int c)
>char *strchr(char *s, int c)
>
>with the additional annoyance that the second doesn't modify s, but
>you can't assert that since doing so would conflict with the first.

You can assert neither modifies what its argument points to this way:

 const char *strchr(const char *s, int c);
 inline char *strchr(char *s, int c) {
  return (char *) strchr((char const *) s, c);
 }

      Ross Ridge

--
 l/  //   Ross Ridge -- The Great HTMU                         +1 519 883 4329
[oo][oo]  rridge@csclub.uwaterloo.ca      http://csclub.uwaterloo.ca/u/rridge/
-()-/()/
 db  //





Author: s5vijen@portland.watson.ibm.com (Anil Vijendran)
Date: 1995/07/28
Raw View
>>>>> On 28 Jul 1995 07:35:41 GMT, tim@handel.Princeton.EDU (Tim
Hollebeek) said:

    Tim> In article <S5VIJEN.95Jul27122501@portland.watson.ibm.com>,
    Tim> Anil Vijendran <s5vijen@watson.ibm.com> wrote:
    >>>>>>> On 26 Jul 1995 03:42:14 GMT, tim@handel.Princeton.EDU (Tim
    >> Hollebeek) said:
    >>
    >> [having const and non-const versions of functions]
    >>
    >> But I understand the general problem: having to maintain two separate
    >> functions with identical bodies.

    Tim> Exactly.

    >> I wonder if this is so bad as to ask for an extension to the
    >> templates... one that just adds a cv-qualifier at the right
    >> places. And how'd you generalize that to support both constant member
    >> functions and non-member functions?

    Tim> Granted, the template syntax is horrid.  Since the real problem is
    Tim> identical function bodies, why not:

    Tim> T1 foo(T2 x) {
    Tim>    ...
    Tim> }

    Tim> T3 bar(T4) = foo(T2);

    Tim> T1 and T3 should differ only by a const_cast<>, as with T2 and T4.
    Tim> (although reinterpret_cast<> may be feasible as well in some cases)

Instead why don't you call the non-const function in the body of the
const function? That way you only have to maintain the non-const
function and the standard conversions from non-const to const would
take care of the const function.

 inline char* strchr(char* str, char ch);

 inline const char* strchr(const char* str, char ch)
 {
           return strchr((char *) str, ch);
 }

    Tim> --
    Tim> Tim Hollebeek        |"What is love? 'tis not hereafter; Present mirth hath
    Tim> PChem Grad Student   |present laughter; What's to come is still unsure: In
    Tim> Princeton University |delay there lies no plenty; Then come kiss me, sweet and
    Tim> ---------------------|twenty, Youth's a stuff will not endure." -Twelfth Night

--
Peace.... +<:-)

Anil
akv@cacs.usl.edu
s5vijen@watson.ibm.com





Author: tim@handel.Princeton.EDU (Tim Hollebeek)
Date: 1995/07/28
Raw View
In article <1995Jul27.104621.1390@ittpub>, Wil Evers <wil@ittpub.nl> wrote:
>In article <3v4dim$9cu@cnn.Princeton.EDU> tim@handel.Princeton.EDU (Tim
>Hollebeek) writes:
>>
>> Possibly, there is a better syntax for getting across the above idea;
>> a simple way of saying 'the return value should be const if all
>> parameters are const' would be less general, although probably
>> adequate.
>
>I don't know about your situation, but most of my colleagues have a hard
>time trying to understand C++ code as it is now. They could probably live
>with the two overloaded function declarations you mention, they do their
>best to understand what templates do (as do many compiler writers, by the
>way) but when they see the pile of papers on my desk and I tell them
>that's the draft C++ standard they walk away in digust and continue with
>their daily jobs, happily doing OO in Objective-C, not enjoying any of
>C++'s benefits.

I agree completely, however I 'const' my code quite strictly, and I
find that to avoid casting away const, you need to double up alot of
definitions, which becomes a maintenance nightmare.  I also agree that
my template based syntax is horrid; it was basically for expressing
the concept.  Perhaps the following is better:

char *strchr(char *, int);

const char *strchr(const char *, int) = strchr(char *, int);

i.e. specify that function 2 has a different signature, but the same
implementation.

template <int n>
void foo()
{
    // general case
}

void foo<1>() {
   // optimized
}

void foo<2>() = foo<1>(); // use same trick for these 3
void foo<3>() = foo<1>();
void foo<4>() = foo<1>();

We already have that obnoxious void func() = 0 syntax
in the grammar ...

>Please ask yourself: do we *really* need this?

Do a quick check on how many posts to comp.lang.c++ ask:

Do I *really* need two member functions to implement operator[] ?

If implemented right, I think this would reduce confusion with respect
to C++ and 'const'.  Then again, that's my opinion; I'm probably more
adverse to implementing the same method twice than most people are.
It seems that having to do so usually points to a flaw in the
language.  Weren't templates introducted to avoid having to do:

int square(int, int);
double square(double, double);
complex square(complex, complex);

i.e. prevent having to implement the _same_ method a number of times
to satisfy the compiler?

--
Tim Hollebeek        |"What is love? 'tis not hereafter; Present mirth hath
PChem Grad Student   |present laughter; What's to come is still unsure: In
Princeton University |delay there lies no plenty; Then come kiss me, sweet and
---------------------|twenty, Youth's a stuff will not endure." -Twelfth Night





Author: wil@ittpub.nl (Wil Evers)
Date: 1995/07/27
Raw View
In article <3v4dim$9cu@cnn.Princeton.EDU> tim@handel.Princeton.EDU (Tim
Hollebeek) writes:
> Assuming you 'const' your code strictly, you will soon run into the
> following problem:
>
> Here is an example from <strings.h>:
>
> char *strchr(const char *s, int c)
>
> However, if s really is const char *, then a pointer into it really
> should be const char * as well.  But we can't do:
>
> const char *strchr(const char *s, int c)
>
> because then if s is char *, and gets converted to const char *, the
> return type should be char *.  You end up needing two functions:
>
> const char *strchr(const char *s, int c)
> char *strchr(char *s, int c)
>
> with the additional annoyance that the second doesn't modify s, but
> you can't assert that since doing so would conflict with the first.
> Another example comes in indexing; most 'array' objects will have
> something like the following:
>
> const T& operator[](int) const;
> T& operator[](int);
>
> so that non-const indexed array objects can be used as lvalues, but
> const ones cannot.  Again, both routines will have identical code.
>
> it seems that there should be a way to indicate that a function
> returns a const reference iff called on a const object, or if
> the appropriate parameter is const.
>
> template <modifier M>
> M char *strchr(M char *, int c);
>
> and
> template <modifier M>
> M T& operator[](int) M;
>
> come to mind; the compiler can either generate two versions or one
> version of the code depending what it feels is best (i.e. in the
> case of volatile, can significant enough optimizations be done to make
> two separate functions more efficient?)
>
> Possibly, there is a better syntax for getting across the above idea;
> a simple way of saying 'the return value should be const if all
> parameters are const' would be less general, although probably
> adequate.

I don't know about your situation, but most of my colleagues have a hard
time trying to understand C++ code as it is now. They could probably live
with the two overloaded function declarations you mention, they do their
best to understand what templates do (as do many compiler writers, by the
way) but when they see the pile of papers on my desk and I tell them
that's the draft C++ standard they walk away in digust and continue with
their daily jobs, happily doing OO in Objective-C, not enjoying any of
C++'s benefits.

Please ask yourself: do we *really* need this?

- Wil






Author: tim@handel.Princeton.EDU (Tim Hollebeek)
Date: 1995/07/27
Raw View
In article <DCC2CH.5Dp@research.att.com>,
Andrew Koenig <ark@research.att.com> wrote:
>In article <3v4dim$9cu@cnn.Princeton.EDU> tim@handel.Princeton.EDU (Tim Hollebeek) writes:
>
>> You end up needing two functions:
>>
>> const char *strchr(const char *s, int c)
>> char *strchr(char *s, int c)
>>
>> with the additional annoyance that the second doesn't modify s, but
>> you can't assert that since doing so would conflict with the first.
>
>But it does potentially modify the memory pointed to by s,
>in the sense that it returns a value that can be used to modify
>that memory.

Look at the first example.  If s is of type 'const char *', it's
probably b/c writing into it is bad.  If this is true, then writing
into the pointer returned by strchr() is also bad.  Let me give an
example:

const char *foo(); // writing into this string causes very bad things
                   // to happen; possibly a segfault or worse

bar() {
    char *p = strchr(foo(), ':'); // No error!  And no way to force one!
    *p = 0; // ouch
}

And as I pointed out, you can't change the return type of strchr to
'const char *' because then it won't be possible to do:

char *foo2();

bar() {
    char *p = strchr(foo2(), ':'); // error here
    *p = 0; // perfectly legitimate
}

So you do indeed need two strchrs.
--
Tim Hollebeek        |"What is love? 'tis not hereafter; Present mirth hath
PChem Grad Student   |present laughter; What's to come is still unsure: In
Princeton University |delay there lies no plenty; Then come kiss me, sweet and
---------------------|twenty, Youth's a stuff will not endure." -Twelfth Night





Author: s5vijen@portland.watson.ibm.com (Anil Vijendran)
Date: 1995/07/27
Raw View
>>>>> On 26 Jul 1995 03:42:14 GMT, tim@handel.Princeton.EDU (Tim
Hollebeek) said:

    Tim> Assuming you 'const' your code strictly, you will soon run into the
    Tim> following problem:

    Tim> Here is an example from <strings.h>:

    Tim> char *strchr(const char *s, int c)

I don't quite like this example. C++ is paying too much for past (C)
follies...

But I understand the general problem: having to maintain two separate
functions with identical bodies.

Maybe I haven't seen much but from what I've seen, such methods are
mostly accessors and they are really small that I've always done a
cut-paste with const additions.

I wonder if this is so bad as to ask for an extension to the
templates... one that just adds a cv-qualifier at the right
places. And how'd you generalize that to support both constant member
functions and non-member functions?



--
Peace.... +<:-)

Anil
akv@cacs.usl.edu
s5vijen@watson.ibm.com





Author: tim@handel.Princeton.EDU (Tim Hollebeek)
Date: 1995/07/28
Raw View
In article <S5VIJEN.95Jul27122501@portland.watson.ibm.com>,
Anil Vijendran <s5vijen@watson.ibm.com> wrote:
>>>>>> On 26 Jul 1995 03:42:14 GMT, tim@handel.Princeton.EDU (Tim
>Hollebeek) said:
>
> [having const and non-const versions of functions]
>
>But I understand the general problem: having to maintain two separate
>functions with identical bodies.

Exactly.

>Maybe I haven't seen much but from what I've seen, such methods are
>mostly accessors and they are really small that I've always done a
>cut-paste with const additions.

Well, one can generalize the strchr idea; maybe a database class, with
a function that returns an entry that matches certain specifications.
You might want both a const and non-const versions, and the body will
probably be nontrivial.

>I wonder if this is so bad as to ask for an extension to the
>templates... one that just adds a cv-qualifier at the right
>places. And how'd you generalize that to support both constant member
>functions and non-member functions?

Granted, the template syntax is horrid.  Since the real problem is
identical function bodies, why not:

T1 foo(T2 x) {
   ...
}

T3 bar(T4) = foo(T2);

T1 and T3 should differ only by a const_cast<>, as with T2 and T4.
(although reinterpret_cast<> may be feasible as well in some cases)

--
Tim Hollebeek        |"What is love? 'tis not hereafter; Present mirth hath
PChem Grad Student   |present laughter; What's to come is still unsure: In
Princeton University |delay there lies no plenty; Then come kiss me, sweet and
---------------------|twenty, Youth's a stuff will not endure." -Twelfth Night





Author: tim@handel.Princeton.EDU (Tim Hollebeek)
Date: 1995/07/26
Raw View
Assuming you 'const' your code strictly, you will soon run into the
following problem:

Here is an example from <strings.h>:

char *strchr(const char *s, int c)

However, if s really is const char *, then a pointer into it really
should be const char * as well.  But we can't do:

const char *strchr(const char *s, int c)

because then if s is char *, and gets converted to const char *, the
return type should be char *.  You end up needing two functions:

const char *strchr(const char *s, int c)
char *strchr(char *s, int c)

with the additional annoyance that the second doesn't modify s, but
you can't assert that since doing so would conflict with the first.
Another example comes in indexing; most 'array' objects will have
something like the following:

const T& operator[](int) const;
T& operator[](int);

so that non-const indexed array objects can be used as lvalues, but
const ones cannot.  Again, both routines will have identical code.

it seems that there should be a way to indicate that a function
returns a const reference iff called on a const object, or if
the appropriate parameter is const.

template <modifier M>
M char *strchr(M char *, int c);

and
template <modifier M>
M T& operator[](int) M;

come to mind; the compiler can either generate two versions or one
version of the code depending what it feels is best (i.e. in the
case of volatile, can significant enough optimizations be done to make
two separate functions more efficient?)

Possibly, there is a better syntax for getting across the above idea;
a simple way of saying 'the return value should be const if all
parameters are const' would be less general, although probably
adequate.

--
Tim Hollebeek        |"What is love? 'tis not hereafter; Present mirth hath
PChem Grad Student   |present laughter; What's to come is still unsure: In
Princeton University |delay there lies no plenty; Then come kiss me, sweet and
---------------------|twenty, Youth's a stuff will not endure." -Twelfth Night





Author: ark@research.att.com (Andrew Koenig)
Date: 1995/07/26
Raw View
In article <3v4dim$9cu@cnn.Princeton.EDU> tim@handel.Princeton.EDU (Tim Hollebeek) writes:

> You end up needing two functions:
>
> const char *strchr(const char *s, int c)
> char *strchr(char *s, int c)
>
> with the additional annoyance that the second doesn't modify s, but
> you can't assert that since doing so would conflict with the first.

But it does potentially modify the memory pointed to by s,
in the sense that it returns a value that can be used to modify
that memory.
--
    --Andrew Koenig
      ark@research.att.com