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

  • Thread starter Turamnvia Suouriviaskimatta
  • Start date
J

Jerry Coffin

Ioannis Vranos wrote:

[ ... ]
unsigned char *a = reinterpret_cast<unsigned char *>(&x);

Oops -- quite right. Fortunately, despite it's reputed lack of safety,
any properly-functioning C++ compiler would have caught my mistake had
it been in real code.

[ ... ]
Also since Ada is more ancient than C++ in terms of a final standard,
we can expect that some things are "more ancient", but it is still
an interesting language since it can do low level stuff.

I'm not sure "ancient" applies to either -- at least the last time I
noticed, the most recent Ada standard was approved in 1995, which is
marginally older than the C++ standard. I'm not really sure this means
a lot though -- most of the basic ideas of C++ were fixed by then in
any case. Certainly there were changes in the text of the rules after
that point, but most of these were to get the rules to require what had
already been decided upon, not things that were intended to change the
language.
I am not sure it is "safer" than C++ too, I am suspicious of "safe"
languages.

The first problem is to define what you mean by safety. Bjarne has been
fairly explicit that most safety features in C++ are intended to
prevent accidents, not intentional subversion. It's always seemed to me
that Ada has had a rather muddled idea of the "threat model", so the
language features have never been entirely aligned to a single intent.
Some parts appear intended to prevent accidents, but are quite easy to
subvert when one wishes to do so. Other parts appear to have been
designed with the intent of preventing even intentional subversion, but
fail to do so, and simply render some things quite a bit uglier than
there seems to be good reason for.

In fairness, I should add that my personal experiece with Ada was
almost entirely with the 1983 version, so it's entirely possible that
at least some of these have been fixed -- OTOH, looking at the code
snippets posted to this thread, it looks like at least some of the
ugliness remains.
 
P

Peter Amey

Hans said:
Can you explain the "real-time" part?

Reading this thread, it seems to me Ada's focus is on safety rather
than efficiency.
These safety constraints also tend to limit expressiveness. Not that
safety is bad, just that it's not free.

Actually, a close reading of the thread should have made it clear that
the additional safety is indeed "free". Since the majority of Ada's
checks are compile time they do not impact on run-time efficiency.
Where Ada's goals can only be met by run-time checks these are no more
expensive than equivalent manually-inserted checks in any other language
(and are often less because Ada provides the compiler with more
information by which to judge when they can safely be optimised away).
They can, in any case be turned off if the user requires a different
performance/safety tradeoff.

It should also have been clear from the thread that Ada imposes no
limits on expressiveness.

Can you say what led you to the opposite conclusion?

Peter
 
L

Larry Kilgallen

Can you explain the "real-time" part?

Reading this thread, it seems to me Ada's focus is on safety rather
than efficiency.

Many of these checks have been described as being compile-time checks.
A goal of fast compiles should not dominate.
These safety constraints also tend to limit expressiveness. Not that
safety is bad, just that it's not free.

Nobody has come up with something that cannot be expressed in Ada (or
in C++ for that matter). Ada code is more verbose, on the grounds that
code is read more often than it is written (or at least it should be).
 
D

Dr. Adrian Wrigley

C++ is a multiparadigm language and supports 4 paradigms. Each paradigm
is supported *well* with optimal space and time efficiencies.

It does not enforce a specific paradigm, but allows the mixing of them
as the programmer thinks it fits better for a specific problem.

This is is well known.

I was asking for my misconception on what *C++ was designed for* to
be dispelled.
 
P

Pascal Obry

Peter Amey said:
manually-inserted checks in any other language (and are often less because
Ada provides the compiler with more information by which to judge when they
can safely be optimised away).

For example (if you ask):

type Table is array (1 .. 10) of Integer;

Data : Table;

for K in Data'Range loop
if Data (K) = 1 then
...
end if;
end loop;

