Topic: New qualifier, pointer, and arrays


Author: dwalker07@snet.net (Daryle Walker)
Date: Wed, 25 Aug 2004 14:16:44 GMT
Raw View
In article <4pgki01973lifiufpnkahjfeisevnhrqhj@4ax.com>,
 invalid@bigfoot.com (Bob Hairgrove) wrote:

> On Mon, 23 Aug 2004 02:57:11 GMT, dwalker07@snet.net (Daryle Walker)
> wrote:
>
> [snip]
> >Let's add "explicit" as a puesdo-qualifier like:
> >
> >   MyDerived  x, y;
> >   MyType     z;
> >
> >   MyType explicit *  p = &x;
> >   MyType const explicit &  r = y;
> >   MyType explicit &r2 = z;
> >
> >For such qualified variables, "MyType" is always treated as the final
> >type.  This suppresses virtual method dispatch.  We could even allow
> >this go all the way to deletion (which would cause undefined behavior).
> >The compiler should optimize due to the lack of virtual method searches.
>
> Well, it isn't required to write virtual functions at all ... if you
> don't want them, don't write them (P.S. :  C++ only has functions, not
> "methods").
>
> As to the rest of (1) above, Hyman Rosen has already pointed out that
> the language currently allows explicit calls to base class member
> functions.

Neither suggestion let's you suppress virtual member-function dispatch
after-the-fact on a object-wide basis (instead of per-call).

> >Using "explicit" on non-class types has no effect.  (Supports templates
> >better.)
>
> >2. New pointers
> >
> >A problem with "MyType *" is that it's overloaded.  It can be:
> >
> >   a. pointer to a single object of "MyType"
> >   b. pointer to a single object of "MyDerived"
> >   c. pointer into an array segment of "MyType"
> >   d. combine [b] and [c]: pointer into an array segment of "MyDerived"
> >
> >Usually [d] is an unfortunate combination of events that leads to
> >disaster when going beyond the first element.
> >
> You would do well to read Item 3 of Scott Meyers' book "More Effective
> C++", published by Addison-Wesley, titled: "Never treat arrays
> polymorphically".

I don't have that book.  What does that item say?

I never said that [d] is a valid technique.  It is caused the synergy
between [b] and [c], and we can't stop it.

> >The "explicit" syntax in [1] should help distinguishing between [a] and
> >[b].  We can go further:
> >
> >   MyType $                 pp = &z;
> >   MyType $                 pp1 = &y;
> >   MyType const explicit $  pp2 = &x;
> >   MyDerived $ const        pp3 = &y;
> >
> >The "$" is like "*", except it denotes a single object.  The array-like
> >operations of "*" are banned.  (This means no "[]", "++", "+", "-",
> >"--".)  Dereferencing still uses the unary "*" operator.
>
> >The unary "&" operator returns a "$" pointer.  (It should be "explicit"
> >when possible.)
> >
> >Now this distinguishes [a] from [c] from [a]'s side.  Let's do it from
> >[c]'s side.
> >
> >   MyType  aa[5];
> >   MyType[$]  ap = &aa[3];  // valid index: -3 thru 1 (with 2 as end)
>
> How would this be enforced (i.e. writing past array bounds)? After ap
> has been assigned, there is no way for the compiler to know that it
> came from memory declared as an array because it was initialized with
> a pointer value (i.e. the address of one of its elements). That memory
> could have come from anywhere...

It's not enforced.  Whatever happens if you go out-of-bounds when using
a "*" pointer happens here too.  I should have just used

   MyType[$]  ap = &aa[0];

which is one of the supposed standard uses (besides as a return from
new[]).  But the array-segment pointer supports index-shifting; that's
what my original example was supposed to show.  (I guess I confused you
by introducing with an advanced sample.)

> (BTW, the STL containers specify an unsigned integral type for index
> to std::vector, for example. Allowing negative indices is highly
> contra-intuitive).

AFAIK, negative indices are perfectly legal with "*" pointers (and
classic arrays).  And they do backtrack in that case.  Obviously you can
only do this if your first pointer was up-shifted into the array segment.

