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

  • Thread starter Turamnvia Suouriviaskimatta
  • Start date
I

Ioannis Vranos

Wes said:
That is the approach perl takes to multiple inheritance. At least
I think so--I haven't finished studying it. What does C++ or Eiffel
do when multiple inheritance creates two meanings for the same name?


What do you mean by two meanings?
 
E

Ed Falis

I'll openly admit that my knowledge of Ada 95 is _extremely_ limited
(I'm afraid I quit using Ada before 1995). Perhaps I need to take
another look in this area.
OTOH, doing a bit more looking, if I've misunderstood the situation, at
least I have some company. For example:


This is just a matter of simile. A tagged type and derivatives of tagged
types provide dispatching and other typical OOP facilities. Where the
concept differs from the class concept is that visibility is orthogonal,
provided by packages and other more traditional Ada facilities, while the
class concept combines the two.

- Ed
 
W

Wes Groleau

Ioannis said:
What do you mean by two meanings?

Class Wagon has method Draw.
Class Picture has method Draw.

If you multiply inherit to make
Class Picture_Of_A_Wagon, what does the method Draw do?

This was one of the arguments against having multiple
inheritance. But I think it's a silly argument. One
could just as logically argue that the "use" clause
should not exist because two packages might have Draw
subprograms. Or that overloading should not be allowed
because it's possible to write something ambiguous.

Ada's answer for the former is that if and only if the
situation _actually_ arises, neither of the choices is visible.

For the latter, the code just won't compile.
 
E

Ed Falis

That is the approach perl takes to multiple inheritance. At least
I think so--I haven't finished studying it. What does C++ or Eiffel
do when multiple inheritance creates two meanings for the same name?


Eiffel has very nice and elegant facilities for resolving ambiguities. A
lot of thought went into the situation of "diamond inheritance", where an
ultimate base class is inherited via two distinct ancestors, and how to
resolve it. Eiffel is in many ways Ada's beautiful niece. But Ada is a
lot more portable.

- Ed
 
I

Ioannis Vranos

Jim said:
My reading of the information at that url indicates that GMP allows
the specification of precision, but not the specification of a limited
range of valid values.

Since I have had enough with this signed value range of Ada, here is a
quick implementation of mine and some uses of it.

I am sure one can create a better one or a container directly that
supports ranges, if he devotes some time, so the question again arises,
since it is possible and nothing exists, probably it is not considered
useful to have such a feature in C++:


#include <vector>
#include <algorithm>
#include <cstdlib>

template <class T>
class range
{
std::vector<T> array;
T min, max;

public:
range(const T &mi, const T &ma):array(ma-mi+1), min(mi), max(ma)
{
using namespace std;

if(max-min<=0)
;//throw some exception

for(typename vector<T>::size_type i=0; i<array.size(); ++i)
array=min+i;
}

const T &operator[](const T &index)
{
// Add range checking max>=index>=min if desirable

return array[index-min+1];
}

operator T() { return array.size(); }

};


int main()
{
using namespace std;

range<int> r(-100, -20);

vector<int> vec(r);

vec[r[-65]]=3;
}
 
I

Ioannis Vranos

Ioannis said:
Jim said:
My reading of the information at that url indicates that GMP allows
the specification of precision, but not the specification of a limited
range of valid values.


Since I have had enough with this signed value range of Ada, here is a
quick implementation of mine and some uses of it.

I am sure one can create a better one or a container directly that
supports ranges, if he devotes some time, so the question again arises,
since it is possible and nothing exists, probably it is not considered
useful to have such a feature in C++:


#include <vector>
#include <algorithm>
#include <cstdlib>

template <class T>
class range
{
std::vector<T> array;
T min, max;

public:
range(const T &mi, const T &ma):array(ma-mi+1), min(mi), max(ma)
{
using namespace std;

if(max-min<=0)
;//throw some exception

for(typename vector<T>::size_type i=0; i<array.size(); ++i)
array=min+i;
}

const T &operator[](const T &index)
{
// Add range checking max>=index>=min if desirable


fixed: return array[index-min];
}

operator T() { return array.size(); }

};


int main()
{
using namespace std;

range<int> r(-100, -20);

vector<int> vec(r);

vec[r[-65]]=3;
}
 
I

Ioannis Vranos

Wes said:
Class Wagon has method Draw.
Class Picture has method Draw.

If you multiply inherit to make
Class Picture_Of_A_Wagon, what does the method Draw do?


In C++ you will get an error message until you define a newer version of
Draw for Picture_Of_A_Wagon class.
 
J

Jerry Coffin

Jim Rogers wrote:

[ ... ]
For instance the following macro can cause some serious problems when
mis-applied:

#define SWAP(A,B) ((temp) = (A);(A) = (B); (B) = (temp))

char s1[30];
int i;

SWAP(S1, i);

In many cases it is safer to define an in-line function than to
define a macro.

I don't see a particularly serious problem here -- the code simply
won't compile. At the moment you have some syntactical problems and
sloppiness that's overlooked by Ada comilers (because Ada is
case-insensitive).

