Teaching new tricks to an old dog (C++ -->Ada)

  • Thread starter Turamnvia Suouriviaskimatta
  • Start date
G

Guest

Falk Tannhäuser said:
Perhaps the closest way you can get to this in C++ is

std::vector<foo_type> Data;
...
std::for_each(Data.begin(), Data.end(), DoSomething);

where "DoSomething" evaluates to a so-called "function object"
having an "operator()" accepting a (reference to) "foo_type".
OK. Try this in straight C++.

type Index is range -800_000..12_000_000;
type Decimal_Fraction is digits 12 range -473.0 .. 2_000.0;

type Vector is array (Index range <>) of Decimal_Fraction;

V1 : Vector ( -47..600); -- note the negative index range
V2 : Vector (-1.. 10); -- also a negative index range
V3 : Vector (42..451); -- index starts higher than zero;
...............................

function Scan (The_Vector : Vector ; Scan_Value : Decimal_Fraction )
return Natural;
-- This function can process any of those vector instances
without modification
.............................
-- possible implementation of the function
function Scan(The_Vector : Vector; Scan_Value : Decimal_Fraction )
return Natrual is
Scan_Count : Natural := 0; -- Natual begins at zero
begin
for I in The_Vector'Range -- No way to index off the end array
parameter
loop
if The_Vector(I) = Scan_Value; -- looking for an
exact match
Scan_Count := Scan_Count + 1; -- increment the count
end if;
end loop;
return Scan_Count; -- return required; never an implicit
return
end Scan;
.....................................................

I submit this in response to the observation someone made about the alleged
added
difficulty Ada imposes on the programmer in flexibility of expressiveness. In
my
own experience, Ada is far more expressive of a larger range of idioms than C++.
Note my use of the word "expressive." We can express any idea in any language,
but some languages are more expressive of some ideas than others.

This is just one example of Ada's flexibility in managing arrays. I could fill
many pages with
more examples. Of interest, here, is how easy it is to have an array index
that begins at
a value other than zero, and how easy it is to create a function that will
accept any array
of any range defined for that index. Yes, I know you can do this in C++, but
from my
perspective, it is not nearly as easy, expressive, or readable.

Counter examples to expressiveness can be illustrated in C++. For example,
many
programmers prefer the += to the x := x + 1 syntax. This is a minor
convenience
compared to the final program in Ada.

Richard Riehle

Disclaimer: I did not compile the code
before submitting it so there
might be some minor
errors. RR
 
I

Ioannis Vranos

OK. Try this in straight C++.

type Index is range -800_000..12_000_000;
type Decimal_Fraction is digits 12 range -473.0 .. 2_000.0;

type Vector is array (Index range <>) of Decimal_Fraction;

V1 : Vector ( -47..600); -- note the negative index range
V2 : Vector (-1.. 10); -- also a negative index range
V3 : Vector (42..451); -- index starts higher than zero;
...............................

function Scan (The_Vector : Vector ; Scan_Value : Decimal_Fraction )
return Natural;
-- This function can process any of those vector instances
without modification
.............................
-- possible implementation of the function
function Scan(The_Vector : Vector; Scan_Value : Decimal_Fraction )
return Natrual is
Scan_Count : Natural := 0; -- Natual begins at zero
begin
for I in The_Vector'Range -- No way to index off the end array
parameter
loop
if The_Vector(I) = Scan_Value; -- looking for an
exact match
Scan_Count := Scan_Count + 1; -- increment the count
end if;
end loop;
return Scan_Count; -- return required; never an implicit
return
end Scan;
.....................................................

I submit this in response to the observation someone made about the alleged
added
difficulty Ada imposes on the programmer in flexibility of expressiveness. In
my
own experience, Ada is far more expressive of a larger range of idioms than C++.
Note my use of the word "expressive." We can express any idea in any language,
but some languages are more expressive of some ideas than others.


I think you mean something like:


#include <map>
#include <iostream>

int main()
{
using namespace std;

map<int, double> id;

id[-400]= 7.1;

id[-2500]= -1;

id[10]= 9;

id[-300]= 7.1;

unsigned counter= 0;

for(map<int, double>::const_iterator p= id.begin(); p!=id.end(); ++p)
{
if(p->second== 7.1)
{
cout<<"7.1 was found at index "<<p->first<<"\n";

++counter;
}
}

cout<<"\n7.1 was found "<<counter<<" times in total.\n";
}


C:\c>temp
7.1 was found at index -400
7.1 was found at index -300

7.1 was found 2 times in total.

C:\c>


I think this is even faster than yours, which scans the entire range.


And a more elegant and with the same efficiency solution:


#include <map>
#include <iostream>
#include <algorithm>


class comp: public std::unary_function<std::pair<int, double>, bool>
{
const double searchValue;

public:

comp(const double value):searchValue(value) {}

bool operator() (const std::pair<int, double> &arg) const
{
if(arg.second==searchValue)
return true;

return false;
}
};



int main()
{
using namespace std;

map<int, double> id;

id[-400]= 7.1;

id[-2500]= -1;

id[10]= 9;

id[-300]= 7.1;

cout<<"\n7.1 was found "<<count_if(id.begin(), id.end(), comp(7.1))
<<" times in total.\n";
}


C:\c>temp

7.1 was found 2 times in total.

C:\c>

This is just one example of Ada's flexibility in managing arrays. I could fill
many pages with
more examples. Of interest, here, is how easy it is to have an array index
that begins at
a value other than zero, and how easy it is to create a function that will
accept any array
of any range defined for that index.


In C++ you can create whatever you want. Even a container with abstract
conceptual features (you are limited only by your imagination).

Yes, I know you can do this in C++, but
from my
perspective, it is not nearly as easy, expressive, or readable.


I think even better. That said, I like enough Ada too. :)
 
W

Wes Groleau

Possibly. However, I have seen more than enough "hacked" Ada to wonder
whether, in the hands of the larger community, the unruly would still run amuck,
even with Ada as their language. Although the default for most constructs in

I don't know how much effort I've put into persuading colleagues
that an Ada access type and a C pointer are NOT always interchangeable.

Or how many times they turned a deaf ear to my recommendation to
think about a warning (that the source and target of an
unchecked_conversion instantiation were not the same size).
 
L

Larry Kilgallen

If one has a copy of the validation test suite, why is it necessary
to wait for the users to find the bugs? Is it that you have to pay
for the cost of fixing the bugs that the users would never encounter?

One of the costs of fixing defects not reported by a user is the non-zero
probability that making a change will introduce additional defects.

Clearly that is unacceptable if the chance of some user discovering
the original defect can be proven to be zero.

It is required (someday) if the chance of some user discovering
the original defect can be proven to be one.

Unfortunately, most such probabilities lie somewhere in the vast middle.
 
G

Georg Bauhaus

I think you mean something like:

[redesigning the problem?]
map<int, double> id;

id[-400]= 7.1;
for(map<int, double>::const_iterator p= id.begin(); p!=id.end(); ++p)
I think this is even faster than yours, which scans the entire range.

Hm. Searching a *map* of entries at numeric keys is different
from scanning an array of values and counting occurences. What
are you trying to do here?
The std::vector is missing an instantiation argument which adds
the guarantee that no index value is outside the range
-800_000..12_000_000;
std::map<int, double> is a different beast entirely, with
unknown size. Consider Vector ( -47..600);

(How do you make a subrange of double, which is missing from
your example.)

Imagine an array shared between a number of threads. The program's
task is to count the number of occurences of a particular value
in the array. Examples:
1) A shop has 10 unique doors (use an enum). For each door 4 states
can be measured: open/closed, van/no van at the door.
2) A 5-player team, each team is identified by number drawn from a fixed
set of team numbers. An array (an array, not some other data
structure) measures the number of players from each team present in
a room. Count the number of odd-team players in a room.