> >(The "$" is a separate token.  This whole idea adds a new source
> >character, BTW.)  This type bans dereferencing with the unary "*"
> >operator, but allows all the array-like operators.
> >
> >I don't like it, but I guess the "[$]" in declarations has to be parsed
> >like the "*" (and "$") on where the "const" can be stuck in.
> >
> >   const MyType[$]         bp1;
> >   MyType const [$]        bp2;
> >   MyType[$] const         bp3;
> >   MyType const [$] const  bp4;
> >
> >The "$" and "[$]" implicitly convert to the "*"-style pointer.  Going
> >the other way (or "$" <-> "[$]") requires an explicit cast.  Probably a
> >"static_cast".
>
> I think there is nothing gained by this, especially if you allow
> implicit conversions to normal pointers. It seems as if the idea is to
> enforce type safety in arrays ... is this true?

The "$" and "[$]" pointers have to implicitly convert to the "*" pointer
to be compatible with old code.

You misunderstand my idea.  These pointers are _not_ for enforcing array
type safety.  When you see a "char *" in code, you think of a classic
string, right?  What if you really meant an address of exactly one
"char"?  You would have to go out of your way to document it.  People
use a pointer in code for either exactly one object, or an array
segment, never both simultaneously (at least in sane code).  The new
pointers give extra information for the user that the compiler can
enforce.

These types are only different from each other, and the "*" pointer,
from the programmer's end.  The compiler gives them the same assembly
language as the "*" pointer internally (modulo any extra optimizations).

> What about null pointers, BTW?

They're allowed for both kinds of new pointers.  There's no valid
operations besides comparison and assignment for them, of course.

> >3. Changes to C++ operations
> >
> >*  Single operator "new" returns a "MyType explicit $"
> >*  Single operator "delete" takes the same type, or it can drop the
> >"explicit" if virtual destruction is to be enabled.
> >*  Array operator "new" returns a "MyType [$]"
> >*  Array operator "delete" takes the same type.
> >*  Actually, with distinct type markings for single objects and array
> >segments, the single "looking" delete can also do array segments (it
> >just secretly forwards to the array delete).
> >*  The main function has "char[$][$] argv" ("const" can be inserted if
> >desired)
> >*  std::auto_ptr uses "T $" for its pointer type
> >
> >4. Strong arrays
> >
> >Currently, arrays in C++ have no operations.  Everything works as if the
> >dumb array-to-pointer conversion happens first.  (Strangely, many
> >compilers optimize the use of array[x] over pointer[x].)
>
> Which ones? Can you be more specific about the ways in which the
> optimizations are implemented?

I don't know the specifics.  I remember reading about the true-array
optimization in a book called "Deep C Secrets" (over a decade ago).

When I developed Boost.CRC, I got a big boost in speed when switching my
array access from via-pointer to true-array.  So modern C++ compilers
still apply the optimization.  These compilers acknowledge the
differences between pointers and arrays, although the language proper
usually doesn't.

> >Before reciting the "use dynamic arrays, especially std::vector" speech,
> >realize that dynamic and static arrays solve different problems, so the
> >standard retort is useless.
>
> The current C++ standard guarantees that the memory allocated by an
> object of std::vector for its elements be contiguous, i.e. I can
> write:
>
>  std::vector<MyType> a;
>
> and pass &(a[0]) to a C function expecting a pointer to MyType or an
> array thereof.
>
> You can have your cake now and eat it, too <g>

You forgot that the memory block is dynamically allocated.  The
fanciness of the STL containers can't nullify the static allocation
advantage that regular (classic or strong) arrays have.

Before you complain that the "regular array can't be resized," realize
that I'm using a regular array in a context where I want the size to be
deliberately fixed at compile-time.  The flexibility of the STL
containers is useless overkill in this case.

> >After all, "struct" in ancient C had the
> >same limitation of not being directly assignable.  Fixing that allowed
> >C++ to be possible.
>
> "Ancient C"? It's not really that much older than C++, BTW (have you
> read "Design and Evolution of C++" by Bjarne Stroustrup?)

I'm talking about early K&R C.

The classic array is the only type that doesn't have proper direct
assignment.  Considering that other languages don't have that problem,
why don't we just fix our problem instead of giving up?  It won't affect
old code, after all (it makes previously illegal code legal).

> >   MyType[$5]  c;
> >
> >This is a strong array of 5 elements.  (As before, the "$" is a separate
> >token.)  Unlike the other decorators, the symbols stick with the type.
> >
> >   const MyType[$5]  d;  // OK
> >   MyType[$5] const  e;  // OK, same as previous
>
> Arrays themselves are always const, especially since they can only be
> declared statically or locally ... this last line doesn't make sense
> to me.