Even ignoring those, however, it will fail because of a
type-difference. To give a concrete example, after (partially) fixing
your code above, Visual C++ says:

swap.cpp(7) : error C2440: '=' : cannot convert from 'int' to 'char
[30]'
There are no conversions to array types, although there are
conversions to references or pointers to arrays

Modulo trivial things like ':=' vs. '=', 'int' vs. 'Integer', etc., I
suspect that's on the same general order as you'd expect to see from an
Ada compiler given code that attempted to assign an Integer to an array
of characters.

I think most C++ programmers, however, would tend to agree that in this
case you'd generally be better off using std::swap.
 
G

Georg Bauhaus

Jim doesn't mention fpt ranges in the context of arrays.
Just a floating point type whose values range between 0.0 and 1.0.
And a description of the use of this type that has a range constraint.
No arrays or signs in sight.

Since I have had enough with this signed value range of Ada,

Forget about the sign, as you did in your code with min and max.
This is not a sign issue. This isn't limited to array bounds
checking. It has to do with types and with values that match at compile
time.
For a start, add a template parameter to class range from which
the compiler can infer the min and max values.
 
J

Jerry Coffin

Wes Groleau wrote:

[ ... ]
Class Wagon has method Draw.
Class Picture has method Draw.

If you multiply inherit to make
Class Picture_Of_A_Wagon, what does the method Draw do?

In C++ the result would be simple: attempting to call Draw without
qualification would cause an error due to ambiguity. Note that the mere
existence of the situation doesn't lead to an ambiguity or a compiler
error -- only when/if you attempt to use the name without qualification
does the error arise.

This ambiguity would typically be resolved by qualifying the name (i.e.
explicitly calling Wagon::Draw() or Picture::Draw()). If you think it
makes sense for the derived class to normally call one instead of the
other, a using declaration or a forwarding function can provide that.
For example, if we wanted the derived Draw to resolve to Picture::Draw,
here's how it looks with a using declaration:

#include <iostream>

struct Picture {
virtual void Draw() {
std::cout << "Drawing Picture";
}
};

struct Wagon {
virtual void Draw() {
std::cout << "Drawing Wagon";
}
};

struct Picture_Of_A_Wagon : Wagon, Picture {
using Picture::Draw;
};

For explicit forwarding you'd do this instead:

struct Picture_Of_A_Wagon : Wagon, Picture {
void Draw() { Picture::Draw(); }
};

Of course, in well-written code, you just wouldn't do anything like
this at all -- a picture of a wagon is NOT a wagon, and does not
satisfy the Liskov Substitution Principle, so having Picture_Of_A_Wagon
derive (publicly) from Wagon is just plain wrong.

For better or worse, however, compilers (at least for C++) aren't quite
smart enough to recognize such problems yet, so it would be up to a
person to point out that the code above is broken beyond repair.

Multiple inheritance can make sense, but should be restricted to
situations where a derived object really can be substituted for either
(or any) of the base types. Examples would be having an object with a
particular function made from a particular material:

class piece_of_aluminium {
int alloy;
std::string heat_treatment;
std::string hardness;
int density;
};

class pipe{
int diameter;
int wall_thickness;
int length;
};

class aluminium_pipe : public piece_of_aluminium, public pipe {

};

Now we have a perfectly reasonable situation: an aluminium pipe that
can be treated as either a piece of aluminium or as a pipe.
Unfortunately, right now I can't think of a name that would really make
good sense in both base classes, so we don't have a potential
ambiguity.
 
L

Larry Kilgallen

Larry Kilgallen wrote:

Attempting to separate programming from constructing (a program) seems
to me a strong indication that you don't really know much of what
you're talking about.

Your reliance on ad-hominem attacks makes me believe the rest of
what you say will not be worthwhile either.
 
J

Jerry Coffin

Ioannis said:
In C++ you will get an error message until you define a newer version
of Draw for Picture_Of_A_Wagon class.

Not so -- until or unless you _call_ the function (without qualifying
its name) there's no ambiguity. When/if ambiguity does arise, you can
resolve it without adding a function to the derived class.
 
M

Martin Krischik

C++ continues to evolve, but much of that evolution seems to follow a
course of shoring up things already in the language that don't quite work
as one might prefer, or adding a truss here and a buttress there to
prevent or enable deficiencies in the language; e.g., cast-away const,
a truly silly addition to the language.

Needed for calling broken functions in 3td party libraries. There are still
libraries around which won't mark unmodified parameters with "const" to be
compatible with older compilers.

I am not saying it's good. And of course the library vendors are just hiding
there laziness behing a "compatiblity" argument:

