* tagged record child: override constructor? @ 2005-09-13 5:58 sean.gilbertson 2005-09-13 6:39 ` David Trudgett ` (3 more replies) 0 siblings, 4 replies; 38+ messages in thread From: sean.gilbertson @ 2005-09-13 5:58 UTC (permalink / raw) I have a tagged record that is declared private, along with a constructor function which returns an instance. I do this to enforce the assignment of several required fields in the record, so if you know how to do this another way, please let me know! But Ada is telling me I have to override this function in the child. This doesn't make a lot of sense. How can I deal with this? I'm not currently using Ada 2005. ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-13 5:58 tagged record child: override constructor? sean.gilbertson @ 2005-09-13 6:39 ` David Trudgett 2005-09-13 7:32 ` Dmitry A. Kazakov ` (2 subsequent siblings) 3 siblings, 0 replies; 38+ messages in thread From: David Trudgett @ 2005-09-13 6:39 UTC (permalink / raw) sean.gilbertson@gmail.com writes: > I have a tagged record that is declared private, along with a > constructor function which returns an instance. I do this to enforce > the assignment of several required fields in the record, so if you know > how to do this another way, please let me know! Have you thought about using Ada.Finalization? (Check the Ada Reference Manual.) Others, more knowledgeable, might have other suggestions for you. David -- David Trudgett http://www.zeta.org.au/~wpower/ In his essay 'The Banality of Evil', the great American dissident Edward Herman described the division of labour among those who design and produce weapons like cluster bombs and daisy cutters and those who take the political decisions to use them and those who create the illusions that justify their use. 'It is the function of the experts, and the mainstream media,' he wrote, 'to normalise the unthinkable for the general public.' It is time journalists reflected upon this, and took the risk of telling the truth about an unconscionable threat to much of humanity that comes not from faraway places, but close to home. -- John Pilger, 'The truths they never tell us' , New Statesman, November 26, 2001 ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-13 5:58 tagged record child: override constructor? sean.gilbertson 2005-09-13 6:39 ` David Trudgett @ 2005-09-13 7:32 ` Dmitry A. Kazakov 2005-09-13 7:56 ` tmoran 2005-09-13 15:23 ` sean.gilbertson 2005-09-13 9:33 ` Georg Bauhaus 2005-09-13 16:37 ` Jeffrey Carter 3 siblings, 2 replies; 38+ messages in thread From: Dmitry A. Kazakov @ 2005-09-13 7:32 UTC (permalink / raw) On 12 Sep 2005 22:58:54 -0700, sean.gilbertson@gmail.com wrote: > I have a tagged record that is declared private, along with a > constructor function which returns an instance. I do this to enforce > the assignment of several required fields in the record, so if you know > how to do this another way, please let me know! Ada.Finalization? > But Ada is telling me I have to override this function in the child. I presume you have something like: type X is private; function Create (...) return X; private type X is tagged ...; > This doesn't make a lot of sense. On the contrary, Create above is covariant, that means that a derived type cannot inherit it, because otherwise the result would be of the parent type X. Thus it has to be overridden in each derived type. This is a very important feature that saves a lot of debugging. > How can I deal with this? Just make Create contravariant, provided that you know how to create derived instances from there. In Ada contravariant subroutines are class-wide: type X is tagged private; -- Publicly tagged to have an ability to declare class-wides function Create (...) return X'Class; -- This is same for all derived types private type X is tagged ...; -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-13 7:32 ` Dmitry A. Kazakov @ 2005-09-13 7:56 ` tmoran 2005-09-13 15:23 ` sean.gilbertson 1 sibling, 0 replies; 38+ messages in thread From: tmoran @ 2005-09-13 7:56 UTC (permalink / raw) > type X is tagged private; > -- Publicly tagged to have an ability to declare class-wides > function Create (...) return X'Class; If you then have type Y is new X ... then function Create will of course still return an X, not a Y unless you make a new Create that knows how to return a Y. > constructor function which returns an instance. I do this to enforce > the assignment of several required fields in the record, so if you know > how to do this another way, please let me know! Make X a child of Ada.Finalization.Controlled (as suggested) and writing an Initialize doesn't work? Or can you simply make those required fields discriminants? type X(Required_A, Required_B : Integer) is ... ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-13 7:32 ` Dmitry A. Kazakov 2005-09-13 7:56 ` tmoran @ 2005-09-13 15:23 ` sean.gilbertson 2005-09-13 17:37 ` Martin Krischik 1 sibling, 1 reply; 38+ messages in thread From: sean.gilbertson @ 2005-09-13 15:23 UTC (permalink / raw) I don't feel totally comfortable with the 'Class function, but after looking at the Ada Wikibook, it seems like I can use it something like a "static class method" in Java. I'm going to research all of these suggestions. Thanks, Dmitry! And thanks to everyone else! I was surprised at the quick and learned replies. ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-13 15:23 ` sean.gilbertson @ 2005-09-13 17:37 ` Martin Krischik 2005-09-13 19:29 ` Ludovic Brenta 0 siblings, 1 reply; 38+ messages in thread From: Martin Krischik @ 2005-09-13 17:37 UTC (permalink / raw) sean.gilbertson@gmail.com wrote: > I don't feel totally comfortable with the 'Class function, but after > looking at the Ada Wikibook, it seems like I can use it something like > a "static class method" in Java. I'm going to research all of these > suggestions. In this case it's more like a "non virtual" functions. That's because all class members are virtual in Ada. So 'Class methods replace both static and not virtual - depending on context. Actually it's static as in: class C { static F (C& Param); }; I guess we have to improve the text here. Martin -- mailto://krischik@users.sourceforge.net Ada programming at: http://ada.krischik.com ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-13 17:37 ` Martin Krischik @ 2005-09-13 19:29 ` Ludovic Brenta 2005-09-14 7:49 ` Dmitry A. Kazakov ` (2 more replies) 0 siblings, 3 replies; 38+ messages in thread From: Ludovic Brenta @ 2005-09-13 19:29 UTC (permalink / raw) Martin Krischik <krischik@users.sourceforge.net> writes: > That's because all class members are virtual in Ada. I think this needs to be elaborated on. All _primitive operations of a tagged type_ are _potentially_ "virtual" in Ada. Each caller of such a primitive operation decides, at the call site, whether the dispatching is static or dynamic. In contrast, in (my recollection of) C++, all virtual methods dispatch dynamically from all call sites: the decision is not made at the point of call but at the point of definition of the method. -- Ludovic Brenta. ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-13 19:29 ` Ludovic Brenta @ 2005-09-14 7:49 ` Dmitry A. Kazakov 2005-09-14 9:05 ` Maciej Sobczak 2005-09-14 16:14 ` Martin Krischik 2005-09-14 9:28 ` Alex R. Mosteo 2005-09-14 16:10 ` Martin Krischik 2 siblings, 2 replies; 38+ messages in thread From: Dmitry A. Kazakov @ 2005-09-14 7:49 UTC (permalink / raw) On Tue, 13 Sep 2005 21:29:06 +0200, Ludovic Brenta wrote: > Martin Krischik <krischik@users.sourceforge.net> writes: >> That's because all class members are virtual in Ada. > > I think this needs to be elaborated on. > > All _primitive operations of a tagged type_ are _potentially_ > "virtual" in Ada. Each caller of such a primitive operation decides, > at the call site, whether the dispatching is static or dynamic. Ada's model is quite simple and logical. If T appears as a formal parameter or the result of a primitive operation of T, then this parameter is controlled (= the operation can dispatch on it.) Dispatching is determined not by any site but exclusively by the actual parameter type. It must be of T'Class. For this reason re-dispatching never happens. To re-dispatch, one should explicitly convert T to T'Class. > In contrast, in (my recollection of) C++, all virtual methods dispatch > dynamically from all call sites: the decision is not made at the point > of call but at the point of definition of the method. C++ model is more tricky and in some aspects more limited. Only the prefix (distinguished) parameter can be controlled (the "this-parameter".) The result cannot be controlled as well. There is no difference between T and T'Class (the source of countless problems.) So all operations re-dispatch except for constructors and destructors, which don't. I.e. it is rather C++ where the call site determines whether a virtual function dispatches. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-14 7:49 ` Dmitry A. Kazakov @ 2005-09-14 9:05 ` Maciej Sobczak 2005-09-14 13:20 ` Dmitry A. Kazakov 2005-09-14 16:14 ` Martin Krischik 1 sibling, 1 reply; 38+ messages in thread From: Maciej Sobczak @ 2005-09-14 9:05 UTC (permalink / raw) Dmitry A. Kazakov wrote: > C++ model is more tricky and in some aspects more limited. Only the prefix > (distinguished) parameter can be controlled (the "this-parameter".) There is a distinction between static and dynamic type and also a distinction between class member non-static functions and free functions as well as between non-static member functions being virtual or not. The "first" parameter causes the virtual dispatch on the dynamic type of the object when it is used as a prefix for a virtual member function. In all other cases (including further parameters) it is the static type of the object that drives the dispatch. > There is no difference between T and > T'Class (the source of countless problems.) Could you elaborate? > So all operations re-dispatch What does that mean? > except for constructors and destructors, which don't. They do, but there the dynamic and static type of the object is the same, which actually saves a lot of problems. > I.e. it is rather C++ > where the call site determines whether a virtual function dispatches. It always does, independent on the call site. It is rather the dynamic type of the object that changes during construction and destruction, thus leading to the "virtual functions don't work" impression. -- Maciej Sobczak : http://www.msobczak.com/ Programming : http://www.msobczak.com/prog/ ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-14 9:05 ` Maciej Sobczak @ 2005-09-14 13:20 ` Dmitry A. Kazakov 2005-09-14 13:52 ` Hyman Rosen 0 siblings, 1 reply; 38+ messages in thread From: Dmitry A. Kazakov @ 2005-09-14 13:20 UTC (permalink / raw) On Wed, 14 Sep 2005 11:05:10 +0200, Maciej Sobczak wrote: > Dmitry A. Kazakov wrote: > >> There is no difference between T and >> T'Class (the source of countless problems.) > > Could you elaborate? 1. It is space inefficient. To have T and T'Class same requires keeping the type tag in all Ts, while it is only necessary in T'Class. 2. There is no chance to get a consistent type system where each specific type would have classes and primitive operations. If Boolean and Boolean'Class have to be same, then the size of Boolean cannot be 1 bit. (Presently Ada does not use this opportunity, but I hope it some day will) 3. It is inefficient, because it has to dispatch everywhere (except for constructors and destructors, which still have the overhead of unused dispatching.) Maybe this is the reason why so many people believe that OO isn't suitable for real-time. I believe that this is rather an ungrounded projection from C++ to Ada. 4. It is error-prone because of enforced re-dispatch (see below.) One should explicitly specify T::Foo() referring member functions within a member function. In practice nobody cares to do this, which often leads to nasty surprises. 5. When T and T'Class are same, objects have identity. This identity might be arbitrary. Nevertheless people will exploit it. This leads to bad fragile design. Another form of identity is the object's address. Which is quite similar case. Here again Ada prevents you from occasional using this sort of identity (you have to make the object aliased), while C++ encourages it (you can apply & to almost everything.) >> So all operations re-dispatch > > What does that mean? type Base is tagged ...; procedure Foo (X : Base); -- Primitive operation procedure Bar (X : Base); -- Another primitive operation procedure Foo (X : Base) is begin Bar (X); -- This does not dispatch! It calls Base.Bar end Foo; The implementation of Foo stay consistent no matter what a derived type would do with Bar. Note that this is nothing but a trivial contract model which C++ lacks in this case. Within Foo, X is declared of the type Base. So it would be infeasible to dispatch again. The type is known. If dispatch is really needed, then that should be specified in the contract. I.e. the type of X should be Base'Class. [Ada allows explicit conversions to Base'Class, a bad idea though. Better pattern is to make the objects explicitly having identity, using the Rosen's trick.] Re-dispatch usually indicates a design problem. >> except for constructors and destructors, which don't. > > They do, but there the dynamic and static type of the object is the > same, which actually saves a lot of problems. Yes, they do as if they wouldn't. To me it is that they don't. Of course this saves the problems just because there is no re-dispatch, which is in general a bad thing. Actually what C++ does in constructors and destructors is using Ada's (consistent) model. >> I.e. it is rather C++ >> where the call site determines whether a virtual function dispatches. > > It always does, independent on the call site. It is rather the dynamic > type of the object that changes during construction and destruction, > thus leading to the "virtual functions don't work" impression. Yes. It changes from T'Class to T and backwards depending on the location, utterly inefficient and very error-prone. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-14 13:20 ` Dmitry A. Kazakov @ 2005-09-14 13:52 ` Hyman Rosen 2005-09-14 16:47 ` Dmitry A. Kazakov 0 siblings, 1 reply; 38+ messages in thread From: Hyman Rosen @ 2005-09-14 13:52 UTC (permalink / raw) Dmitry A. Kazakov wrote: > 1. It is space inefficient. To have T and T'Class same requires keeping the > type tag in all Ts, while it is only necessary in T'Class. No Ada implementation takes this approach, though. They all keep a type tag in every tagged object. > 3. It is inefficient, because it has to dispatch everywhere In C++, however, you have already declared some functions to be dispatching (virtual) and some not. For those functions which are virtual, dispatching is what's wanted. How many Ada newbies get completely confused trying to figure out what dispatches and what doesn't? Do any of them write T'Class the first time? > 4. It is error-prone because of enforced re-dispatch It is error-prone not to dispatch, because the first thing everyone learns about OO is that methods dispatch! It's only after significant hair-pulling that an Ada newbie will realize why his overriden methods are not being called. > 5. When T and T'Class are same, objects have identity. Huh? All type tags for a given type are identical. Why does embedding a type tag in an object give it any more identity than not doing so? > The implementation of Foo stay consistent no matter what a derived type > would do with Bar. Except that when the programmer overrides Bar for derived types, he's going to be monumentally confused as to why Foo isn't calling it. Foo is going to be consistent, but as far as the programmer is concerned, it's going to be consistently incorrect. > Re-dispatch usually indicates a design problem. What nonsense! > Yes, they do as if they wouldn't. To me it is that they don't. As has been repeatedly demonstrated to you, this is just wrong. Once again, here is the code: struct A { virtual void f() { print("A"); } void g() { f(); } A() { g(); } ~A() { g(); } }; struct B : A { void f() { print("B"); } B() { g(); } ~B() { g(); } }; int main() { B b; } This will print "ABBA", demonstrating that dispatching is occuring within g() even when it is called from a constructor or destructor. ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-14 13:52 ` Hyman Rosen @ 2005-09-14 16:47 ` Dmitry A. Kazakov 2005-09-14 17:16 ` Hyman Rosen 0 siblings, 1 reply; 38+ messages in thread From: Dmitry A. Kazakov @ 2005-09-14 16:47 UTC (permalink / raw) On 14 Sep 2005 06:52:55 -0700, Hyman Rosen wrote: > Dmitry A. Kazakov wrote: >> 1. It is space inefficient. To have T and T'Class same requires keeping the >> type tag in all Ts, while it is only necessary in T'Class. > > No Ada implementation takes this approach, though. > They all keep a type tag in every tagged object. May be, so what? I already said that Ada 95 didn't use all advantages of its OO model. The question was about the model, not its particular implementation in Ada 95. >> 3. It is inefficient, because it has to dispatch everywhere > > In C++, however, you have already declared some functions to be > dispatching (virtual) and some not. Which is bad. All operations should be primitive if not specified otherwise. Moreover, it would be reasonable to prohibit operations whose arguments are neither controlling nor class-wide. The only reason not to do this is absence of multiple dispatch. > For those functions which are > virtual, dispatching is what's wanted. How many Ada newbies get > completely confused trying to figure out what dispatches and what > doesn't? Do any of them write T'Class the first time? Why should they care? Most of the operations are primitive, yet meaningful dispatch is rare. How often the specific type is really unknown? A goal of Ada design was safe software construction. Putting former C++ programmers on the right track wasn't. (:-)) >> 4. It is error-prone because of enforced re-dispatch > > It is error-prone not to dispatch, because the first thing everyone > learns about OO is that methods dispatch! In the past everyone learnt that the Earth is flat... The effect of C++ as the first language is sometimes comparable to undergoing the lobotomy... >> 5. When T and T'Class are same, objects have identity. > > Huh? All type tags for a given type are identical. Why does embedding > a type tag in an object give it any more identity than not doing so? Because having the tag you can determine the object's type, which might differ from what the contract states. Ada is based on contract model. >> The implementation of Foo stay consistent no matter what a derived type >> would do with Bar. > > Except that when the programmer overrides Bar for derived types, Especially in this case! > he's going to be monumentally confused as to why Foo isn't calling > it. Because it is the contract to determine what's going on. >> Yes, they do as if they wouldn't. To me it is that they don't. > > As has been repeatedly demonstrated to you, this is just wrong. > Once again, here is the code: > > struct A { > virtual void f() { print("A"); } > void g() { f(); } > A() { g(); } > ~A() { g(); } > }; > struct B : A { > void f() { print("B"); } > B() { g(); } > ~B() { g(); } > }; > int main() { B b; } > > This will print "ABBA", demonstrating that dispatching is occuring > within g() even when it is called from a constructor or destructor. It demonstrates that in the calls made from T::T and T::~T the type of "this" is T. f() in A::g() dispatches only if g() wasn't called [directly or indirectly] from a constructor or destructor. If that was A::A then it calls A::f(). If that was B::B then it calls B::f() (which might be inherited from A.) And so on. No mystery, just a mess. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-14 16:47 ` Dmitry A. Kazakov @ 2005-09-14 17:16 ` Hyman Rosen 2005-09-14 20:20 ` Dmitry A. Kazakov 0 siblings, 1 reply; 38+ messages in thread From: Hyman Rosen @ 2005-09-14 17:16 UTC (permalink / raw) Dmitry A. Kazakov wrote: > meaningful dispatch is rare. How often the specific type is really unknown? This is just baffling. The whole point of object-oriented programming is that you have classes which conform to an interface but have differing implementations, and those implementations are dispatched to by making a call using a classwide type. That's the whole reason tagged types were implemented in Ada 95. Without it, you just have ADTs. > A goal of Ada design was safe software construction. Putting former C++ > programmers on the right track wasn't. (:-)) How nice for Ada. But what does dispatching have to do with safety? > Because having the tag you can determine the object's type, which might > differ from what the contract states. Ada is based on contract model. That's how tagged types work. That's the whole point of tagged types. You're supposed to be able to get at aspects of the object's real type, via dispatching. > Because it is the contract to determine what's going on. You've got a tagged type, so it's methods should dispatch. That's why you use a tagged type in the first place. When the method call doesn't dispatch that's surprising and unexpected. OO came to Ada late. Making its behavior weirdly different from other OO languages isn't helpful. > It demonstrates that in the calls made from T::T and T::~T the type of > "this" is T. Yes, that's one thing it demonstrates. > f() in A::g() dispatches only if g() wasn't called [directly or indirectly] > from a constructor or destructor. That's false. B::B() and B::~B() both call A::g() (there is no separate B::g() defined), and that dispatches to B::f(). There is no difference in the dispatch mechanism when f() is called, whether or not that call originates in a constructor. The only difference is what type tag the object contains when the dispatching happens. ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-14 17:16 ` Hyman Rosen @ 2005-09-14 20:20 ` Dmitry A. Kazakov 2005-09-14 20:34 ` Georg Bauhaus 2005-09-14 20:56 ` Hyman Rosen 0 siblings, 2 replies; 38+ messages in thread From: Dmitry A. Kazakov @ 2005-09-14 20:20 UTC (permalink / raw) On 14 Sep 2005 10:16:16 -0700, Hyman Rosen wrote: > Dmitry A. Kazakov wrote: >> meaningful dispatch is rare. How often the specific type is really unknown? > > This is just baffling. The whole point of object-oriented > programming is that you have classes which conform to an > interface but have differing implementations, and those > implementations are dispatched to by making a call using a > classwide type. That's the whole reason tagged types were > implemented in Ada 95. That does not invalidate my point. You need dispatch only if you have a class-wide object. So if your newbie doesn't write T'Class s/he doesn't need it. > Without it, you just have ADTs. Which is all I need! There is no magic in OO. Class is just an ADT. Polymorphic object is just a value of that ADT. A polymorphic operation is just an operation defined on that ADT, which body is composed out of parts defined by specific types. >> A goal of Ada design was safe software construction. Putting former C++ >> programmers on the right track wasn't. (:-)) > > How nice for Ada. But what does dispatching have to do > with safety? It was your point that for a newbie coming from C++ it might appear difficult to understand Ada. But as it was pointed out many times before, Ada's default choices are the safest ones. Thus by default an operation on a tagged type is primitive and dispatching. C++ choices are at most adventurous. The languages designers had different goals in mind. So for a newbie it indeed might look as a paradigm shift. High time it is. >> Because having the tag you can determine the object's type, which might >> differ from what the contract states. Ada is based on contract model. > > That's how tagged types work. That's the whole point of > tagged types. You're supposed to be able to get at aspects > of the object's real type, via dispatching. That is how *class-wide* types work. Class-wide objects have the type identity. Specific types need not, because in Ada you cannot dispatch on them. >> Because it is the contract to determine what's going on. > > You've got a tagged type, so it's methods should dispatch. > That's why you use a tagged type in the first place. When > the method call doesn't dispatch that's surprising and > unexpected. OO came to Ada late. Making its behavior weirdly > different from other OO languages isn't helpful. There is no point in adopting what other languages did wrong. > The only difference is what type tag > the object contains when the dispatching happens. As I said before, it is equivalent to dispatch that does not happen. How the compiler implements it, by either patching the dispatch table or not, I don't care. It is a *consistent* behavior, and it is how Ada does it everywhere. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-14 20:20 ` Dmitry A. Kazakov @ 2005-09-14 20:34 ` Georg Bauhaus 2005-09-14 20:56 ` Hyman Rosen 1 sibling, 0 replies; 38+ messages in thread From: Georg Bauhaus @ 2005-09-14 20:34 UTC (permalink / raw) Dmitry A. Kazakov wrote: > There is no magic in OO. Class is just an ADT. So why do cats not propose to dogs? ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-14 20:20 ` Dmitry A. Kazakov 2005-09-14 20:34 ` Georg Bauhaus @ 2005-09-14 20:56 ` Hyman Rosen 2005-09-15 7:31 ` Dmitry A. Kazakov 1 sibling, 1 reply; 38+ messages in thread From: Hyman Rosen @ 2005-09-14 20:56 UTC (permalink / raw) Dmitry A. Kazakov wrote: > There is no point in adopting what other languages did wrong. Except that Ada isn't doing things any differently than the other languages, it's just adopted a notation that makes it confusing. Ada *does* allow redispatch, type tags *are* in every tagged object, you *can* convert from the type to the classwide type, tagged objects are *always* passed by reference. But where C++ and Java will make dispatching calls where those are indicated by the class specification, Ada forces the programmer to request such calls in a strange way at call sites, and since there's nothing syntactically wrong with making a non-dispatching call, the programmer won't notice the mistake if that request isn't made. > As I said before, it is equivalent to dispatch that does not happen. You may say it all you like, but my code clearly demonstrates that dispatching *does* happen. B::B() calls A::g() and that dispatches to B::f(). The A::g() that's called by B::B() is exactly the same A::g() that's called from outside constructors. ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-14 20:56 ` Hyman Rosen @ 2005-09-15 7:31 ` Dmitry A. Kazakov 2005-09-15 13:19 ` Hyman Rosen 0 siblings, 1 reply; 38+ messages in thread From: Dmitry A. Kazakov @ 2005-09-15 7:31 UTC (permalink / raw) On 14 Sep 2005 13:56:11 -0700, Hyman Rosen wrote: > Dmitry A. Kazakov wrote: >> There is no point in adopting what other languages did wrong. > > Except that Ada isn't doing things any differently than the > other languages, it's just adopted a notation that makes it > confusing. Ada *does* allow redispatch, type tags *are* in > every tagged object, you *can* convert from the type to the > classwide type, tagged objects are *always* passed by > reference. Ada's model is the *only* model I know which allows: 1. A universal-purpose language (like C++ or Ada) to become a pure OO language. 2. To support multiple dispatch 3. To avoid distributed overhead 4. To create a consistent construction/destruction model OK, these opportunities weren't used, so what? > But where C++ and Java will make dispatching > calls where those are indicated by the class specification, > Ada forces the programmer to request such calls in a strange > way at call sites, and since there's nothing syntactically > wrong with making a non-dispatching call, the programmer won't > notice the mistake if that request isn't made. Which mistake? Again, it is solely the actual object's *type* which determines dispatch. It is 100% safe, intuitive and unambiguous. Ada is a typed language! Is C++ one? >> As I said before, it is equivalent to dispatch that does not happen. > > You may say it all you like, but my code clearly demonstrates > that dispatching *does* happen. B::B() calls A::g() No. It calls B::g() which was inherited from A. I don't care if code of B::g() and A::g() share memory or not. They are different functions because their signatures are different. > and that dispatches to B::f(). It does not. It calls B::f() from B::g() -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-15 7:31 ` Dmitry A. Kazakov @ 2005-09-15 13:19 ` Hyman Rosen 2005-09-15 13:45 ` Maciej Sobczak 2005-09-15 17:45 ` Dmitry A. Kazakov 0 siblings, 2 replies; 38+ messages in thread From: Hyman Rosen @ 2005-09-15 13:19 UTC (permalink / raw) Dmitry A. Kazakov wrote: > Which mistake? Again, it is solely the actual object's *type* which > determines dispatch. The mistake of not calling the overridden method of the actual type of the object rather than of the declared type. In OO programming, by-reference objects have two types, their declared base type and their actual created type. In OO programming, it is expected by the programmers that a call to a potentially overridden function using a base by-reference object will dispatch to the overridden function. If the language claims to support OO but then makes it easy to call these methods in such a way that overriding doesn't happen, and without warning, then the language has introduced a dangerous trap. That is supposedly antithetical to the spirit of Ada. > It is 100% safe, intuitive and unambiguous. It is not intuitive at all, except to people whose intuition has been warped. It's just plain wrong. > Ada is a typed language! Is C++ one? It's the Ada language that has caused itself this problem. C++ requires that you clearly distinguish between a reference to an object, a pointer to an object, and the object itself. Thus, when you see a pointer or reference to a base class, you know that the referee object may be of a derived class, and that dispatching may take place. Ada, in trying to be clever about hiding parameter passing mode from the programmer, and by not supporting a distinguished dispatching object syntax, let itself in for a trap. > No. It calls B::g() which was inherited from A. I don't care if code of > B::g() and A::g() share memory or not. They are different functions because > their signatures are different. No, they are the same function. If, for example, I included a static variable within it, calls to A::g() and B::g() would see the same variable. And for that matter, simply write this: #include <iostream> #include <ostream> struct A { void g(); }; struct B : A { }; int main() { std::cout << (&A::g == &B::g) << "\n"; } and you'll see whether the compiler thinks they're the same or not. > It does not. It calls B::f() from B::g() No. There is no B::g separate from A::g. Inherited functions are not copies, they are the same function. There is no code you can write which will demonstrate a difference. ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-15 13:19 ` Hyman Rosen @ 2005-09-15 13:45 ` Maciej Sobczak 2005-09-15 17:45 ` Dmitry A. Kazakov 1 sibling, 0 replies; 38+ messages in thread From: Maciej Sobczak @ 2005-09-15 13:45 UTC (permalink / raw) Hyman Rosen wrote: >>It is 100% safe, intuitive and unambiguous. > > It is not intuitive at all, except to people whose intuition has > been warped. It's just plain wrong. I agree with this. It can be instructive to see how it is done in other languages which claim to support OO. Any examples supporting the "Ada way"? If there are not many, then claiming that the whole world got it wrong whereas Ada got it right does not help, especially those who are learning or evaluating languages. For me it is clear in C++ that whenever I call a function which is virtual I should expect dynamic dispatch if the call is made through the pointer or reference to the base class. If I want to suppress this behaviour, I still can (obj.Base::fun()), which means that the element of control is there and I can take advantage of it when it is what is really needed. It is the question of what is the default that we get implicitly. For me, the default in C++ is correct, because it is consistent with what I understand by dynamic polymorphism. Note also that in C++ there is something like slicing, which actually "deprives" the object from its inherited properties, but people early learn to avoid it - for a reason. -- Maciej Sobczak : http://www.msobczak.com/ Programming : http://www.msobczak.com/prog/ ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-15 13:19 ` Hyman Rosen 2005-09-15 13:45 ` Maciej Sobczak @ 2005-09-15 17:45 ` Dmitry A. Kazakov 2005-09-15 18:54 ` Hyman Rosen 1 sibling, 1 reply; 38+ messages in thread From: Dmitry A. Kazakov @ 2005-09-15 17:45 UTC (permalink / raw) On 15 Sep 2005 06:19:40 -0700, Hyman Rosen wrote: > Dmitry A. Kazakov wrote: >> Which mistake? Again, it is solely the actual object's *type* which >> determines dispatch. > > The mistake of not calling the overridden method of the actual > type of the object rather than of the declared type. There is no actual type, there is only the type specified by the contract. That's the whole point. > In OO > programming, by-reference objects have two types, their declared > base type and their actual created type. That's your view on OO, which is clearly inconsistent with the notion of types. Try to ask yourself simple questions: is "by-reference" a type property? Why by-copy objects (of which type?) have one (which?) type while by-reference objects (of which type?) have more than one? It is rubbish. In a typed language an object has a type, only one type. > In OO programming, it is > expected by the programmers that a call to a potentially overridden > function using a base by-reference object will dispatch to the > overridden function. If the language claims to support OO but then > makes it easy to call these methods in such a way that overriding > doesn't happen, and without warning, then the language has introduced > a dangerous trap. No. Trap is when the declared type does not determine what's going on. >> It is 100% safe, intuitive and unambiguous. > > It is not intuitive at all, except to people whose intuition has > been warped. It's just plain wrong. You haven't presented anything to support this point, other than assumptions about some hypothetical newbies. I (again) formulate the properties of Ada's model: 1. The contract model 2. No space overhead 3. No run-time overhead caused by unnecessary re-dispatch 4. Pure OO (all types can have classes) 5. Compatible to multiple dispatch 6. Consistent with multi-methods 7. Compatible with class construction (dispatching from constructors/destructors can be ) 8. Compatible with assignment, abstract factory and all other cases, where the result is dispatching. 9. Polymorphic object can be coped 10. Can return polymorphic objects on the stack 11. Can aggregate polymorphic objects in other objects 12. Covariant in all parameters Now show me how C++ or Java could accomplish 1..12. >> No. It calls B::g() which was inherited from A. I don't care if code of >> B::g() and A::g() share memory or not. They are different functions because >> their signatures are different. > > No, they are the same function. If, for example, I included a static > variable within it, calls to A::g() and B::g() would see the same > variable. And for that matter, simply write this: > #include <iostream> > #include <ostream> > struct A { void g(); }; > struct B : A { }; > int main() { std::cout << (&A::g == &B::g) << "\n"; } > and you'll see whether the compiler thinks they're the same or not. What should that code prove? That pointers to members is a total mess in C++? Everybody knows it. >> It does not. It calls B::f() from B::g() > > No. There is no B::g separate from A::g. I don't know what "separate" members are. Do you mean memory location? Why should I care of? > Inherited functions are > not copies, they are the same function. They cannot be same because they act on different types. You cannot call B::g() on A. The same question again, is C++ typed? > There is no code you can > write which will demonstrate a difference. class A { public : void g(); }; class B : public A {}; B * Y = new B; A * X = Y; X->B::g (); // Error, how so? X is of B, or maybe not quite? Y->B::g (); // OK -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-15 17:45 ` Dmitry A. Kazakov @ 2005-09-15 18:54 ` Hyman Rosen 2005-09-16 9:32 ` Dmitry A. Kazakov 0 siblings, 1 reply; 38+ messages in thread From: Hyman Rosen @ 2005-09-15 18:54 UTC (permalink / raw) Dmitry A. Kazakov wrote: > There is no actual type, there is only the type specified by the contract. There is an actual type, and it can be recovered from the object through redispatching (or even by simply extracting the type tag, if I'm not mistaken). > That's your view on OO, which is clearly inconsistent with the notion of > types. Perhaps with your notions of types. > is "by-reference" a type property? Yes. In C++ you have "type" and "reference to type" and the two are completely different. A "reference to type" may have as its referant a sub-object of the given type which is a base class of a larger object. An object of "type" is exactly that. Both of these concepts are useful. > Why by-copy objects (of which type?) have one (which?) type while > by-reference objects (of which type?) have more than one? It's not the "by-copy" that gives something a type. When you declare that a parameter has a class type, it has that type and nothing more. It's a separate object within its lifetime, and when the function is called, it's initialized with a copy of the argument. When you have a parameter of reference type, when the function is called it's initialized to refer to the appropriate subobject of the argument. (Actually, newbies in C++ face the same confusion between paramaters of T and T& as Ada newbies do between T and T'Class.) > It is rubbish. In a typed language an object has a type, only one type. You may not like it, but that's the way it's done, including in Ada. > No. Trap is when the declared type does not determine what's going on. In C++ the declared type does determine what's going on. In Ada, you are permitted to go from the declared type to the classwide type, so by your argument C++ reflects the contract model better than Ada does. > I (again) formulate the properties of Ada's model: But no Ada compiler implements the model the way you would like it to be. And I believe that allowing conversion from T to T'Class is a requirement of Ada. > Now show me how C++ or Java could accomplish 1..12. C++ doesn't have polymorphic variables or copying, so some things are out of reach. But the main problem with your approach is that no one wants to implement differently sized pointers for T'Class than for T. For all your posting on this, I've yet to see an implementor so taken by your approach that he's willing to adopt it. > What should that code prove? That pointers to members is a total mess in > C++? Everybody knows it. It proves that they're equal. *That's* what everyone knows. > I don't know what "separate" members are. Do you mean memory location? Why > should I care of? I mean "different", or "distinct", or "not the same", or any other synonym you care to pick. > They cannot be same because they act on different types. You cannot call > B::g() on A. The same question again, is C++ typed? As I said, in OO languages like Ada, C++, and Java, when you have a reference type, the object to which it refers may be a typed base subobject of a larger object of different type. There is only an A::g(), and it operates on an A object which is standalone or which is a base subobject of an object of a class derived from A. Because B inherits g() from A, you may also refer to it as B::g, much as Ada lets you rename one thing as another, but it is only a different name for the same thing. And if you have a 'B b;' it's perfectly legal to say 'b.A::g();'. > X->B::g (); // Error, how so? X is of B, or maybe not quite? Because the rules of C++ (3.4.5/4 of the Standard, if you would like to check) say that the name preceding the :: is first looked up in context. There is no B in A, so it simply fails to compile without ever getting to consider the g() part. ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-15 18:54 ` Hyman Rosen @ 2005-09-16 9:32 ` Dmitry A. Kazakov 2005-09-16 14:52 ` Hyman Rosen 0 siblings, 1 reply; 38+ messages in thread From: Dmitry A. Kazakov @ 2005-09-16 9:32 UTC (permalink / raw) On 15 Sep 2005 11:54:15 -0700, Hyman Rosen wrote: > Dmitry A. Kazakov wrote: >> There is no actual type, there is only the type specified by the contract. > > There is an actual type, and it can be recovered from the object > through redispatching (or even by simply extracting the type tag, > if I'm not mistaken). You are. Tag is said to be "associated", the standard does not require to engrave it on the objects. Moreover, X'Tag is valid for class-wide objects only! See 3.9 (11) >> is "by-reference" a type property? > > Yes. In C++ you have "type" and "reference to type" and the two > are completely different. So T and T& are two different types, then why methods of T can be used with T&? Is there a type conversion between them? When T is converted to T&, why it dispatches to methods of T rather than to ones of T&? A poor C++ newbie asks why the following does not compile: class T& {}; // Isn't T& a type? > A "reference to type" may have as its > referant a sub-object of the given type which is a base class of > a larger object. An object of "type" is exactly that. Both of > these concepts are useful. T != T& is useful, you say. Then why T /= T'Class is not? (:-)) >> Why by-copy objects (of which type?) have one (which?) type while >> by-reference objects (of which type?) have more than one? > > It's not the "by-copy" that gives something a type. When you declare > that a parameter has a class type, it has that type and nothing more. But the actual parameter may have another type. It leaks! > It's a separate object within its lifetime, and when the function is > called, it's initialized with a copy of the argument. A copy, really? It is a strange copy, when the actual parameter is of a derived type. Your theory leaks. > When you have > a parameter of reference type, when the function is called it's > initialized to refer to the appropriate subobject of the argument. Now you have a difficult job to explain what is a subobject. Which type it has. Is it a by-reference or by-copy type, etc. Poor old C++ newbie! > (Actually, newbies in C++ face the same confusion between paramaters > of T and T& as Ada newbies do between T and T'Class.) Huh, that's the minor confusion they face in C++... >> It is rubbish. In a typed language an object has a type, only one type. > > You may not like it, but that's the way it's done, including in Ada. No, Ada is consistent here: "A type is characterized by a set of values, and a set of primitive operations which implement the fundamental aspects of its semantics. An object of a given type is a run-time entity that contains (has) a value of the type." - ARM 3.2 (1) >> No. Trap is when the declared type does not determine what's going on. > > In C++ the declared type does determine what's going on. Finally! (:-)) I.e. C++ is not a typed language. What else evidence you need? > In Ada, you are permitted to go from the declared type to the > classwide type, so by your argument C++ reflects the contract > model better than Ada does. Allowing specific to class-wide conversion was in my view a mistake. However, even though it contradicts to Ada's model, technically it is not a big issue, because tagged types are by-reference anyway. When Ada will have T'Class for non-tagged types as well, for those this conversion will not be allowed. >> I (again) formulate the properties of Ada's model: > > But no Ada compiler implements the model the way you would like > it to be. There is a great difference between not yet implemented things and ones which cannot be under any circumstances. Alone to separate T and T'Class was a revolutionary breakthrough, importance of which many people still do not grasp. >> Now show me how C++ or Java could accomplish 1..12. > > C++ doesn't have polymorphic variables or copying, so some things > are out of reach. But the main problem with your approach is that > no one wants to implement differently sized pointers for T'Class > than for T. Egh? Ada vendors have used fat pointers for decades. Even in C++, print sizeof() of a member function pointer! > For all your posting on this, I've yet to see an > implementor so taken by your approach that he's willing to adopt it. Come on, c.l.a. is a chat root. Do you think I have influence of Bjarne Stroustrup, or what? (:-)) >> What should that code prove? That pointers to members is a total mess in >> C++? Everybody knows it. > > It proves that they're equal. *That's* what everyone knows. Pointers are equal, so what? >> They cannot be same because they act on different types. You cannot call >> B::g() on A. The same question again, is C++ typed? > > As I said, in OO languages like Ada, C++, and Java, when you have a > reference type, the object to which it refers may be a typed base > subobject of a larger object of different type. See? It is of a different type. Now observe that B <: A, it is an asymmetric relation. So B::g() cannot be same as A::g(), because that would require equivalence B :=: A, which is wrong. This is why you cannot call B::g() on A, even if B::g() was inherited, not overridden. My example shows this. At least here C++ is consistent. [my "theory" on] The difference between A::g() and B::g() inherited from A is that B::g() is a composition of three functions: B::g() :=: (B *) o A::g() o (A *) The argument "this" is first converted from B* to A*, then this->A::g() is called, then it is converted back to B*. These conversions might be identity ones (not always in presence of multiple inheritance, BTW), but that gives no right to declare A::g() and B::g() same. [my "theory" off] -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-16 9:32 ` Dmitry A. Kazakov @ 2005-09-16 14:52 ` Hyman Rosen 2005-09-16 15:33 ` Jean-Pierre Rosen 2005-09-16 21:03 ` Dmitry A. Kazakov 0 siblings, 2 replies; 38+ messages in thread From: Hyman Rosen @ 2005-09-16 14:52 UTC (permalink / raw) Dmitry A. Kazakov wrote: > So T and T& are two different types, then why methods of T can be used with > T&? Because the language says so. > Is there a type conversion between them? No. > When T is converted to T&, why > it dispatches to methods of T rather than to ones of T&? T& has no methods. It dispatches to methods of T and derived classes because that's what the language says. It's not unlike the way Ada sometimes gives you an implicit .all on an access type. > A poor C++ newbie > asks why the following does not compile: > > class T& {}; // Isn't T& a type? Because T& cannot be used as the name of a class. All classes are types, but not all types are classes. > T != T& is useful, you say. Then why T /= T'Class is not? (:-)) Actually, you're right about that and I'm wrong. I still think that Ada's requirement that tagged types be passed by reference shows that it was a mistake for the language to try to conceal the parameter passing mode from the programmer. > But the actual parameter may have another type. It leaks! Irrelevant. When the paramater is of a specific class type and you pass a derived type as an argument, the parameter simply receives a copy of the base part of the argument. It's referred to as "slicing". And just as you claim to prefer, once that happens there is no going back to the derived type. The parameter is well and truly only of its declared type. > A copy, really? It is a strange copy, when the actual parameter is of a > derived type. Your theory leaks. Nope. No leak. It really is a copy of just that subpart of the argument. That's why most functions in C++ which take class types as arguments get them by reference. And of course, the distinguished calling object itself is passed by reference; it's address becomes the value of the 'this' keyword in the member function. > Now you have a difficult job to explain what is a subobject. Which type it > has. Is it a by-reference or by-copy type, etc. Poor old C++ newbie! Not difficult at all. When you declare a class, it is either standalone or it inherits from one or more other classes. A reference to any class in this inheritance hierarchy may be initialized to refer to an object of the derived class. (It's slightly more complicated because of access restrictions and the ability to have a class more than once in the inheritance hierarchy, but that's details.) Classes in an inheritance hierarchy are just that - classes. You cannot inherit from anything else, so there is no such thing as a base class which is a reference. > "A type is characterized by a set of values..." But what is the set of values for a tagged type? Doesn't it include the values of all possible derived types? After all, reading 4.6, it certainly seems that I'm allowed to do view conversions back and forth between base and derived tagged types. So it looks like I can have something that's declared as a type 'B' but is really an object of a derived type 'D', and I can take my 'B'-declared object and view-convert it into a 'D'-declared object, all the while maintaining the identity of the object. > > In C++ the declared type does determine what's going on. > > Finally! (:-)) I.e. C++ is not a typed language. What else evidence you > need? Huh? I said "does", not "doesn't". > There is a great difference between not yet implemented things and ones > which cannot be under any circumstances. The requirements for view conversion and by-refernce passing make it seem to me that no Ada compiler will ever implement your version of things. > The argument "this" is first converted from B* to A*, then this->A::g() is > called, then it is converted back to B*. And when this->A::g() calls f(), it does so by dispatching, regardless of whether the A::g() call originated in a constructor or not. ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-16 14:52 ` Hyman Rosen @ 2005-09-16 15:33 ` Jean-Pierre Rosen 2005-09-16 18:37 ` Hyman Rosen 2005-09-16 21:03 ` Dmitry A. Kazakov 1 sibling, 1 reply; 38+ messages in thread From: Jean-Pierre Rosen @ 2005-09-16 15:33 UTC (permalink / raw) Hyman Rosen a �crit : > Irrelevant. When the paramater is of a specific class type > and you pass a derived type as an argument, the parameter > simply receives a copy of the base part of the argument. > It's referred to as "slicing". And just as you claim to prefer, > once that happens there is no going back to the derived type. > The parameter is well and truly only of its declared type. > Not at all. You receive a *view*, not a copy (as you noted above, tagged types are always passed by reference). If you convert it to T'Class, it will dispatch according to its run-time type (i.e. the tag is not changed by parameter-passing - even if the parameter is of a specific type). -- --------------------------------------------------------- J-P. Rosen (rosen@adalog.fr) Visit Adalog's web site at http://www.adalog.fr ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-16 15:33 ` Jean-Pierre Rosen @ 2005-09-16 18:37 ` Hyman Rosen 0 siblings, 0 replies; 38+ messages in thread From: Hyman Rosen @ 2005-09-16 18:37 UTC (permalink / raw) Jean-Pierre Rosen wrote: > Not at all. You receive a *view*, not a copy Sorry, you misunderstood. I was talking to DK about C++, not Ada. Ada works just as you describe, of course. ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-16 14:52 ` Hyman Rosen 2005-09-16 15:33 ` Jean-Pierre Rosen @ 2005-09-16 21:03 ` Dmitry A. Kazakov 2005-09-16 21:33 ` Hyman Rosen 1 sibling, 1 reply; 38+ messages in thread From: Dmitry A. Kazakov @ 2005-09-16 21:03 UTC (permalink / raw) On 16 Sep 2005 07:52:36 -0700, Hyman Rosen wrote: > I still think > that Ada's requirement that tagged types be passed by reference > shows that it was a mistake for the language to try to conceal > the parameter passing mode from the programmer. Why do you think so? The program semantics should be independent from the parameter passing mode. The type identity is another thing. Though IMO, it is always suspicious from the design point view to have objects with identity. Fortunately Ada does not re-dispatch by default. I say fortunately, because it was a number of times were the compiler gave me the message, that a class-wide was expected, but a specific type was found instead. I can remember none of them, where the program were right. In C++ these errors would slip through. >> But the actual parameter may have another type. It leaks! > > Irrelevant. When the paramater is of a specific class type > and you pass a derived type as an argument, the parameter > simply receives a copy of the base part of the argument. > It's referred to as "slicing". And just as you claim to prefer, > once that happens there is no going back to the derived type. > The parameter is well and truly only of its declared type. But it is not a copy of the actual object. It is a copy of its part! The point is that T and T& are no less different C++ than T and T'Class in Ada. But in C++ T& is overloaded with two different meanings: closure of types (class) vs. reference type. As a consequence T& is always a reference to class (except from con/destructors (:-)) You cannot have a reference to a specific type. Same with pointers. In Ada both renaming (a rough equivalent of reference) and access types can be either specific or class-wide. Wasn't Java's final types an unsuccessful attempt to mend this? Further, because T& is not an object, you cannot do such elementary thing as to copy a polymorphic object. >> "A type is characterized by a set of values..." > > But what is the set of values for a tagged type? > Doesn't it include the values of all possible derived types? No. It is the class rooted in the type which do. A T'Class serves as a closure of all types derived from T. > After all, reading 4.6, it certainly seems that I'm allowed > to do view conversions back and forth between base and derived > tagged types. A view conversion is still a conversion. If all types had classes, then for non-tagged types T<->T'Class were a full conversions constructing new objects. In Ada there are things much close to this pattern. Unconstrained array types are similar to classes, their constrained subtypes are similar to specific types. Here bounds play the same role as the tag does. Note that the language does not define how bounds are passed back and forth. It also allows to pass arrays by copy and throw known bounds away. >> There is a great difference between not yet implemented things and ones >> which cannot be under any circumstances. > > The requirements for view conversion and by-refernce passing > make it seem to me that no Ada compiler will ever implement > your version of things. This is required for tagged types only. Let's wait until people finally turn sick of templates! (:-)) >> The argument "this" is first converted from B* to A*, then this->A::g() is >> called, then it is converted back to B*. > > And when this->A::g() calls f(), it does so by dispatching, regardless > of whether the A::g() call originated in a constructor or not. Don't you see that the following two interpretations are equivalent: 1. It dispatches, but the tag changes 2. It does not dispatch, and the tag is constant. It is clear that you prefer (1) because your mental model is many types - one object. But I stick to (2) because I am convinced that the multiple types model will show itself infeasible, should somebody try to formalize it. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-16 21:03 ` Dmitry A. Kazakov @ 2005-09-16 21:33 ` Hyman Rosen [not found] ` <98ox2x9xvj9z.1uh92dslhvt4g.dlg@40tude.net> 0 siblings, 1 reply; 38+ messages in thread From: Hyman Rosen @ 2005-09-16 21:33 UTC (permalink / raw) Dmitry A. Kazakov wrote: > Why do you think so? The program semantics should be independent from the > parameter passing mode. If tagged types were not passed by reference then the program semantics would be severely affected. That's why the language requires that passing mode. But in that case, it should be part of the program, not a hidden requirement. > You cannot have a reference to a specific type. Same with pointers. True (for class types). > Further, because > T& is not an object, you cannot do such elementary thing as to copy a > polymorphic object. No, that's not why. It's purely the pragmatic implementation complexity of arranging for the space and copying of an object of runtime-determined size. Even in Ada objects of T'Class type are somewhat half-baked. They can't live inside records, you can't have arrays of them, etc. > No. It is the class rooted in the type which do. A T'Class serves as a > closure of all types derived from T. > A view conversion is still a conversion. When I view convert from a derived class type to a base class type, and then back again, I have the same contents (and the same object) that I started with. The extra derived stuff cannot magically appear from nowhere; therefore it must have been there all along. Therefore even though I have an object of some declared type I know that it may be carrying along a bunch of extra data. ^ permalink raw reply [flat|nested] 38+ messages in thread
[parent not found: <98ox2x9xvj9z.1uh92dslhvt4g.dlg@40tude.net>]
* Re: tagged record child: override constructor? [not found] ` <98ox2x9xvj9z.1uh92dslhvt4g.dlg@40tude.net> @ 2005-09-17 12:47 ` Georg Bauhaus 2005-09-17 15:56 ` Dmitry A. Kazakov 0 siblings, 1 reply; 38+ messages in thread From: Georg Bauhaus @ 2005-09-17 12:47 UTC (permalink / raw) Dmitry A. Kazakov wrote: > type T tagged record > Self : T_Ptr := T'Unchecked_Access; This won't compile unless you make T limited. ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-17 12:47 ` Georg Bauhaus @ 2005-09-17 15:56 ` Dmitry A. Kazakov 0 siblings, 0 replies; 38+ messages in thread From: Dmitry A. Kazakov @ 2005-09-17 15:56 UTC (permalink / raw) On Sat, 17 Sep 2005 14:47:07 +0200, Georg Bauhaus wrote: > Dmitry A. Kazakov wrote: > >> type T tagged record >> Self : T_Ptr := T'Unchecked_Access; > > This won't compile unless you make T limited. Exactly. Limited implies by-reference. -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-14 7:49 ` Dmitry A. Kazakov 2005-09-14 9:05 ` Maciej Sobczak @ 2005-09-14 16:14 ` Martin Krischik 2005-09-14 16:57 ` Dmitry A. Kazakov 1 sibling, 1 reply; 38+ messages in thread From: Martin Krischik @ 2005-09-14 16:14 UTC (permalink / raw) Dmitry A. Kazakov wrote: > here is no difference between T and > T'Class (the source of countless problems.) Actually there is - its called T and T&. Problem is that T will allways pass by copy and therefor is useless except for very primitive classes like maybe string<>. Martin -- mailto://krischik@users.sourceforge.net Ada programming at: http://ada.krischik.com ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-14 16:14 ` Martin Krischik @ 2005-09-14 16:57 ` Dmitry A. Kazakov 2005-09-14 18:35 ` Martin Krischik 0 siblings, 1 reply; 38+ messages in thread From: Dmitry A. Kazakov @ 2005-09-14 16:57 UTC (permalink / raw) On Wed, 14 Sep 2005 18:14:20 +0200, Martin Krischik wrote: > Dmitry A. Kazakov wrote: > >> here is no difference between T and >> T'Class (the source of countless problems.) > > Actually there is - its called T and T&. Not quite. Compare: T& X = Factory (...); with X : T'Class := Factory (...); -- Regards, Dmitry A. Kazakov http://www.dmitry-kazakov.de ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-14 16:57 ` Dmitry A. Kazakov @ 2005-09-14 18:35 ` Martin Krischik 0 siblings, 0 replies; 38+ messages in thread From: Martin Krischik @ 2005-09-14 18:35 UTC (permalink / raw) Dmitry A. Kazakov wrote: > On Wed, 14 Sep 2005 18:14:20 +0200, Martin Krischik wrote: > >> Dmitry A. Kazakov wrote: >> >>> here is no difference between T and >>> T'Class (the source of countless problems.) >> >> Actually there is - its called T and T&. > > Not quite. Compare: > > T& X = Factory (...); > > with > > X : T'Class := Factory (...); ROFL. That is indeed the weekness of the C++ concept and the source of countless problems. :-). Martin -- mailto://krischik@users.sourceforge.net Ada programming at: http://ada.krischik.com ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-13 19:29 ` Ludovic Brenta 2005-09-14 7:49 ` Dmitry A. Kazakov @ 2005-09-14 9:28 ` Alex R. Mosteo 2005-09-14 16:10 ` Martin Krischik 2 siblings, 0 replies; 38+ messages in thread From: Alex R. Mosteo @ 2005-09-14 9:28 UTC (permalink / raw) Ludovic Brenta wrote: > Martin Krischik <krischik@users.sourceforge.net> writes: > >>That's because all class members are virtual in Ada. > > > I think this needs to be elaborated on. > > All _primitive operations of a tagged type_ are _potentially_ > "virtual" in Ada. Each caller of such a primitive operation decides, > at the call site, whether the dispatching is static or dynamic. > > In contrast, in (my recollection of) C++, all virtual methods dispatch > dynamically from all call sites: the decision is not made at the point > of call but at the point of definition of the method. This is worth stressing. I was bitten by this distinction in cases like this: type Blah is tagged ...; procedure Do_Something (B : Blah); -- Default processing, -- To be overriden/extended by descendant types. procedure Run (B : Blah) is begin ... Do_Something (B); end Run; If you make a descendant type (say Duh), the call inside Duh.Run will not be dispatching, but will call the Blah.Do_Something. This is plain vanilla consequence of what you say, but for newcomers to Ada could be an easy mistake to made. -- Take the Snape polls: http://snape.mosteo.com [Updated 16/05] ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-13 19:29 ` Ludovic Brenta 2005-09-14 7:49 ` Dmitry A. Kazakov 2005-09-14 9:28 ` Alex R. Mosteo @ 2005-09-14 16:10 ` Martin Krischik 2 siblings, 0 replies; 38+ messages in thread From: Martin Krischik @ 2005-09-14 16:10 UTC (permalink / raw) Ludovic Brenta wrote: > Martin Krischik <krischik@users.sourceforge.net> writes: >> That's because all class members are virtual in Ada. > > I think this needs to be elaborated on. > > All _primitive operations of a tagged type_ are _potentially_ > "virtual" in Ada. Each caller of such a primitive operation decides, > at the call site, whether the dispatching is static or dynamic. > In contrast, in (my recollection of) C++, all virtual methods dispatch > dynamically from all call sites: the decision is not made at the point > of call but at the point of definition of the method. In theory: No, C++ will staticly call function when possible. In praxis: In 99% of the time there is only a pointer/reference available and C++ need to dispatch then. You see the point of a virtual function that there are child classes (unlike i.E. string<> where there are no child classes). Since C++ has no 'Class all child classes need to be dealt by pointer/reference use. so you need to dispatch most of the time Martin -- mailto://krischik@users.sourceforge.net Ada programming at: http://ada.krischik.com ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-13 5:58 tagged record child: override constructor? sean.gilbertson 2005-09-13 6:39 ` David Trudgett 2005-09-13 7:32 ` Dmitry A. Kazakov @ 2005-09-13 9:33 ` Georg Bauhaus 2005-09-13 16:37 ` Jeffrey Carter 3 siblings, 0 replies; 38+ messages in thread From: Georg Bauhaus @ 2005-09-13 9:33 UTC (permalink / raw) sean.gilbertson@gmail.com wrote: > I have a tagged record that is declared private, along with a > constructor function which returns an instance. I do this to enforce > the assignment of several required fields in the record, so if you know > how to do this another way, please let me know! Another option in addition to those already mentioned is to make the constructor function part of a nested construction package. This way the function is no longer a primitive operation of the type, and can be used for initialising the T-components of type derived from T. package P ... type T .... package Construction ... function make(...) return T ... end Construction; end P; ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-13 5:58 tagged record child: override constructor? sean.gilbertson ` (2 preceding siblings ...) 2005-09-13 9:33 ` Georg Bauhaus @ 2005-09-13 16:37 ` Jeffrey Carter 2005-09-13 18:55 ` Robert A Duff 3 siblings, 1 reply; 38+ messages in thread From: Jeffrey Carter @ 2005-09-13 16:37 UTC (permalink / raw) sean.gilbertson@gmail.com wrote: > I have a tagged record that is declared private, along with a > constructor function which returns an instance. I do this to enforce > the assignment of several required fields in the record, so if you know > how to do this another way, please let me know! It depends on what you mean by "enforce the assignment of several required fields". It is impossible to force the client to call your function, though you can arrange things so that the type is useless to the client if the client doesn't first call your function. If you have type T is private; function Make return T; then you don't need Make at all. You can ensure the required fields are initialized through default values: private type T is record X : Integer := -37; Y : Character := '*'; Z : Duration; end record; (Note that this is a property of record types in general; whether your record type is tagged doesn't affect this.) If you have fields you can't easily initialize through defaults, you can use the initialization features of controlled types: private type T is new Ada.Finalization.Controlled with record ... end record; procedure Initialize (Object : in out T); Initialize is called for every object of type T when it is created. On the other hand, if you mean the assignment of client-supplied values to the required components, your choices are more limited. If the values are such that they may be discriminants, then that's a way to force the client to supply them: type T (X : Integer) is private; If your required fields cannot be discriminants, then things get more complicated. You can include a Boolean field, default initialized to False, which is set to True by your function. All other operations of the type then check this field, and raise an exception if it is False: type T is private; procedure Initialize (X : out T; ...); Not_Initialized : exception; procedure Op (X : in out T); private type T is record Initialized : Boolean := False; ... end record; ... procedure Initialize (X : out T; ...) is ... begin -- Initialize X.Initialized := True; ... end Initialize; procedure Op (X : in out T) is ... begin -- Op if not X.Initialized then raise Not_Initialized; end if; ... end Op; This makes the type useless unless an object is first initialized. None of this has anything to do with the fact that if T is tagged and a primitive operation of T is a function that returns T, then, yes, you do have to override such functions for each child of T. -- Jeffrey Carter "Now go away or I shall taunt you a second time." Monty Python and the Holy Grail E-mail: jeffrey_r_carter-nr [commercial-at] raytheon [period | full stop] com ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-13 16:37 ` Jeffrey Carter @ 2005-09-13 18:55 ` Robert A Duff 2005-09-13 22:18 ` Jeffrey Carter 0 siblings, 1 reply; 38+ messages in thread From: Robert A Duff @ 2005-09-13 18:55 UTC (permalink / raw) Jeffrey Carter <spam@spam.com> writes: > sean.gilbertson@gmail.com wrote: > > I have a tagged record that is declared private, along with a > > constructor function which returns an instance. I do this to enforce > > the assignment of several required fields in the record, so if you know > > how to do this another way, please let me know! > > It depends on what you mean by "enforce the assignment of several > required fields". It is impossible to force the client to call your > function, though you can arrange things so that the type is useless to > the client if the client doesn't first call your function. [... lots of good possibilities snipped...] There's another way to "enforce...": package P is type T(<>) is private; function Make_T(...) return T; private type T is record... end P; use P; Now clients can't create uninitialized objects of type T: X: T; -- illegal type T_Ptr is access T; Y: T_Ptr := new T; -- illegal type R is record Component: T; -- illegal end record; type A is array(...) of T; -- illegal The client is forced to call Make_T: X: T := Make_T(...); -- OK Y: T_Ptr := new T'(Make_T(...)); -- OK etc. - Bob ^ permalink raw reply [flat|nested] 38+ messages in thread
* Re: tagged record child: override constructor? 2005-09-13 18:55 ` Robert A Duff @ 2005-09-13 22:18 ` Jeffrey Carter 0 siblings, 0 replies; 38+ messages in thread From: Jeffrey Carter @ 2005-09-13 22:18 UTC (permalink / raw) Robert A Duff wrote: > > There's another way to "enforce...": > > package P is > type T(<>) is private; > function Make_T(...) return T; > private > type T is record... > end P; Right. I forgot indefinite types. -- Jeffrey Carter "Now go away or I shall taunt you a second time." Monty Python and the Holy Grail E-mail: jeffrey_r_carter-nr [commercial-at] raytheon [period | full stop] com ^ permalink raw reply [flat|nested] 38+ messages in thread
end of thread, other threads:[~2005-09-17 15:56 UTC | newest] Thread overview: 38+ messages (download: mbox.gz / follow: Atom feed) -- links below jump to the message on this page -- 2005-09-13 5:58 tagged record child: override constructor? sean.gilbertson 2005-09-13 6:39 ` David Trudgett 2005-09-13 7:32 ` Dmitry A. Kazakov 2005-09-13 7:56 ` tmoran 2005-09-13 15:23 ` sean.gilbertson 2005-09-13 17:37 ` Martin Krischik 2005-09-13 19:29 ` Ludovic Brenta 2005-09-14 7:49 ` Dmitry A. Kazakov 2005-09-14 9:05 ` Maciej Sobczak 2005-09-14 13:20 ` Dmitry A. Kazakov 2005-09-14 13:52 ` Hyman Rosen 2005-09-14 16:47 ` Dmitry A. Kazakov 2005-09-14 17:16 ` Hyman Rosen 2005-09-14 20:20 ` Dmitry A. Kazakov 2005-09-14 20:34 ` Georg Bauhaus 2005-09-14 20:56 ` Hyman Rosen 2005-09-15 7:31 ` Dmitry A. Kazakov 2005-09-15 13:19 ` Hyman Rosen 2005-09-15 13:45 ` Maciej Sobczak 2005-09-15 17:45 ` Dmitry A. Kazakov 2005-09-15 18:54 ` Hyman Rosen 2005-09-16 9:32 ` Dmitry A. Kazakov 2005-09-16 14:52 ` Hyman Rosen 2005-09-16 15:33 ` Jean-Pierre Rosen 2005-09-16 18:37 ` Hyman Rosen 2005-09-16 21:03 ` Dmitry A. Kazakov 2005-09-16 21:33 ` Hyman Rosen [not found] ` <98ox2x9xvj9z.1uh92dslhvt4g.dlg@40tude.net> 2005-09-17 12:47 ` Georg Bauhaus 2005-09-17 15:56 ` Dmitry A. Kazakov 2005-09-14 16:14 ` Martin Krischik 2005-09-14 16:57 ` Dmitry A. Kazakov 2005-09-14 18:35 ` Martin Krischik 2005-09-14 9:28 ` Alex R. Mosteo 2005-09-14 16:10 ` Martin Krischik 2005-09-13 9:33 ` Georg Bauhaus 2005-09-13 16:37 ` Jeffrey Carter 2005-09-13 18:55 ` Robert A Duff 2005-09-13 22:18 ` Jeffrey Carter
This is a public inbox, see mirroring instructions for how to clone and mirror all data and code used for this inbox