I hope these example illustrate some points. They are not meant to
trigger a discussion as to whether an array is the best data
structure for everything. (Note that it might be necessary to read
values from the array/Vector using random access in O(1), and to
store and replace values in O(1), another reason to use an array.)

And a more elegant and with the same efficiency solution:
A solution to a different problem, I think.

In C++ you can create whatever you want. Even a container with abstract
conceptual features (you are limited only by your imagination).

You can do that using assembly language or SmallTalk, whatever.
I think this was not the point, but I should have Richard Riehle's
message speak for itself.
For example, look here, and reconsider the programming task as
originally stated:
Georg
 
G

Guest

T Beck said:
Would that happen to include the now-infamous Patriot Missles (the ones
that couldn't hit a barn after they'd been on for a while due to a
bug?)

No. Those missiles were not programmed in Ada.

On the other hand, there have been software failures written
in Ada, just as their have been software failures written in C,
C++, Jovial, Fortran, etc.

The use of Ada does not eliminate to potential for software failure.
No language, by itself, can guarantee there will no defects. The
best we can hope for, in the current state of software engineering,
is to minimize the risk of failure.

Ada is designed to help reduce risk. That is a primary concern when
choosing Ada. Still, we are always dealing with tradeoffs.

A language that allows compilers to generate some kind of result for
every syntactic expression, is likely to also permit erroneous constructs.
This is OK when that language is used in the hands of a person who
never makes mistakes. Of course, as the size of the code grows
larger, and the number of functions increases, human comprehension
begins face more challenges in the management of the complexity.

A language that provides a set of constraints at the outset, such as
Ada, can be annoying for little programming jobs carried out by
one person. As the software product requires more and more
people, more and more code units, and more and more time to
complete, these constraints can be useful because they prohibit
certain practices that lead to unexpected errors.

Software risk management is becoming a more and more respectable
part of software engineering practice. Language choice is only one
part of the risk management decision matrix, and we are not going to
enumerate the many other facets of risk here. However, language
can have an impact on risk.

I have seen software developed in many different languages over a
long period of time. In my own view, and I don't have formal
research to support this, Ada has had value in reducing risk of
failure "when used as directed." C++, and the C family in general,
increases the risk of failure simply because one does not have the
built-in constraints that exist in Ada. A careful programmer can
create excellent, defect-reduced, software in C++. A sloppy
programmer can grind out horrible, unsafe code in Ada (by
relaxing the built-in constraints). All in all, though, I see Ada as
more risk averse than most of its competitors.

What is the cost of being risk averse? That question is open to many
different interpretations. There certainly is a cost. Is that cost worth
the level of risk avoidance we hope to achieve with Ada? The answer
to this question will also vary according to the experience, biases, and
economic decisions favored by those who must make the choices.

Richard Riehle
 
L

Larry Kilgallen

A language that provides a set of constraints at the outset, such as
Ada, can be annoying for little programming jobs carried out by
one person. As the software product requires more and more
people, more and more code units, and more and more time to
complete, these constraints can be useful because they prohibit
certain practices that lead to unexpected errors.

I think there is another figure of merit which should be considered
along with the number of programmers -- the proximity of users to a
single programmer.

To support a hobby of mine, I will single-handedly program in TECO
macros (a strong competitor to APL in the write-only-programming race).
I am the only one who runs the program, and there is nowhere else
to point the finger if it fails.

But for real commercial code (one now at about 200,000 sloc), I use
Ada as a single-programmer, since I want my programming failures to
be evident on my computer, not that of the customer of my customer.
 
F

fabio de francesco

Ioannis said:
OK. Try this in straight C++.
[skip Ada program]
I think you mean something like:
[skip C++ programs]
This is just one example of Ada's flexibility in managing arrays. I could fill
many pages with
more examples. Of interest, here, is how easy it is to have an array index
that begins at
a value other than zero, and how easy it is to create a function that will
accept any array
of any range defined for that index.