You're still thinking in the "arrays are just funky (constant) pointers"
mindset.  Let go of that broken C/C++ idea and realize that an array
state is mutable/constant based on the mutability/constant-ness of its
elements, so using const/volatile qualifiers are sensible.

> >   MyType const [$5]  f;  // NO, parsing error

This declaration is NOT supposed to make sense.  It's an example of a
bad parse.  The base type and the array-length are to be treated as a
single unit, so you can't put the qualifier between them.  You can't
have an array of constant elements, but a constant array of elements.

I guess no one has noticed the nasty parsing problem here.  (If you
remove the "5" then you get a valid declaration of a new-style
array-segment pointer to a constant "MyType".  So the parser has to read
all the way to the "5" before it can spit out an error.)

> What if you want an array of pointers to constant memory, the pointers
> themselves not being const? How would you do it?

   typedef MyType const *  MyPointer;

   MyPointer[$8]  ex;

I don't know if either of these should be legal direct alternatives

   MyType const * [$8]  ex1;

The [$8] has to group on the left side, but the "*" has to group on the
right.  Can that conflict be fixed?

   (MyType const *)[$8]  ex2;

I don't know if parentheses are legal here.

> >Assignment is supported, and it acts member-wise.  So do copy
> >construction, default construction, and destruction.  When it's a class
> >member there is an extra constructor taking the appropriate number of
> >elements.
> >
> >You can "new" a strong array directly, and it acts as a single new
> >
> >   MyType[$5] explicit $ const  g = new MyType[$5];
> >
> >(And it uses the single delete.)
> >
> >The only supported element-level operator is "[]".  (The weird "*(a +
> >i)" syntax is definitely _not_ supported, so no "3[e]" madness.)
>
> But that's exactly how operator[] is implemented unless it's
> overloaded: with pointer arithmetic.

One of my main points is that the "pointer-nature" of arrays should have
_never_ been there, let alone encouraged!  The internal nature of how
operator [] works didn't need to be exposed.  (Especially since the
pointer arithmetic interpretation is generally wrong; compilers optimize
the dereference for true-arrays.)

> >A strong array can be explicitly converted to an array-segment pointer,
> >using "reinterpret_cast".
> >
> >   MyType[$2] const  h;
> >   MyType const [$]  hh = (MyType const [$])h;
> >   assert( hh == (MyType const *)(&h[0]) );
> >
> >You can have arrays of arrays.  Due to where the length is placed...
> >
> >   typedef MyType[$3]  MT3;
> >
> >   MT3[$6]  j;
> >
> >   MyType[$3][$6]  jj;  // in this order so typeof(j) == typeof(jj)
> >
> >..the index usage order is the reverse of its declaration, the reverse
> >of classic arrays.  (Above, "j" and "jj" are 6-element strong arrays of
> >elements that are 3-element strong arrays [each] of "MyType".)
> >
> >   use "j" like j[0..5][0..2];
> >   j[0][0], j[0][1], j[0][2], j[1][0], j[1][1], j[1][2],
> >   j[2][0], j[2][1], j[2][2], j[3][0], j[3][1], j[3][2],
> >   j[4][0], j[4][1], j[4][2], j[5][0], j[5][1], j[5][2]
> >
> >I guess you can mix strong and classic array declarators.  The classic
> >array indices go first, then the strong parts.
> >
> >   MyType[$2][$5]  kk[7][11];  // kk[0..6][0..10][0..4][0..1]
> >
> >"kk" is a 7-element classic array of 11-element classic arrays, where
> >each element is a 5-element strong array of 2-element strong arrays.
>
> The whole point of implementing the STL was so that one wouldn't have
> to deal with arrays for many common tasks. With the newest version of
> the C++ standard, we have a strong guarantee about contiguous
> allocation of memory that allows us to treat a vector like an array if
> necessary. Therefore, I don't believe your idea will buy anything.

Then the inventors of STL solved the problem the wrong way.  The problem
was the classic arrays had insane semantics.  The right solution would
be to fix arrays (especially since it would have, AFAIK, only involved
adding behavior, without worries of breaking old code).  Remember that
one of the top C/C++ tenets is "don't pay for what you don't use."
Using STL containers to fix the array problem is the ultimate betrayal
of that idea!