There is no need to check K validity inside the loop as "Data (K)" is always
correct by definition (K in Data'Range). Just an example.

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
 
C

CTips

Peter said:
Actually, a close reading of the thread should have made it clear that
the additional safety is indeed "free".

Free? Well, lets look at one particular issue: pointers to arbitrary
locations. In C, its a common idiom to do the following:
foo(int * p)
{
int * z;
for( i ... ) {
... p...;
}
z = p;
}

...
foo( &x[lo] );
...

Now, how can *any* language check to see that p is within bounds?

The usual solution is to pass p as a "handle"; basically, pass two
pointers, one of which is the pointer to the descriptor of the
containing "object" (which could be x or something containing x), or
alternatively, pass a pointer to a record that contains x and p. z, in
particular, will have to be a 2 element record, again containing x and p.

A dereference via z will have to become a double dereference; first load
p from z then load from p.

Not quite free.
Since the majority of Ada's
checks are compile time they do not impact on run-time efficiency.
Where Ada's goals can only be met by run-time checks these are no more
expensive than equivalent manually-inserted checks in any other language
(and are often less because Ada provides the compiler with more
information by which to judge when they can safely be optimised away).

Bullsh*t. In several ways, of which two are:
- Usually the checks will be added every place they are needed, and then
the usual optimizations will be used to eliminate them. In other words,
if one adds the checks manually, the compiler should eliminate them
identically.
- Going back to the problem of finding the countaining object for some
pointer. If one is adding the code by hand, one can use several options
NOT available to a compiler.
* what if z always points to objects inside x (but the compiler can't
figure it out).
* what if the containing object for p is always allocated on some 2^n
byte boundary?

There are other interesting things one can do for safety that are less
memory/CPU intensive than what a language can give you automatically.

Have a look at http://users.bestweb.net/~ctips for some of the ideas.
They can, in any case be turned off if the user requires a different
performance/safety tradeoff.

Are you sure? Do I have to recompile the whole program in that case? Or
reannotate the whole program? The above "handles" case is particularily
pernicious. If a sub-program is compiled to not expect handles [because
we just turned off that protection feature], but its caller is still
passing handles, well - interesting things will ensue.
It should also have been clear from the thread that Ada imposes no
limits on expressiveness.

How easy is it to build an arena allocator in Ada?

Given a processor with load-word-locked and store-word-conditional, how
would I build an atomic increment function?
 
P

Pascal Obry

CTips said:
Free? Well, lets look at one particular issue: pointers to arbitrary
locations. In C, its a common idiom to do the following:
foo(int * p)
{
int * z;
for( i ... ) {
... p...;
}
z = p;
}

...
foo( &x[lo] );
...

Now, how can *any* language check to see that p is within bounds?


Good you asked! Let's code this example in Ada (using Ada and
not C/C++ style, in Ada for the above example we do not need a pointer):

type T is array (Positive range <>) of Integer;

procedure Foo (P : in out T) is
Z : Positive;
begin
for I in P'Range loop
... P(i)...;
-- Here P(i) is always within the bounds, no check needed.
end loop;
Z := P'First;
end Foo;

...

