From mboxrd@z Thu Jan 1 00:00:00 1970 X-Spam-Checker-Version: SpamAssassin 3.4.4 (2020-01-24) on polar.synack.me X-Spam-Level: X-Spam-Status: No, score=-1.9 required=5.0 tests=BAYES_00 autolearn=ham autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,7a180be12347b9d3,start X-Google-Attributes: gid103376,public X-Google-Thread: 1108a1,7a180be12347b9d3,start X-Google-Attributes: gid1108a1,public X-Google-ArrivalTime: 2002-02-07 02:24:14 PST Path: archiver1.google.com!news1.google.com!newsfeed.stanford.edu!news-spur1.maxwell.syr.edu!news.maxwell.syr.edu!newsfeed00.sul.t-online.de!t-online.de!news-lei1.dfn.de!news-fra1.dfn.de!fu-berlin.de!uni-berlin.de!tar-alcarin.cbb-automation.DE!not-for-mail From: dmitry@elros.cbb-automation.de (Dmitry A. Kazakov) Newsgroups: comp.lang.ada,comp.object Subject: Merits of re-dispatching [LONG] Date: Thu, 07 Feb 2002 10:26:00 GMT Message-ID: <3c62524f.93369796@News.CIS.DFN.DE> NNTP-Posting-Host: tar-alcarin.cbb-automation.de (212.79.194.111) X-Trace: fu-berlin.de 1013077451 44879529 212.79.194.111 (16 [77047]) X-Newsreader: Forte Free Agent 1.21/32.243 Xref: archiver1.google.com comp.lang.ada:19694 comp.object:33547 Date: 2002-02-07T10:26:00+00:00 List-Id: Hi! C++ and Ada 95 support re-dispatching, which I think is inherently unsafe. Consider the following: type A is new Ada.Finalization.Limited_Controlled with ... procedure Foo (Object : A); procedure Finalize (Object : in out A) ; Now let AA derived from A override Foo and Finalize: type AA is new A with record ... -- Some components, which may use components of A end record; procedure Foo (Object : AA) ; procedure Finalize (Object : in out AA) is begin ... -- Finalize components, maybe accessing components of A Finalize (A (Object)); -- Finalize parent end Finalize; Consider the following implementation of A.Finalize: procedure Finalize (Object : in out A) is begin Foo (A'Class (Object)); -- Re-dispatch to the child's Foo end Finalize; When A is finalized as a part of AA, it re-dispatches to AA.Foo, which may access already finalized parts of AA! A user of A has no way to learn the problem from the specification of A. He must look into the implementation of A.Finalize. The same is valid for Initialize as well. Actually it is a common error among fresh baked C++ programmers to call virtual functions (dispatch) from constructors. Though merits of re-dispatching seem to me suspicious, it is easy to implement it without casting. Let we really want object's IDENTITY, i.e. objects which always "know" their actual type. Ada 95 perfectly supports this without any casting! package Self_Identifying is type A is new Ada.Finalization.Limited_Controlled with private; type A_Ptr is access all A'Class; procedure Finalize (Object : in out A); procedure Foo (Object : in out A); private type A is new Ada.Finalization.Limited_Controlled with record Self : A_Ptr := A'Unchecked_Access; -- Class wide self-reference end record; end Self_Identifying; The implementation of Finalize: procedure Finalize (Object : in out A) is begin Foo (Object.Self.all); -- Dispatch to child's Foo end Finalize; Now, if type AA is new A with null record; procedure Foo (Object : in out AA); Then an object of AA will dispatch to AA.Foo during finalization exactly like in the first example. The difference is that self-identification can be explicitly exposed and documented: function Identity (Object : A['Class]) return A_Ptr is begin return Object.Self; end Identity; With re-dispatching the contract of Finalize is not clear. According to its specification it receives an argument of type A. Thus there should be no legal way for Finalize to discover that actually an instance of AA was passed. I.e. Foo (A'Class (Object)) should always statically dispatch to A.Foo. Yet it dispatches to AA.Foo. This is a violation of substitutability. [Not that I hold LSP for a sacral cow, but there is no reason to encourage its violation.] I think that there should be no exceptions from the rule "Want dispatching in a subprogram? Make the argument class wide." What is worse is that implementation of re-dispatching requires that the type tag be accessible for not only class wide objects but also for specific ones. Thus tagged types are reference types, type tag shall be a part of a tagged object. As a consequence elementary types cannot be tagged and so they are excluded from OOP. Maybe I have missed something, but I see no other reason why not to allow for all types inheritance, dispatching subroutines and class wide objects. Methodically re-dispatching breaks segregation of class wide and specific types returning us to the languages like C++, where there is no difference between them. So Ada lingers somewhere in-between. Is there examples where re-dispatching is really unavoidable? Regards, Dmitry Kazakov