The so-called "recent fix" of ensuring that std::vector uses contiguous
memory is a "straw man."  It did nothing to change the fact that STL
containers use dynamic memory while regular arrays don't.

The only good STL containers did for this problem was to make
programmers that wanted dynamic sizing go to types that were made for
it, instead of trying force dynamic semantics into regular arrays.  And
this isn't a flaw of regular arrays, it's just a case of programmers
trying to use the _wrong_ tool for what they really needed.

I don't think STL sucks, though.  It did solve a bunch of problems, just
not the one that was intended.

> This wheel has already been invented, so to speak.

Nope, that just an (unfortunately popular) misconception.

--
Daryle Walker
Mac, Internet, and Video Game Junkie
dwalker07 AT snet DOT net

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: invalid@bigfoot.com (Bob Hairgrove)
Date: Tue, 24 Aug 2004 02:53:18 GMT
Raw View
On Mon, 23 Aug 2004 02:57:11 GMT, dwalker07@snet.net (Daryle Walker)
wrote:

[snip]
>Let's add "explicit" as a puesdo-qualifier like:
>
>   MyDerived  x, y;
>   MyType     z;
>
>   MyType explicit *  p = &x;
>   MyType const explicit &  r = y;
>   MyType explicit &r2 = z;
>
>For such qualified variables, "MyType" is always treated as the final
>type.  This suppresses virtual method dispatch.  We could even allow
>this go all the way to deletion (which would cause undefined behavior).
>The compiler should optimize due to the lack of virtual method searches.

Well, it isn't required to write virtual functions at all ... if you
don't want them, don't write them (P.S. :  C++ only has functions, not
"methods").

As to the rest of (1) above, Hyman Rosen has already pointed out that
the language currently allows explicit calls to base class member
functions.

>Using "explicit" on non-class types has no effect.  (Supports templates
>better.)

>2. New pointers
>
>A problem with "MyType *" is that it's overloaded.  It can be:
>
>   a. pointer to a single object of "MyType"
>   b. pointer to a single object of "MyDerived"
>   c. pointer into an array segment of "MyType"
>   d. combine [b] and [c]: pointer into an array segment of "MyDerived"
>
>Usually [d] is an unfortunate combination of events that leads to
>disaster when going beyond the first element.
>
You would do well to read Item 3 of Scott Meyers' book "More Effective
C++", published by Addison-Wesley, titled: "Never treat arrays
polymorphically".

>The "explicit" syntax in [1] should help distinguishing between [a] and
>[b].  We can go further:
>
>   MyType $                 pp = &z;
>   MyType $                 pp1 = &y;
>   MyType const explicit $  pp2 = &x;
>   MyDerived $ const        pp3 = &y;
>
>The "$" is like "*", except it denotes a single object.  The array-like
>operations of "*" are banned.  (This means no "[]", "++", "+", "-",
>"--".)  Dereferencing still uses the unary "*" operator.

>The unary "&" operator returns a "$" pointer.  (It should be "explicit"
>when possible.)
>
>Now this distinguishes [a] from [c] from [a]'s side.  Let's do it from
>[c]'s side.
>
>   MyType  aa[5];
>   MyType[$]  ap = &aa[3];  // valid index: -3 thru 1 (with 2 as end)

How would this be enforced (i.e. writing past array bounds)? After ap
has been assigned, there is no way for the compiler to know that it
came from memory declared as an array because it was initialized with
a pointer value (i.e. the address of one of its elements). That memory
could have come from anywhere...

(BTW, the STL containers specify an unsigned integral type for index
to std::vector, for example. Allowing negative indices is highly
contra-intuitive).

>(The "$" is a separate token.  This whole idea adds a new source
>character, BTW.)  This type bans dereferencing with the unary "*"
>operator, but allows all the array-like operators.
>
>I don't like it, but I guess the "[$]" in declarations has to be parsed
>like the "*" (and "$") on where the "const" can be stuck in.
>
>   const MyType[$]         bp1;
>   MyType const [$]        bp2;
>   MyType[$] const         bp3;
>   MyType const [$] const  bp4;
>
>The "$" and "[$]" implicitly convert to the "*"-style pointer.  Going
>the other way (or "$" <-> "[$]") requires an explicit cast.  Probably a
>"static_cast".