In C++ you can create whatever you want. Even a container with abstract
conceptual features (you are limited only by your imagination).

Yes, I know you can do this in C++, but
from my
perspective, it is not nearly as easy, expressive, or readable.


I think even better. That said, I like enough Ada too. :)

Ciao,

Ioannis wrote a C++ program that in his intentions would had operate
like the Ada code that was provided as an example of language
expressiveness.

Unfortunatelly that C++ code doesn't resembles the Ada one. Have a
closer look at what Richard wrote:

type Index is range -800_000..12_000_000;
type Decimal_Fraction is digits 12 range -473.0 .. 2_000.0;
type Vector is array (Index range <>) of Decimal_Fraction;

V1 : Vector ( -47..600); -- note the negative index range
V2 : Vector (-1.. 10); -- also a negative index range
V3 : Vector (42..451); -- index starts higher than zero;

He instantiated three arrays that contain elements of type
Decimal_Fraction, each of them is 12 or more digits in whatever machine
the program can compile and each variable of the type is checked to be
in range -473.0 .. 2_000.0.
Each single array has a different number of elements and no array with
more than 12_800_001 elements is permitted to be created.

Furthermore you used a tree (std::map<> is often implemented as a
red-black tree) to be read sequentially in searching the second
element. I don't think this is the most efficient way to implement what
it is better thought to be an array. I mean that if I always have to
read every element from the beginning to the end I would prefer to
insert them in a vector.

Now I remember that you wrote "This is faster than yours scanning the
entire range". Now I can be wrong, since I don't know either enough C++
and Ada too, but it seems that you scanned the entire range too. Is it
not true? Anyway I suppose that Richard attention was to array
declarations and not to function operating on them.

Regards,

fabio de francesco
 
I

Ioannis Vranos

Georg said:
Hm. Searching a *map* of entries at numeric keys is different
from scanning an array of values and counting occurences. What
are you trying to do here?


Just using the appropriate available container. :)

The std::vector is missing an instantiation argument which adds
the guarantee that no index value is outside the range
-800_000..12_000_000;


vector provides method at() that performs range checking. Also if you
want a vector that has signed integer subscripts or even floating point,
you can easily write one. However this does not "feel" C++ whose built
in arrays always store elements from index 0 and upwards.


std::map<int, double> is a different beast entirely, with
unknown size. Consider Vector ( -47..600);

(How do you make a subrange of double, which is missing from
your example.)



Do you mean like this?

#include <map>
#include <iostream>

int main()
{
using namespace std;

map<double, double> id;

id[-400.1]= 7.1;

id[-2500.6]= -1;

id[10.43]= 9;

id[-300.65]= 7.1;

unsigned counter= 0;

for(map<double, double>::const_iterator p= id.begin(); p!=id.end(); ++p)
{
if(p->second== 7.1)
{
cout<<"7.1 was found at index "<<p->first<<"\n";

++counter;
}
}

cout<<"\n7.1 was found "<<counter<<" times in total.\n";
}


C:\c>temp
7.1 was found at index -400.1
7.1 was found at index -300.65

7.1 was found 2 times in total.

C:\c>



You can also check the entire range if you want. Here the previous style
along with your full-range (expensive) style:


#include <map>
#include <iostream>

int main()
{
using namespace std;

map<int, double> id;

id[-400]= 7.1;

id[-2500]= -1;

id[10]= 9;

id[-300]= 7.1;

unsigned counter= 0;

for(map<int, double>::const_iterator p= id.begin(); p!=id.end(); ++p)
{
if(p->second== 7.1)
{
cout<<"7.1 was found at index "<<p->first<<"\n";

++counter;
}
}

cout<<"\n7.1 was found "<<counter<<" times in total.\n\n\n";


// Checks *each* range from -2500 to 10
counter=0;
for(int i= -2500; i<10; ++i)
{
if(id== 7.1)
{
cout<<"7.1 was found at index "<<i<<"\n";

++counter;
}
}

cout<<"\n7.1 was found "<<counter<<" times in total.\n";
}


