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: 109fba,f92fbb4a0420dd57,start X-Google-Attributes: gid109fba,public X-Google-Thread: 103376,f92fbb4a0420dd57,start X-Google-Attributes: gid103376,public From: wnewman@netcom.com (Bill Newman) Subject: some questions re. Ada/GNAT from a C++/GCC user Date: 1996/03/27 Message-ID: X-Deja-AN: 144492689 sender: wnewman@netcom6.netcom.com organization: NETCOM On-line Communication Services (408 261-4700 guest) followup-to: comp.lang.ada,comp.lang.c++ newsgroups: comp.lang.ada,comp.lang.c++ Date: 1996-03-27T00:00:00+00:00 List-Id: I have been skimming _Ada as a Second Language_ (second edition) and I have looked at the GNAT documentation. I am impressed: Ada and GNAT look like they should do a pretty good job of meeting their design criteria. However, before I start planning my next project in Ada :-) there are a few things I'd like to know.. (I hope against hope that the tone of the answers will resemble the cross-language comparisons in _Ada as a Second Language_ more closely than it resembles the recent C++/Ada flamewar.) The Ada Programming FAQ pooh-poohs STL, but I like it. (Yes, I know I could write my own versions of what I need, but a bunch of Ada programmers shouldn't need to be told that that's not the ideal solution.) This FAQ also says the Booch components library is coming -- when? Does GNAT completely implement generics as defined in the standard? (I ask because I have heard that no compiler, G++ otherwise, has yet implemented C++ templates completely, and the G++ implementation caused me lots of hassles before 2.7.x, and still causes some hassles now.) I *assume* that GNAT supports exceptions completely since they're an integral part of the language and I didn't see any disclaimers, but since AFAIK G++ doesn't do them very well, I'd like to double-check: how well does GNAT do exceptions? I didn't notice anything about garbage collection in the GNAT docs, so I assume it doesn't support it. Will GNAT support GC in the foreseeable future? How well does GDB work with GNAT output? Is it possible to get GDB to interactively call arbitrary procedures/functions from GNAT-generated code? I have been very frustrated by the way that I can't get GDB to call operator<<(ostream&, const Some_Class&) interactively, which makes it very painful to inspect the state of a program produced by G++. (I do *not* appreciate having to inspect my data raw field by raw field for every object in a complicated graph.) Would I have the same problem when inspecting the state of programs produced by GNAT? Is there any way in Ada to iterate abstractly over the contents of a container, i.e. without writing each loop in a way which depends strongly on the implementation of the container? I know I could define a container class Foo_Basket_Type which would let me do something like for I = 1 .. Size(Foo_Basket) loop -- class implemented as array Sum := Sum + Bletchery(Element(Foo_Basket, I)); end loop; or I : Foo_Basket_Iterator_Type := Head(Foo_Basket); .. while not Is_Done(I) loop -- class implemented as list Sum := Sum + Bletchery(dereference I); I := Next(I); end loop; but what I'd really like to is something like for each I in Foo_Basket loop -- don't much care how class is implemented Sum := Sum + Bletchery(dereference I); end loop; I understand that in languages like Sather, I could do this without macros. In G++ I can come pretty close to this with CPP macros: FOR_EACH(i, foo_basket) sum += bletchery(*i); In standard C++ (without the G++ `typeof' operator) I'd write something like FOR_EACH(i, Foo_Basket::Iterator, foo_basket) sum += bletchery(*i); I use this idiom a lot -- about once per 40 lines of code in my current project. I much prefer it to the alternative of making my code depend on the implementation of the container. Is there any way of doing something like this in Ada? (I imagine there is, since the alternatives look unnecessarily difficult to maintain.) Due in part to my problems with GDB mentioned above, my C++ programs tend to contain a lot of calls to macros MUTTER1, MUTTER2, etc. defined either as no-op (for low levels of verbosity) or as #define MUTTER1(x) do { cerr << mutter_prefix << x << '\n'; } while (false) so that I can write things like MUTTER1("done with sampling, w = " << w << ", table = " << table); concisely. It's my impression that Ada's I/O facilities make it hard to do trivial things like this concisely. Is this wrong? I don't really want to have to write if (Global_Verbosity >= 1) then Write_Mutter_Indent(Global_Mutter); Write(Global_Mutter, "done with sampling, W = "); Write(Global_Mutter, W); Write(Global_Mutter, ", table = "); Write(Global_Mutter, Table); Write_Line(Global_Error); -- silly typo encouraged by excess verbosity end if; for a simple statement like the one above. When I make two different instantiations of a generic package with the same arguments, I understand the compiler treats them formally as two different packages, which is OK with me. However, I'd appreciate knowing the compiler wouldn't actually output two redundant copies of the corresponding (identical?) machine code, but instead share the code. I saw somewhere that the compiler is given considerable freedom to share one instantiation between several arguments if it thinks it's appropriate, which is also OK with me. However, I haven't seen any guarantee that the compiler won't output redundant copies for instantiations with identical arguments. Is there such a guarantee? Can someone give me an idea what kind of compilation speed I could expect from GNAT on a 486DX2/66 with 20 Mb of RAM, for projects of 1,000-50,000 lines? (I'm interested both in the time required for complete recompilation and in the time required for recompilation after a trivial local change.) My examples of `for each' and `mutter' above share a common feature: I'd like to abstract away a common pattern so that I only need to type each argument once, and I haven't figured out how to do it in Ada. I don't object to the rest of the verbosity I have seen in Ada: I can see that it might make programs more readable, and since it's checked by the compiler it shouldn't make programs significantly harder to maintain. However, the kind of verbosity required to hand-code patterns like `for each' and `mutter' does not seem to make code more readable, and since it is not checked by the compiler I'm afraid it might cause maintenance problems by making it possible to add bugs by changing some but not all occurrences of the redundant terms. Are there ways (e.g. in the cases described above, or other cases that I haven't thought of yet) in which Ada will force me to do this kind of thing where C++ wouldn't? If so, is this a bad thing (as I suspect), or is it a positive feature in some sense that I haven't figured out, or is it just the price we pay for the benefit of being guaranteed that no one has abused macros in the program? I also have a few idle questions about the design and application of Ada 95, more-or-less unrelated to the question of whether I'll decide to use it: Why doesn't Ada 95 allow declarations to be interspersed with ordinary statements as C++ does? (Or does it? _Ada as a Second Language_ is a big book!) It seems to me that the C++ approach is a small but definite win. Does it interact very badly somehow with all those guarantees on elaboration order? Someone remarked -- in this newsgroup recently IIRC -- that macros were explicitly disallowed in the design goals for Ada (Ada 83?), since they make programs hard to understand. I don't remember the exact wording, but as I remember the key reason was that you would never know what was a macro and what wasn't without reading the entire program hunting for macro definitions. (I searched for `macro' in the Rationale without success, so I'm just going on speculation and dim memory here.) It seems to me that that is a funny objection: most macro processors these days don't require a characteristic pattern to introduce macro expansions, but as far as I can tell there's no reason that you couldn't restrict macro expansion to e.g. patterns preceded by the keyword `macro'. Granted, C's macro facilities are fairly disgusting and inconsistent with the design goals of Ada, but it seems to me a macro facility (or `generic syntax' facility?) more consistent with Ada could be useful -- perhaps something closer to Scheme's `hygienic macros' than to C's macro preprocessor. Some restrictions (e.g. only allowing macros to expand into expressions, sequences of statements, or sequences of declarations, and not into arbitrary textual strings) and perhaps importing from Lisp the idea that macros are transformations on patterns of language tokens and expressions, rather than transformations on patterns of characters) could do a pretty decent job of discouraging people from writing truly screwy macros without forbidding useful macros like `For_Each' or (to generalize `MUTTER') `Write_Sequence' (sending a bunch of items to the same stream, and using the language's overloading facility to sort out which version of the Write procedure to call for each). Would this have been a good idea, except perhaps that the language is too complicated already, or would this be a bad idea for some more fundamental reason? Finally, I found it intriguing when someone (somewhere in the endless C++ vs. Ada thread) described a program which used exceptions to convert runtime errors to `graceful degradation' so successfully that the program (for fire control?!) continued operating more-or-less correctly despite numerous bugs. It seems to me that for the kinds of programs that I typically work on, that this would require more than exceptions (perhaps divine intervention?) but I can imagine that it would be possible for a program which was conceptually like a bunch of closed-loop feedback systems, without a lot of state. Therefore, it's my guess that the program in question was such a program. Is that a sensible guess? If not, i.e. if this kind of performance would be possible in a program with lots of state, can someone give me a pointer to the theory of this kind of thing? (And are there any examples of such fault-tolerant programs available on the net?) (Examples of programs with lots of state include computer operating systems like UNIX (or Emacs:-), or perhaps an air-traffic control system which needs to base its output on data up to twenty hours old regarding flight departure times, radar and weather reports, etc. An example of a system without much state would be a translation of a system of analog radar-guided AA gun controllers directly into software.) Bill Newman wnewman@netcom