I think there is nothing gained by this, especially if you allow
implicit conversions to normal pointers. It seems as if the idea is to
enforce type safety in arrays ... is this true?

What about null pointers, BTW?

>3. Changes to C++ operations
>
>*  Single operator "new" returns a "MyType explicit $"
>*  Single operator "delete" takes the same type, or it can drop the
>"explicit" if virtual destruction is to be enabled.
>*  Array operator "new" returns a "MyType [$]"
>*  Array operator "delete" takes the same type.
>*  Actually, with distinct type markings for single objects and array
>segments, the single "looking" delete can also do array segments (it
>just secretly forwards to the array delete).
>*  The main function has "char[$][$] argv" ("const" can be inserted if
>desired)
>*  std::auto_ptr uses "T $" for its pointer type
>
>4. Strong arrays
>
>Currently, arrays in C++ have no operations.  Everything works as if the
>dumb array-to-pointer conversion happens first.  (Strangely, many
>compilers optimize the use of array[x] over pointer[x].)

Which ones? Can you be more specific about the ways in which the
optimizations are implemented?

>Before reciting the "use dynamic arrays, especially std::vector" speech,
>realize that dynamic and static arrays solve different problems, so the
>standard retort is useless.

The current C++ standard guarantees that the memory allocated by an
object of std::vector for its elements be contiguous, i.e. I can
write:

 std::vector<MyType> a;

and pass &(a[0]) to a C function expecting a pointer to MyType or an
array thereof.

You can have your cake now and eat it, too <g>

>After all, "struct" in ancient C had the
>same limitation of not being directly assignable.  Fixing that allowed
>C++ to be possible.

"Ancient C"? It's not really that much older than C++, BTW (have you
read "Design and Evolution of C++" by Bjarne Stroustrup?)

>   MyType[$5]  c;
>
>This is a strong array of 5 elements.  (As before, the "$" is a separate
>token.)  Unlike the other decorators, the symbols stick with the type.
>
>   const MyType[$5]  d;  // OK
>   MyType[$5] const  e;  // OK, same as previous

Arrays themselves are always const, especially since they can only be
declared statically or locally ... this last line doesn't make sense
to me.

>   MyType const [$5]  f;  // NO, parsing error

What if you want an array of pointers to constant memory, the pointers
themselves not being const? How would you do it?

>Assignment is supported, and it acts member-wise.  So do copy
>construction, default construction, and destruction.  When it's a class
>member there is an extra constructor taking the appropriate number of
>elements.
>
>You can "new" a strong array directly, and it acts as a single new
>
>   MyType[$5] explicit $ const  g = new MyType[$5];
>
>(And it uses the single delete.)
>
>The only supported element-level operator is "[]".  (The weird "*(a +
>i)" syntax is definitely _not_ supported, so no "3[e]" madness.)

But that's exactly how operator[] is implemented unless it's
overloaded: with pointer arithmetic.

>A strong array can be explicitly converted to an array-segment pointer,
>using "reinterpret_cast".
>
>   MyType[$2] const  h;
>   MyType const [$]  hh = (MyType const [$])h;
>   assert( hh == (MyType const *)(&h[0]) );
>
>You can have arrays of arrays.  Due to where the length is placed...
>
>   typedef MyType[$3]  MT3;
>
>   MT3[$6]  j;
>
>   MyType[$3][$6]  jj;  // in this order so typeof(j) == typeof(jj)
>
>..the index usage order is the reverse of its declaration, the reverse
>of classic arrays.  (Above, "j" and "jj" are 6-element strong arrays of
>elements that are 3-element strong arrays [each] of "MyType".)
>
>   use "j" like j[0..5][0..2];
>   j[0][0], j[0][1], j[0][2], j[1][0], j[1][1], j[1][2],
>   j[2][0], j[2][1], j[2][2], j[3][0], j[3][1], j[3][2],
>   j[4][0], j[4][1], j[4][2], j[5][0], j[5][1], j[5][2]
>
>I guess you can mix strong and classic array declarators.  The classic
>array indices go first, then the strong parts.
>
>   MyType[$2][$5]  kk[7][11];  // kk[0..6][0..10][0..4][0..1]
>
>"kk" is a 7-element classic array of 11-element classic arrays, where
>each element is a 5-element strong array of 2-element strong arrays.

