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=unavailable autolearn_force=no version=3.4.4 Path: eternal-september.org!reader01.eternal-september.org!reader02.eternal-september.org!news.eternal-september.org!mx02.eternal-september.org!.POSTED!not-for-mail From: Brian Drummond Newsgroups: comp.lang.ada Subject: Re: Instantiating package problems Date: Mon, 4 Jan 2016 14:23:01 -0000 (UTC) Organization: A noiseless patient Spider Message-ID: References: <7dcd49f3-b04f-4ea3-b431-5c27f73b9afe@googlegroups.com> <87poxixqmy.fsf@theworld.com> Mime-Version: 1.0 Content-Type: text/plain; charset=UTF-8 Content-Transfer-Encoding: 8bit Injection-Date: Mon, 4 Jan 2016 14:23:01 -0000 (UTC) Injection-Info: mx02.eternal-september.org; posting-host="da745e888d4a5182b5fda6212bbb0a63"; logging-data="16103"; mail-complaints-to="abuse@eternal-september.org"; posting-account="U2FsdGVkX1/p/42ycLJrotX8s5+NCFWabNgyvBw7clc=" User-Agent: Pan/0.139 (Sexual Chocolate; GIT bf56508 git://git.gnome.org/pan2) Cancel-Lock: sha1:37gfdzBKGlPsCa4ynJ7oa3EaeoA= Xref: news.eternal-september.org comp.lang.ada:29007 Date: 2016-01-04T14:23:01+00:00 List-Id: On Sun, 03 Jan 2016 16:30:02 -0800, Andrew Shvets wrote: > Ok, I think I understand. In Ada, every vanilla package imports all of > the public functions/procedures to be used and can be thought of as > static functions in C++. You're starting to get there. You can't create an instance of a package, because a package already is an instance - like a C++ namespace rather than a C include. Then the variables, functions etc exported are "static" in C/C++ jargon. ("Static" has its own meanings in Ada and other non-C languages, it's advisable to be clear when you're using C-specific jargon). (You CAN create an instance of a *generic* package, which is then an ordinary package. For example you could make Calculator generic on a numeric type, then create different instances of it with Integer or modular or floating point types giving FloatCalc.Addition for example.) Being its own namespace, a package's functions can be used either as qualified names, Calculator.Addition, or made locally visible by "use Calculator" (cf C++ "using"). You can also use renames to simplify long (e.g.qualified) names, and operator overloading if you prefer to write C := A + B; > However, I can create instances of different > records in order to retain an element of state like I would in C++ when > I create an object. Almost right. As C++ classes are basically structs or unions, Ada uses the record as the basis for object oriented programming, by making it a "tagged record" where you want inheritance. Record declarations, tagged or untagged, are type declarations, then you can declare instances of that record (objects). But first : you don't need a record to give your package state. A package can contain variables, and even export them (declare them in the package specification). NB this is not good practice - it allows the package user to fiddle with the variable. Better to move the variable declaration into the "private part" or even the package body itself, and only allow access to it via approved methods. As this means your application can only have one instance of this state, you may recognise the singleton pattern - an Ada singleton is simply a package. But assuming you need multiple objects, you will declare and use records. It's good practice to wrap a record declaration and all related subprograms in a package. You can keep the details of the record "private" so that users can't fiddle with its contents directly, only through subprograms in the same package. (I'm not providing samples - you'll find them easily enough once you grasp the basics, but just ask if you can't) If you declare tagged records... (1) you can later extend them, providing inheritance type Derived is new Base with null record; (2) although functions are declared with function(argument) notation, they can be called with either function(argument) or object.function notation. (3) MyVar : Base; declares a variable holding a Base ONLY MyVar : Base'Class; declares a variable holding a Base or any Derived type. This distinction is a key difference from most other OO languages. (4) there are rules regarding primitive and despatching methods, allowing dynamic despatching where necessary but optimising it away otherwise. (5) You can declare methods "overriding" or not, to catch certain types of error (6) You have interfaces to provide a limited form of multiple inheritance, but not full MI. (7) As with arrays, it's normal to handle (allocate a variable containing) the object itself, rather than an access (pointer) to it where possible. Access types with allocation impose the normal sorts of problems (albeit with stricter checks to limit them) but there are strategies to reduce the need for them : (a) Ada.Containers (like the C++ STL, which actually started life as an Ada project) (b) storage pools, which allow automatic release when the entire pool goes out of scope, (c) patterns for implementing various types of smart pointer. (8) Constructors and destructors don't happen the same way. Think of Ada as adopting the Object Factory pattern, as a first approximation. BUT... (9) If you need things to happen automatically on object creation, assignment, destruction, look at Controlled Types. much more, but that's probably enough for now. -- Brian