#if COMPILER_X_VERSION < 2.0
#define const
#endif

Martin
 
D

Dmitry A. Kazakov

One thing I've never quite understood is the argument of ambiguity.
Ada allows other things that have a potential of ambiguity. The
solution there is that when something _actually_ has two meanings
(as opposed to _potentially_) then you get neither.

Of course it not an argument. A real argument I think is view conversions,
they would be impossible to do without a distributed overhead (as it takes
place in C++.) I think that MI from concrete types should be allowed, later
when Ada will have "tagged" by-value types without tags carved on the
object. For those one could allow full MI, for others only multiple
interfaces but single implementation inheritance (as in Ada 2005.)
 
D

Dmitry A. Kazakov

Well, actually, during the Ada 9X design I tried to push for a
class-hierarchy of exceptions. I don't like every detail of the way C++
does it, but at least in *this* regard, it's better than Ada.

I think it is a question whether exception hierarchy has to be mapped on
(1) types, (2) subtypes or (3) value ranges. C++ sticks to (1) which is
problematic without ad-hoc supertypes. Presently it ends up with a mess in
the list of catch'es. I think that (3) may have a chance.
Jerry Coffin is wrong that Ada does not allow attaching information to
exception, by the way. Ada allows attaching Strings, which is
admittedly a kludge. Using the class-hierarchy, as Jerry advocates,
would be cleaner, and type safe.

The problems with that:

1. An exception object referring to a scope that does not exist (mounting
accessibility rules?)

2. The target of an dispatching method is out of scope.

3. The whole scope of the exception type is finalized before "catch".

4. How to check that choices don't intersect? (C++ dependency of the order
of catch is error-prone.)

5. The hierarchy of types may not reflect the hierarchy needed by an
exception handler. How to do enumeration and ranges of exception objects
having different types?
 
M

Martin Krischik

Hyman said:
What's wrong with C++'s model?

As allways: the default behaviour. Default is static inheritance.

Take the following example:

class X : public A, public B { };

This causes a problem when you have

class A: public C {};

class B public C {};

Of couse C++ has a perfectly good solution:

class A: virtual public C {};

class B virtual public C {};

All very well, only I know C++ programmers which had 2 to 7+ years
experience in C++ and did not know about it until I told them. And not just
one programmer: the whole team of about 20!

Side note: This puts all the "good programmers do than/don't do that"
arguments into an instersting new light: In this case only 1 in 20 of the
C++ programmers realy knew his trade and that one is now an Ada
advocate ;-) .

Martin
 
P

Peter Amey

Ioannis Vranos wrote:
[snip]
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.

Of all the comments in this rambling (but remarkably bloodless) thread,
this is the one that, for me, gets closest to the fundamental
philosophical difference between the C language family and Ada.

The [0, +] styles does indeed map closely to what is happening in the
machine; however, for me, what is happening in the machine is generally
much less interesting than what is being represented in my problem
domain. I am always struck by the way a C user's first thought always
seems to be about how many bits he needs to represent something where an
Ada user is concerned with real-world values and leaves the bit size to
be chosen by the compiler. Of course, when writing an interface to a
hardware device, we have to worry about bit patterns but I certainly
want to stop doing that as soon as possible and worry about the problem
domain instead.

So, for an array, I want to index it with a problem domain entity and
let the compiler turn that into a wholy uninteresting set of address
offsets.

Ionnis wanted an example of negative indexes. How about:

type X_Values is range -5 .. 5;
type Y_Values is range 0 .. 25;
type Graph is array (X_Values) of Y_Values;
Squares : constant Graph := (25, 16, 9, 4, 1, 0, 1, 4, 9, 16, 25);

Peter
 
M

Martin Krischik

Ioannis said:
Since I have had enough with this signed value range of Ada, here is a
quick implementation of mine and some uses of it.

I am sure one can create a better one or a container directly that
supports ranges, if he devotes some time, so the question again arises,
since it is possible and nothing exists, probably it is not considered
useful to have such a feature in C++:

Shure it is usefull, very usefull indeed. Only you started off wrong:

namespace Ada
{
template <
class Base_Type,
Base_Type The_First,
Base_Type The_Last>
class Range
{
.....
}

template <
class Element_Type,
class Index_Type> // derived from Range.
class Array
{
.....
}
}

Martin
 
M

Martin Dowie

Hyman said:
Yes there is. The C preprocessor works with C tokens, and Ada's
use of the single quote character for attributes will utterly
confuse it.

Ok - but the only part of the C preprocessor that I've ever even
seen used embedded in Ada code was some "#ifdef" to allow
types to have different ranges on different platforms.

Personally, I'd rather use a separate package and a rename and
let the build process take care of everything - but that's how this
one company had decided to do it.

Cheers

-- Martin
 

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,665
Latest member
salkete

Latest Threads

Top