"for(int i= -2500; i<10; ++i)" can also be written as

for(int i= id.begin()->first; i<(--id.end())->first; ++i)

if you want this kind of abstraction. But better abstraction is the
iterator approach and the *best* the count family which (iterator and
count family) work with *all* containers.


These are bullet-proof code approaches.


Imagine an array shared between a number of threads. The program's
task is to count the number of occurences of a particular value
in the array. Examples:
1) A shop has 10 unique doors (use an enum).


We can use whatever I like, perhaps strings. What is wrong with "Door 1"
etc? :)

For each door 4 states
can be measured: open/closed, van/no van at the door.


OK, this sounds easy. What do you think? Since you want an array:


#include <vector>
#include <algorithm>
#include <functional>
#include <iostream>

class Door
{
bool open, van;

public:
Door(){ open= van= true; }

bool IsOpen() const { return open; }
bool IsClosed() const { return !open; }
bool IsVan() const { return van; }
bool IsNoVan() const { return !van; }

Door &SetOpen() { open= true; return *this; }
Door &SetClosed() { open= false; return *this; }
Door &SetVan() { van= true; return *this; }
Door &SetNoVan() { van= false; return *this; }
};

class CheckOpen
{
bool open, van;

public:
CheckOpen(bool isopen, bool isvan):eek:pen(isopen), van(isvan) {}

bool operator() (const Door &arg)
{
return open== arg.IsOpen() && van== arg.IsVan();
}
};


int main()
{
using namespace std;

vector<Door> doors(10);

doors[4].SetOpen().SetNoVan();

unsigned counter= count_if(doors.begin(), doors.end(),
CheckOpen(true, false));

cout<<"Counted "<<counter<<" occurrences.\n";
}


C:\c>temp
Counted 1 occurrences.

C:\c>



2) A 5-player team, each team is identified by number drawn from a fixed
set of team numbers. An array (an array, not some other data
structure) measures the number of players from each team present in
a room. Count the number of odd-team players in a room.


This is easy too. I do not get your point. This can be done with arrays
as also with other containers. Why should we be restricted to one type
of container?


I hope these example illustrate some points. They are not meant to
trigger a discussion as to whether an array is the best data
structure for everything. (Note that it might be necessary to read
values from the array/Vector using random access in O(1), and to
store and replace values in O(1), another reason to use an array.)

OK.




You can do that using assembly language or SmallTalk, whatever.
I think this was not the point, but I should have Richard Riehle's
message speak for itself.



I am not talking about assembly. It is not difficult to write a
container getting a signed integer as a subscript and have range checking.



For example, look here, and reconsider the programming task as
originally stated:


I think it is probably more and at least the same. That said, I like Ada
somewhat. :)
 
I

Ioannis Vranos

fabio said:
Ciao,

Ioannis wrote a C++ program that in his intentions would had operate
like the Ada code that was provided as an example of language
expressiveness.

Unfortunatelly that C++ code doesn't resembles the Ada one. Have a
closer look at what Richard wrote:

type Index is range -800_000..12_000_000;
type Decimal_Fraction is digits 12 range -473.0 .. 2_000.0;
type Vector is array (Index range <>) of Decimal_Fraction;

V1 : Vector ( -47..600); -- note the negative index range
V2 : Vector (-1.. 10); -- also a negative index range
V3 : Vector (42..451); -- index starts higher than zero;


It isn't difficult to implement a vector that accepts signed ranges too
*with* range-checking, the default ones begin from 0 because this is the
C++ natural feel. vector::at() performs range checked access.


#include <vector>
#include <exception>
#include <iostream>


