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=-0.3 required=5.0 tests=BAYES_00, REPLYTO_WITHOUT_TO_CC autolearn=no autolearn_force=no version=3.4.4 X-Google-Language: ENGLISH,ASCII-7-bit X-Google-Thread: 103376,1042f393323e22da X-Google-Attributes: gid103376,public From: "John G. Volan" Subject: Re: Any research putting c above ada? Date: 1997/05/09 Message-ID: <3372D44E.5F44@sprintmail.com> X-Deja-AN: 240403348 References: <208C9C61CA05C32B.65D82DC950AAA33A.D68E7B27EB42E98A@library-proxy.airnews.net> Organization: Sprint Internet Passport Reply-To: johnvolan@sprintmail.com Newsgroups: comp.lang.ada Date: 1997-05-09T00:00:00+00:00 List-Id: Kevin Cline wrote: > > eachus@spectre.mitre.org (Robert I. Eachus) wrote: > > > First, many of the calls to malloc and free in C and C++ just go > >away, since a lot of the allocations that have to be done on the heap > >in C end up on the stack in Ada. The most obvious example is that a > >function can return an unconstrained array without the programmer > >putting it in the heap. The compiler may or may not use the > >heap--most prefer to play games with the stack or to have two > >stacks--but that is an implementation detail, and there is no > >(programmer) risk of memory leaks or invalid pointers. > > The last time I tried this, the compiler copied the entire array when the > function returned. That much overhead was completely unexpected, and in my > case, also unacceptable. I don't like languages that hide large amounts of > overhead in simple operations. It's okay if they let me hide it through > operator overloading. But I don't enjoy finding out when I port my program > that a function return is massively wasteful. In C++, you would solve this > problem by passing a reference to a container. And promptly run into bizarre undefined behavior, because the container whose reference you were returning happened to be one you declared locally on the stack: MyContainer& MyClass::myFunction () const { MyContainer myContainer; ... // build value in myContainer return myContainer; // dangling reference returned after destructor!!! } or MyContainer* MyClass::myFunction () const { MyContainer myContainer; ... // build value in myContainer return &myContainer; // dangling pointer returned after destructor!!! } AFAIK, C++ compilers don't warn you about this. On the other hand, Ada95's extensive rules about static accessibility levels guarantee that this sort of thing won't happen, because it's illegal: type My_Container_Pointer_Type is access all My_Container_Type; function My_Function (This : in My_Class_Type) return My_Container_Pointer_Type is My_Container : aliased My_Container_Type; begin ... -- build value in My_Container return My_Container'Access; -- compiler error! My_Container too deep! end My_Function; Of course, if you know what you're doing in C++ (no small feat), you'll be sure to return by value: MyContainer MyClass::myFunction () const { MyContainer myContainer; .. // build value in myContainer return myContainer; // copy constructor called on return } But this, while safe, yields the same efficiency concerns you raised about Ada's copying-on-return: function My_Function (This : in My_Class_Type) return My_Container_Type is My_Container : My_Container_Type; begin ... -- build value in My_Container return My_Container; -- copy done on return end My_Function; -- (and Adjust called if Controlled) So now, if you _really_ know what you're doing, you could fall back on using the heap: MyContainer* MyClass::myFunction () const { MyContainer *myContainer = new MyContainer; ... // build value in *myContainer return myContainer; } The same can of course be done in Ada95: function My_Function (This : in My_Class_Type) return My_Container_Pointer_Type is My_Container : My_Container_Pointer_Type := new My_Container_Type; begin ... -- build value in My_Container.all return My_Container; end My_Function; But then we're back where we started, doing our own heap management by hand: { MyClass m1, m2, m3; MyContainer *c1 = m1.myFunction(); MyContainer *c2 = m2.myFunction(); MyContainer *c3 = m3.myFunction(); ... 100 lines later delete c1; delete c2; } // oops! forgot c3! memory leak! Of course, you can fall into the same trap in Ada95: declare procedure Deallocate is new Ada.Unchecked_Deallocation (My_Container_Type, My_Container_Pointer_Type); ... M1, M2, M3 : My_Class_Type; C1 : My_Container_Pointer_Type := My_Function(M1); C2 : My_Container_Pointer_Type := My_Function(M2); C3 : My_Container_Pointer_Type := My_Function(M3); begin ... 100 lines later Deallocate (C1); Deallocate (C2); end; -- oops! forgot C3! memory leak! So don't turn your nose up so fast about the return-by-copy mechanism in Ada95 (or in C++ for that matter). Sure, you lose some efficiency, but you get a lot of safety from that! [snip] > > Buffer := To_Unbounded_String(My_IO.Get_Line(Foo)); [snip] > > In this example, the length of Buffer changes each time through > >the loop, but again the programmer doesn't have to manage any pointers, > >allocation, or deallocation. > > No, but he will probably pay for the string to be copied twice, once upon > return from My_IO.Get_Line, and once in the conversion. Probably, but I think it depends on the particular compiler and what fancy optimizations it has. I believe a smart compiler can legally eliminate one of these copies ... but I should let the compiler gurus have the last word on this. ------------------------------------------------------------------------ Internet.Usenet.Put_Signature (Name => "John G. Volan", Home_Email => "johnvolan@sprintmail.com", Slogan => "Ada95: The World's *FIRST* International-Standard OOPL", Disclaimer => "These opinions were never defined, so using them " & "would be erroneous...or is that just nondeterministic now? :-) "); ------------------------------------------------------------------------