The whole point of implementing the STL was so that one wouldn't have
to deal with arrays for many common tasks. With the newest version of
the C++ standard, we have a strong guarantee about contiguous
allocation of memory that allows us to treat a vector like an array if
necessary. Therefore, I don't believe your idea will buy anything.

This wheel has already been invented, so to speak.


--
Bob Hairgrove
NoSpamPlease@Home.com

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: hyrosen@mail.com (Hyman Rosen)
Date: Tue, 24 Aug 2004 23:48:22 GMT
Raw View
Bob Hairgrove wrote:
> As to the rest of (1) above, Hyman Rosen has already pointed out that
> the language currently allows explicit calls to base class member
> functions.

Actually, there is one place where this cannot be done.
If you have a pointer to a virtual member, there is no
way to dispatch to a particular class's version of the
function.

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: dwalker07@snet.net (Daryle Walker)
Date: Mon, 23 Aug 2004 02:57:11 GMT
Raw View
[I think I saw the "$" idea from a post on c.l.c++.m a while ago.]

1. Suppressing the dereferencing of sub-types

If "MyType" is a class type, then an object with type "MyType *" can
point to a "MyType" object or to "MyDerived" object, where "MyType" is a
direct or indirect base of "MyDerived".  (A similar condition exists for
"MyType &".)  If "MyType" has virtual member functions, then calling
those members always does a check to the final type before activating
the appropriate function.

I want to stop that.

Let's add "explicit" as a puesdo-qualifier like:

   MyDerived  x, y;
   MyType     z;

   MyType explicit *  p = &x;
   MyType const explicit &  r = y;
   MyType explicit &r2 = z;

For such qualified variables, "MyType" is always treated as the final
type.  This suppresses virtual method dispatch.  We could even allow
this go all the way to deletion (which would cause undefined behavior).
The compiler should optimize due to the lack of virtual method searches.

Using "explicit" on non-class types has no effect.  (Supports templates
better.)

2. New pointers

A problem with "MyType *" is that it's overloaded.  It can be:

   a. pointer to a single object of "MyType"
   b. pointer to a single object of "MyDerived"
   c. pointer into an array segment of "MyType"
   d. combine [b] and [c]: pointer into an array segment of "MyDerived"

Usually [d] is an unfortunate combination of events that leads to
disaster when going beyond the first element.

The "explicit" syntax in [1] should help distinguishing between [a] and
[b].  We can go further:

   MyType $                 pp = &z;
   MyType $                 pp1 = &y;
   MyType const explicit $  pp2 = &x;
   MyDerived $ const        pp3 = &y;

The "$" is like "*", except it denotes a single object.  The array-like
operations of "*" are banned.  (This means no "[]", "++", "+", "-",
"--".)  Dereferencing still uses the unary "*" operator.

The unary "&" operator returns a "$" pointer.  (It should be "explicit"
when possible.)

Now this distinguishes [a] from [c] from [a]'s side.  Let's do it from
[c]'s side.

   MyType  aa[5];
   MyType[$]  ap = &aa[3];  // valid index: -3 thru 1 (with 2 as end)

(The "$" is a separate token.  This whole idea adds a new source
character, BTW.)  This type bans dereferencing with the unary "*"
operator, but allows all the array-like operators.

I don't like it, but I guess the "[$]" in declarations has to be parsed
like the "*" (and "$") on where the "const" can be stuck in.

   const MyType[$]         bp1;
   MyType const [$]        bp2;
   MyType[$] const         bp3;
   MyType const [$] const  bp4;

The "$" and "[$]" implicitly convert to the "*"-style pointer.  Going
the other way (or "$" <-> "[$]") requires an explicit cast.  Probably a
"static_cast".

3. Changes to C++ operations

*  Single operator "new" returns a "MyType explicit $"
*  Single operator "delete" takes the same type, or it can drop the
"explicit" if virtual destruction is to be enabled.
*  Array operator "new" returns a "MyType [$]"
*  Array operator "delete" takes the same type.
*  Actually, with distinct type markings for single objects and array
segments, the single "looking" delete can also do array segments (it
just secretly forwards to the array delete).
*  The main function has "char[$][$] argv" ("const" can be inserted if
desired)
*  std::auto_ptr uses "T $" for its pointer type