int main() try
{
std::vector<int> vec(10);

vec.at(12)=4;


}

catch(std::exception &e)
{
std::cerr<<e.what()<<"\n";
}


C:\c>temp
invalid vector<T> subscript

C:\c>



In other words it does not make much sense to have signed indexes.
Otherwise it is easy and possible.
 
F

Frank J. Lhota

And that is the unfortunate aspect of the language: its foundation on C.

I once heard Stroustrup confess that, if he had had his druthers, he would
not have started with C as the seed language.

Stroustrup said in an interview that his preference would have been Algol
with classes.

I think that I probably would have preferred that as well!
 
I

Ioannis Vranos

Georg said:
I hope these example illustrate some points. They are not meant to
trigger a discussion as to whether an array is the best data
structure for everything. (Note that it might be necessary to read
values from the array/Vector using random access in O(1), and to
store and replace values in O(1), another reason to use an array.)


I forgot to say here that the cost of map's operator[] is O(log(n))
which is fairly cheap for large amount of data.
 
G

Georg Bauhaus

Ioannis said:
Georg Bauhaus wrote:
I forgot to say here that the cost of map's operator[] is O(log(n))
which is fairly cheap for large amount of data.

compare O(log(n)) to O(1) where n is 1, 1000, 1_000_000.
Make this access a part of an inner loop.

Georg
 
G

Georg Bauhaus

Ioannis said:
Just using the appropriate available container. :)
Nope.




vector provides method at() that performs range checking.

I think we've been through this. Again, this is not the point.
Also if you
want a vector that has signed integer subscripts or even floating point,

We want a vector type indexed by values between M and N only *and* we
want the compiler + tools to help us make data structures in accord
with these ranges. We want it to take advantage of what it can learn
from the type system which allows index range checking _at compile time_.
Do you mean like this?

No. I mean a double subtype whose values range from N.M to N'.M'.

I think you are missing the point here. As someone else said this is
not about functions operating on containers.

[STL for constructs] These are bullet-proof code approaches.

Unfortunatly, this is not relevant in this discussion, which is not
just about algorithms.
We can use whatever I like, perhaps strings. What is wrong with "Door 1"
etc? :)

I see the smiley but this is what might be wrong with your
approach: a map indexed by strings is not the same as an array
indexed by ten and only ten numbers. (Actually the numbers are in a
type which includes only 10 numbers and their operations.)

Imagine a Matrix. String indexing of matrix element seems possible
but you'd rather have proper arrays. There is something
wrong with ("Row 5", "Column 1") indexing unless you have very much
time and a lot of RAM.
OK, this sounds easy. What do you think? Since you want an array:
bool operator() (const Door &arg)

Nice. However, operator() doesn't return one out of four states,
it returns a Boolean, but o.K.
(If the door is closed and there is a van, you still want some action to
take place. Likewise for the other cases. I know you can write this,
no need as this is not the point, see below)
{
return open== arg.IsOpen() && van== arg.IsVan();
}

vector<Door> doors(10);

Here you can see one point that you might want to demonstrate:
The compiler won't tell you that there is something wrong
with

doors[10].SetOpen().SetNoVan();

Worse, the program won't tell you either. This shows the missing
link between vector indexing and the base type system in your
approach. You could use

doors.at(10).SetOpen().SetNoVan();

and handle the exception _at run time_.
In Ada, the compiler will tell you: "index value 10 is no good!"
because the array "doors" can only be indexed by values effectively
between 0 .. 9. These and only these are the values of the type
enumerating the ten doors, and only these are allowed as index
values x in expressios doors(x).
No exception handling, no .at() needed when you listen to your
compiler and fix the indexing error before you deliver a program.
You get this for free as a result of the language's type handling
at compile time.

This is easy too. I do not get your point. This can be done with arrays
as also with other containers. Why should we be restricted to one type
of container?