Foo (X (Lo .. X'Last));
Bullsh*t. In several ways, of which two are:

Given your assumption about maybe you just don't know Ada.

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
 
L

Ludovic Brenta

John said:
What????????????????????????????????????????????????? Assembly
language? powerful? If you decide to write a X GUI interface in
assembly, I'll check in on you in 20 years to see how your
doing.... Higher level languages handle broader abstract concepts
better than low level languages. Assembly is great if you are
optimizing device drivers or banging bits at the hardware register
level...not good at implementing scientific algorithms, GUIs,
databases, etc. There are thousands of reasearch papers that extol
the problems of assembly language approach to things, and yes, there
are places and reasons to use it, but classifying it as a 'higher
level language' and something more powerful is incorrect....
-John

I think the problem is to define what everyone means by "powerful".

If by "powerful" I mean "I can do exactly anything I want", or "I can
get the most out of my hardware", then assembly is the most powerful
language.

If OTOH "powerful" means "I can program quickly", then assembly is not
powerful, but e.g. SQL or Delphi are.
 
L

Ludovic Brenta

Jerry Coffin said:
In fairness, I should add that my personal experiece with Ada was
almost entirely with the 1983 version, so it's entirely possible that
at least some of these have been fixed -- OTOH, looking at the code
snippets posted to this thread, it looks like at least some of the
ugliness remains.

The "use type" feature of Ada 95 alone makes Ada 95 *much* more
pleasant to program with. Check it out. And yes, we allow it in our
coding standards :)
 
L

Larry Kilgallen

Peter Amey wrote:

Are you sure? Do I have to recompile the whole program in that case?

At a maximum, recompile the module that has become performance critical.
 
L

Ludovic Brenta

Jerry Coffin said:
A poor idea. Just for example, consider writing a generic sorting
function. It needs to swap items that it's sorting. In well-written
C++, this will often be done with a using clause. Specifically, if
the type of items has provided its own specialized version of swap,
then my sorting functino should use that, but otherwise it should
use std::swap to swap them.

I try to specify whatever_type::swap(x,y), then compilation will
fail if the type has not provided a swap function. Conversely, if I
specify std::swap(x,y), then the specialized swap function won't be
used for those types that provide one.

Ada requires explicit instanciation of all templates, so there is no
"default". We do not have problems using fully-qualified names.

Ada 95's object-oriented features do not suffer from this either.

package P is
type Base is tagged null record;

procedure Method (B : in Base);
end P;


with P;
package Q is
type Derived is new P.Base with private;

procedure Method (B : in Derived); -- overloads P.Method
private
-- omitted
end Q;


with P;
procedure Dynamic_Dispatch (B : in Base'Class) is
begin
P.Method (B); -- [1]
end Dynamic_Dispatch;



The procedure Dynamic_Dispatch does not see package Q, yet it can call
Q.Derived if it receives an instance of Q.Derived as its parameter.
This, *even* without a use clause.
 
D

Dmitry A. Kazakov

How easy is it to build an arena allocator in Ada?

It is trivial:

type Object is ...;
type Object_Ptr is access Object;
for Object_Ptr'Storage_Pool use My_Arena;

Here you are:

Ptr : Object_Ptr := new Object; -- This allocates it in My_Arena

Note that Object can still be allocated on the stack or in any other pool:

type Object_Universal_Ptr is access all Object;

This : Object;
-- This allocates it on the stack
That : Object_Universal_Ptr := new Object;
-- This will be in the(a) default pool (in the heap)
Given a processor with load-word-locked and store-word-conditional, how
would I build an atomic increment function?

Why should I have atomic increment function? Ada has native concurrency
support. But if somebody would need that extremely low level thing as
atomic integers, then:

protected type Atomic_Integer is
procedure Increment;
private
Value : Integer;
end Atomic_Integer;

-- Implementation
protected body Atomic_Integer is
procedure Increment is
begin
Value := Value + 1;
end Increment;
end Atomic_Integer;
 
M

Martin Dowie

Jerry said:
Here, however, you lose your grip on reality. This is NOT "in fact" --
it's purely an OPINION! It's certainly possible to find projects that
would involve more than a half-million lines of code for which Ada
would be _extremely_ poorly suited, at best.

It's an opinion /nearly/ shared with P. J. Plauger of the ANSI-C
committee (and Dinkumware) - only I believe he quoted 100 thousand lines
as the point at which you should be using Ada. :)

Cheers

-- Martin
 
C

CTips

Pascal said:
Free? Well, lets look at one particular issue: pointers to arbitrary
locations. In C, its a common idiom to do the following:
foo(int * p)
{
int * z;
for( i ... ) {
... p...;
}
z = p;
}

...
foo( &x[lo] );
...

Now, how can *any* language check to see that p is within bounds?



Good you asked! Let's code this example in Ada (using Ada and
not C/C++ style, in Ada for the above example we do not need a pointer):

type T is array (Positive range <>) of Integer;

procedure Foo (P : in out T) is
Z : Positive;
begin
for I in P'Range loop
... P(i)...;
-- Here P(i) is always within the bounds, no check needed.
end loop;
Z := P'First;


Nope, not z = p[0], z = p; Z is a _pointer_ to int, not an int.
end Foo;

...