4. Strong arrays

Currently, arrays in C++ have no operations.  Everything works as if the
dumb array-to-pointer conversion happens first.  (Strangely, many
compilers optimize the use of array[x] over pointer[x].)  Before
reciting the "use dynamic arrays, especially std::vector" speech,
realize that dynamic and static arrays solve different problems, so the
standard retort is useless.  After all, "struct" in ancient C had the
same limitation of not being directly assignable.  Fixing that allowed
C++ to be possible.

   MyType[$5]  c;

This is a strong array of 5 elements.  (As before, the "$" is a separate
token.)  Unlike the other decorators, the symbols stick with the type.

   const MyType[$5]  d;  // OK
   MyType[$5] const  e;  // OK, same as previous

   MyType const [$5]  f;  // NO, parsing error

Assignment is supported, and it acts member-wise.  So do copy
construction, default construction, and destruction.  When it's a class
member there is an extra constructor taking the appropriate number of
elements.

You can "new" a strong array directly, and it acts as a single new

   MyType[$5] explicit $ const  g = new MyType[$5];

(And it uses the single delete.)

The only supported element-level operator is "[]".  (The weird "*(a +
i)" syntax is definitely _not_ supported, so no "3[e]" madness.)

A strong array can be explicitly converted to an array-segment pointer,
using "reinterpret_cast".

   MyType[$2] const  h;
   MyType const [$]  hh = (MyType const [$])h;
   assert( hh == (MyType const *)(&h[0]) );

You can have arrays of arrays.  Due to where the length is placed...

   typedef MyType[$3]  MT3;

   MT3[$6]  j;

   MyType[$3][$6]  jj;  // in this order so typeof(j) == typeof(jj)

..the index usage order is the reverse of its declaration, the reverse
of classic arrays.  (Above, "j" and "jj" are 6-element strong arrays of
elements that are 3-element strong arrays [each] of "MyType".)

   use "j" like j[0..5][0..2];
   j[0][0], j[0][1], j[0][2], j[1][0], j[1][1], j[1][2],
   j[2][0], j[2][1], j[2][2], j[3][0], j[3][1], j[3][2],
   j[4][0], j[4][1], j[4][2], j[5][0], j[5][1], j[5][2]

I guess you can mix strong and classic array declarators.  The classic
array indices go first, then the strong parts.

   MyType[$2][$5]  kk[7][11];  // kk[0..6][0..10][0..4][0..1]

"kk" is a 7-element classic array of 11-element classic arrays, where
each element is a 5-element strong array of 2-element strong arrays.

--
Daryle Walker
Mac, Internet, and Video Game Junkie
dwalker07 AT snet DOT net

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: hyrosen@mail.com (Hyman Rosen)
Date: Mon, 23 Aug 2004 15:53:46 GMT
Raw View
Daryle Walker wrote:
 > If "MyType" has virtual member functions, then calling  those members
 > always does a check to the final type before activating the appropriate
 > function. I want to stop that.

You seem to be unaware that doing p->MyType::f() will do that.

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]





Author: dwalker07@snet.net (Daryle Walker)
Date: Mon, 23 Aug 2004 20:01:56 GMT
Raw View
In article <uEdWc.8213$IO1.1514@trndny03>,
 hyrosen@mail.com (Hyman Rosen) wrote:

> Daryle Walker wrote:
>  > If "MyType" has virtual member functions, then calling  those members
>  > always does a check to the final type before activating the appropriate
>  > function. I want to stop that.
>
> You seem to be unaware that doing p->MyType::f() will do that.

But that's on a call-by-call basis.  The "explicit"-qualifier option
puts the suppression on a pointer-by-pointer basis, affecting all calls
through the same pointer (or reference).

I wasn't envisioning the new qualifier for this purpose, but more as
part of the "$"-style single new.  Attaching "explicit" to the return of
"new" makes the user aware when passing to a context that loses
"explicit"-ness (i.e. when you want the virtual dispatch to occur).

I'm not sure which way the conversion should be implicit between "T *"
and "T explicit *".  Nor do I know if "Derived explicit *" should
implicitly convert to "Base explicit *" (or "Base *").

--
Daryle Walker
Mac, Internet, and Video Game Junkie
dwalker07 AT snet DOT net

---
[ 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.jamesd.demon.co.uk/csc/faq.html                       ]