There is a reason that arrays still exist. One of the reasons
should be obvious when comp.realtime is on the recipient list.
Again, imagine a wave file manipulation process.
A map indexed by strings is probably not the recommended container
when you need fast matrix computations. In fact, a map might not be
suitable at all irrespective of its key type, when r/w should be in O(1).

- Given an enum, and
- given a language that allows the enum as a basis for the construction
of an array type in the type system (not using some run time computation
method, like those you have shown here, IINM)
- given that the compiler can use its knowledge of the enum
+ when it sees an array type based on the enum
+ when it sees an array
+ when it sees an array indexed by a statically known enum value
+ etc.,
you have
(a) useful names for objects in your problem domain, checked at compile-time
(b) a conceptual link between the enum (naming the single items) and
a container _type_ (containing these items); you cannot use anything
but these named numbers for indexing
(c) the fastest possible access, for both reading and writing, possibly
checked at compile time
(d) etc.

The STL descriptions provide further reaonsing why there can be restrictions
on the uses of specific containers in specific situations, viz. O(f(n)).


What if a compiler or other tool can show that in the following expression
(pseudo notation)

array_variable.at_index [n + m] <- f(x)

does not need an index range check on the lhs? (Again, yes, it is possible
to write correct programs. The question is, does one notation + compilation
system have advantages when compared to another? What is the price to pay?)
It is not difficult to write a
container getting a signed integer as a subscript and have range checking.

Which is not the point.

Georg
 
R

Randy Brukardt

Paul Dietz said:
If one has a copy of the validation test suite, why is it necessary
to wait for the users to find the bugs? Is it that you have to pay
for the cost of fixing the bugs that the users would never encounter?

I'm not quite sure what your point is. But I think Richard was comparing a
compiler that was conformity tested against one that was not. So, such a
system wouldn't have been tested against any test suite.

Of course, in actual practice, there is a continuum of testing practice,
from formal conformity assessment to completely untested. I doubt that there
are many commercial systems on either extreme of that continuum, but Ada
systems tend to be close to the formally tested end.

Randy.
 
I

Ioannis Vranos

Georg said:
We want a vector type indexed by values between M and N only *and* we
want the compiler + tools to help us make data structures in accord
with these ranges. We want it to take advantage of what it can learn
from the type system which allows index range checking _at compile time_.