Foo (X (Lo .. X'Last));




Given your assumption about maybe you just don't know Ada.

Try again. Show me how sizeof(z) != 8 in Ada.
 
C

CTips

Dmitry said:
It is trivial:

type Object is ...;
type Object_Ptr is access Object;
for Object_Ptr'Storage_Pool use My_Arena;

Here you are:

Ptr : Object_Ptr := new Object; -- This allocates it in My_Arena

And how is My_Arena defined? Is it just a blob of memory? Or is it a
"class" that can invoke sbrk (or whatever) when it needs to?
Note that Object can still be allocated on the stack or in any other pool:

type Object_Universal_Ptr is access all Object;

This : Object;
-- This allocates it on the stack
That : Object_Universal_Ptr := new Object;
-- This will be in the(a) default pool (in the heap)




Why should I have atomic increment function? Ada has native concurrency
support. But if somebody would need that extremely low level thing as
atomic integers, then:

protected type Atomic_Integer is
procedure Increment;
private
Value : Integer;
end Atomic_Integer;

-- Implementation
protected body Atomic_Integer is
procedure Increment is
begin
Value := Value + 1;
end Increment;
end Atomic_Integer;

Will that generate:
L0:
lwlock temp,&Value
add temp,temp,1
stwcond temp,&Value
if( failed ) goto L0;
or will it generate something much more heavy-weight.
 
L

Larry Kilgallen

Why should I have atomic increment function? Ada has native concurrency
support. But if somebody would need that extremely low level thing as
atomic integers, then:

protected type Atomic_Integer is
procedure Increment;
private
Value : Integer;
end Atomic_Integer;

-- Implementation
protected body Atomic_Integer is
procedure Increment is
begin
Value := Value + 1;
end Increment;
end Atomic_Integer;

In other words, the code generator within the Ada compiler takes care
of the load-locked store-conditional aspects of the architecture.

What Dmitry has shown is how the programmer accesses that capability.
 
P

Pascal Obry

CTips said:
Nope, not z = p[0], z = p; Z is a _pointer_ to int, not an int.

Look at your program. You do nothing with Z so my solution is equivalent.
That's the point.
Try again. Show me how sizeof(z) != 8 in Ada.

Sorry I don't parse this one. I don't care about the size of Z! I care about
what my application has to do. So if you tell us what you want to achieve we'll
provide the solution using Ada not Ada-Transtaled-From-C++ one. It is always a
mistake to copy the solution word for word when translating from one language
to another.

chauve-souris /= bald-mouse

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
 
G

Georg Bauhaus

CTips said:
Dmitry A. Kazakov wrote:


And how is My_Arena defined? Is it just a blob of memory? Or is it a
"class" that can invoke sbrk (or whatever) when it needs to?

My_Arena is a storage pool, storage pool is a term
defined by the language.
Will that generate:
L0:
lwlock temp,&Value
add temp,temp,1
stwcond temp,&Value
if( failed ) goto L0;
or will it generate something much more heavy-weight.

For how many concurrently executing threads
of control, each invoking the incrementing function,
will this work? (Without any addition that is, as in
the Ada example)

Georg
 
D

Dmitry A. Kazakov

And how is My_Arena defined? Is it just a blob of memory? Or is it a
"class" that can invoke sbrk (or whatever) when it needs to?

It is completely up to you. My_Arena has to be derived from
System.Storage_Pools.Root_Storage_Pool. Which is the abstract base of all
storage pools. The implementation of Allocate and Deallocate is at your
discretion. For an arena the pool may contain a statically allocated array
organized as a stack. Deallocate could be then void, or it can pop
everything above it as in mark'n'release. A more advanced application could
allocate memory in segments at request etc. For a sample implementation of
a segmented stack pool see:

http://www.dmitry-kazakov.de/ada/components.htm#Pools_etc
Will that generate:
L0:
lwlock temp,&Value
add temp,temp,1
stwcond temp,&Value
if( failed ) goto L0;
or will it generate something much more heavy-weight.

Ask your compiler vendor. Though it wouldn't be necessarily polling. Also
usually protected objects are not used for so utterly fine-grained mutual
exclusion/locking. Atomic integer increment is normally just a small part
of some larger (but not lengthy) operation. For example, placing something
in a queue. Therefore spinning for a lock (which probably would be the
implementation) will likely be less expensive than some tricky guards
attached to each and every instruction. Note also that at such a low level
it would be very difficult if possible to maintain data consistency.
Compiler simply does not know what is related to what and will try to cope
with the worst case scenario. Protected types in Ada are to describe this
sort of semantics. So in the end atomic integers are pretty useless, no
matter how efficient they could be implemented.
 
P

Pascal Obry

CTips said:
Will that generate:
L0:
lwlock temp,&Value
add temp,temp,1
stwcond temp,&Value
if( failed ) goto L0;
or will it generate something much more heavy-weight.

No protected type implementations will be so light. This is impossible as it
brings far more than atomic integer. For an atomic integer it is possible to
declare :

Value : Integer;
pragma Atomic (Value);

In this case it would be nice to see the generated 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,206
Messages
2,571,069
Members
47,675
Latest member
RollandKna

Latest Threads

Top