At first, it is easy to write a container in C++ that accepts a
specified range for indexes. The only reason that there is not one, is
because it does not make sense in C++ (when I learned some Pascal in the
past, I could not understand what was the use of the ability to use
negative indexes in arrays. The [0, +] style maps closely what is
happening in the machine.

Also it is possible to define range-checked at run-time, operations.
vector::at() is an example.


vector is also a dynamic-type array, so placing compile-time bounds
checking isn't possible/doesn't make sense.


For fixed size arrays and containers it is true, the compiler does not
catch at compile time any out of boundaries access. However we can do
this explicitly, by using compile-time checked assertions. For example:


#include <vector>
#include <boost/static_assert.hpp>

int main()
{
using namespace std;

const int MAX_SIZE=10;

vector<int> vec(MAX_SIZE);


// Many lines of code


BOOST_STATIC_ASSERT(10<MAX_SIZE);

vec[10]=4;
}


C:\c\temp.cpp In function `int main()':

14 C:\c\temp.cpp incomplete type `boost::STATIC_ASSERTION_FAILURE<
false>' used in nested name specifier



No. I mean a double subtype whose values range from N.M to N'.M'.


May you give an example for a container along with such a subtype?


Here you can see one point that you might want to demonstrate:
The compiler won't tell you that there is something wrong
with

doors[10].SetOpen().SetNoVan();

Worse, the program won't tell you either. This shows the missing
link between vector indexing and the base type system in your
approach. You could use

doors.at(10).SetOpen().SetNoVan();

and handle the exception _at run time_.
In Ada, the compiler will tell you: "index value 10 is no good!"
because the array "doors" can only be indexed by values effectively
between 0 .. 9. These and only these are the values of the type
enumerating the ten doors, and only these are allowed as index
values x in expressios doors(x).
No exception handling, no .at() needed when you listen to your
compiler and fix the indexing error before you deliver a program.
You get this for free as a result of the language's type handling
at compile time.


Will the Ada compiler tell you this, for user-defined types too? Or is
this restricted to built-in arrays? If the latest is true, then its
value isn't that much.

There is a reason that arrays still exist. One of the reasons
should be obvious when comp.realtime is on the recipient list.
Again, imagine a wave file manipulation process. A map indexed by
strings is probably not the recommended container
when you need fast matrix computations. In fact, a map might not be
suitable at all irrespective of its key type, when r/w should be in O(1).


OK, although O(log(n)) is fairly cheap, let's stick to O(1). However
personally I think that the value of defined subranges in the style
-1000, -400 has not any practical use.

- Given an enum, and
- given a language that allows the enum as a basis for the construction
of an array type in the type system (not using some run time computation
method, like those you have shown here, IINM)
- given that the compiler can use its knowledge of the enum
+ when it sees an array type based on the enum
+ when it sees an array
+ when it sees an array indexed by a statically known enum value
+ etc.,
you have
(a) useful names for objects in your problem domain, checked at
compile-time


What do you mean by names?

(b) a conceptual link between the enum (naming the single items) and
a container _type_ (containing these items); you cannot use anything
but these named numbers for indexing


Which has no value in the world of C++, but I guess many things in Ada
depend on this.

(c) the fastest possible access, for both reading and writing, possibly
checked at compile time


Fastest possible access in C++. Checked at run-time. Explicitly checked
at compile-time with compile-time assertions (which are restricted only
to constants), but is the Ada compile-time boundaries checking available
to user-defined containers? If not, can compile-time assertions be used?


(d) etc.

The STL descriptions provide further reaonsing why there can be
restrictions
on the uses of specific containers in specific situations, viz. >O(f(n)).


The access of vector, deque, string, valarray and bitset (and built in
arrays) is O(1) and of map O(log(n)) which is fairly cheap.

If you have access to TC++PL 3, you may check page 17.1.2 on page 464.


What if a compiler or other tool can show that in the following expression
(pseudo notation)

array_variable.at_index [n + m] <- f(x)

does not need an index range check on the lhs? (Again, yes, it is possible
to write correct programs. The question is, does one notation + compilation
system have advantages when compared to another? What is the price to
pay?)


Apart from being eager to see whether this compile-time range checking
is available to user-defined containers, :) in C++ there is no much
need for run-time boundary checking, if programming properly. Of course
one can always program improperly. :)
 
I

Ioannis Vranos

Georg said:
I forgot to say here that the cost of map's operator[] is O(log(n))
which is fairly cheap for large amount of data.


compare O(log(n)) to O(1) where n is 1, 1000, 1_000_000.
Make this access a part of an inner loop.



If you do the maths, you will see that log(10^6) isn't that large.
 
P

Pascal Obry

Ioannis Vranos said:
arrays. The [0, +] style maps closely what is happening in the machine.

Certainly. But the Ada model can maps more closely to the domain problem.
And this is an important point. We are not writting software for the machine
but to solve problems. Most of the time you just don't care how such data will
be handled by the machine (i.e. how the compiler will generate the code).

Pascal.

--

--|------------------------------------------------------
--| Pascal Obry Team-Ada Member
--| 45, rue Gabriel Peri - 78114 Magny Les Hameaux FRANCE
--|------------------------------------------------------
--| http://www.obry.org
--| "The best way to travel is by means of imagination"
--|
--| gpg --keyserver wwwkeys.pgp.net --recv-key C1082595
 

Ask a Question

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

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

Ask a Question

Members online

No members online now.

Forum statistics

Threads
474,202
Messages
2,571,057
Members
47,666
Latest member
selsetu

Latest Threads

Top