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,bd45e29f9dafca87 X-Google-Attributes: gid103376,public From: Mark Lundquist Subject: Ada vs. C/C++ (was re: bitwise something-or-other) Date: 2000/01/21 Message-ID: <388905E0.81FEFBC5@rational.com> X-Deja-AN: 575977918 Content-Transfer-Encoding: 7bit References: <3880D375.7E363123@hotmail.com> <38829638.0@news.pacifier.com> <3882FC1C.2BA8C959@hotmail.com> <85vmn2$ki1$1@nnrp1.deja.com> <38836CF2.AB738B8B@hotmail.com> To: Alexander Van Hecke Content-Type: text/plain; charset=us-ascii Organization: Rational Software Mime-Version: 1.0 Newsgroups: comp.lang.ada Date: 2000-01-21T00:00:00+00:00 List-Id: Alexander Van Hecke wrote: >> Ada makes creating and using abstractions easier than C (packages for >> encapsulation and information hiding; private types for information >> hiding). > > Would you agree that C++ is just as good in that as Ada. Well, I wouldn't agree! I find C++ a lot more awkward and primitive. > Don't forget that > ANY C++ code can be easily translated into C? I'm not sure what you mean. If you mean that a translation program can do it, so "it's as easy as invoking the translator", then your observation is true, but irrelevant. Right? (Just think -- a compiler transforms an HLL into machine code, just as cfront transforms C++ into C. What would you think if someone claimed this fact in support of an argument that coding an equivalent program in machine code is no harder than writing the high-level language program?) If, on the other hand, you mean that given any C++ construct (e.g. a class), it's just as easy to program the C equivalent, then I doubt you can really be serious! Have you ever actually looked at the C text produced by cfront? Can you really say you'd just as soon write that yourself? For instance, C++ has its VPTR/VTABLE crap, which hides your C callback stuff. Don't you think there's some benefit in not having to code that yourself? The point isn't that you *can't* do that in principle... no one is claiming that! The point is, who'd want to? C++, despite its flaws, was invented for a reason. And as much as I'd rather use Ada than C++, I'd still rather use C++ than C. This is the whole purpose of higher-level languages, to take programs that are in principle writable by someone with unlimited patience, unlimited time, and perfect memory and record-keeping, and make them actually writeable by real people! The C level of abstraction is OK for little things, but it doesn't scale up. You can write big things in C -- with a lot of effort and pain. C++ actually does give the programmer a higher view of programming than C, in certain areas. It's all about the level of abstraction you get to work in. > For that matter, everything else you mentioned (namespace control, (easy) > generics, exceptions and exception handlers, typing) is easily achieved in > C++ (and thus C). Your line of reasoning seems to be: 1) Ada and C++ are equivalent 2) C++ and C are equivalent, because you can translate C++ to C 3) Therefore, Ada and C are equivalent! ("Ada feature X is 'easily achieved' in C") You seemed to take it for granted that anyone should concede (1), but those who really know both Ada and C++ will be able to tell you all the ways in which that is so wrong! Ada is not C++ with better syntax. As for (2), as I discussed above, the argument from translatability is specious. The translation in question is not transliteration, it's expressing higher-level abstractions in terms of lower-level ones. When you move up and down the "abstraction level continuum", you don't get to use the "translatability" argument, because you're trying to apply it with regard to expressive power when it's only relevant to operational equivalence, which is already assumed... Am I makin' sense here? > As I have said already a few times, and as I said in my original post : I > THINK IT'S A NICE FEATURE OF ADA THAT YOU CAN WRITE READABLE CODE, BUT > THAT > DOES NOT NECESSARILY MEAN THAT THE LANGUAGE IS MORE POWERFUL! Once again, nobody is saying that there are programs that you can write in Ada that you can't write in some other language. If this is what you're arguing against, it's a straw-man argument. The whole Turing-equivalence thing is kind of something that Everybody Knows -- I think you can safely assume that when someone claims that Ada is "more powerful" in some way, this is not what they mean! Yet that appears to be, from your "translation" argument, just what you are arguing against... The 'readability' thing always sounds like a superficial, syntax-level kind of concern ("You say, 'tomato', I say..."). And certainly there's an aspect of that, from the relatively inconsequential, like curly braces vs. "begin...end", to stuff that makes more of a difference. While human-engineering at the syntax level is important, I don't think anyone is going to say that getting it right results in huge gains in productivity/reliability/whatever. But the differences between Ada and C/C++ run much deeper than this. When an Ada advocate says things like "Ada is designed to be readable", they are using the term "readable" in a way that doesn't really do justice to the concept. What they actually mean is that the source text lends itself to a level of deep understanding of the programmer's intent. For instance, the "contract model" can result in a lot more information for the programmer who is reading the declaration of a generic entity, when compared to a C++ template declaration -- in the template "equivalent", the information would have to be given in comments (and in that case, the compiler can't guarantee its veracity), but is more likely going to be ignored by the writer of the template. In the C++ world, it's going to an extreme to document that sort of thing. For the "client" programmer to find out the hard way when they try to compile an instantiation of the template, is just considered business as usual. That's just one example, but the theme of making it easy for the programmer to express design intent, not just operation, runs all the way through the design of the language. > It might > be > easier to use once you've mastered it, but it also is harder to learn. OK, a couple of points... 1) I assume we're talking about C here, not C++. I think C++ is a lot harder to learn than Ada, because while it's roughly the same in "size", it has a lot of deep concepts (like "const correctness", linkage, etc.) that are not only arcane, but are hightly cross-coupled, so that you kind of have to understand all of it and keep your brain wrapped around it all in order to really get any of it right. For this reason, I don't think knowing C gives one any real advantage when it comes to learning C++ vs. learning Ada. Instead, what it gives you is a false sense that "I'm already halfway there", which gives you the hope you need to keep trying to learn the screwy language! You're not really halfway there, but it helps to think you are... :-) 2) While Ada is larger than C, you don't have to learn the whole language in order to start using it., e.g. if you don't need tasking, you don't have to learn Ada tasking, etc. 3) Since C is smaller, it's easier to master all the basic elements of the language... but then so what? What has it gotten you? Now, in addition, you also have to learn or invent ad hoc techniques, as in your example of structs with callbacks. And not only do you have to master the technique intellectually, you then have to apply it correctly, and if you don't get it right, the compiler can't tell you, because it has no clue what you are up to. You're going to have to debug it. Debugging can be fun, but we usually have little control over the circumstances that make it fun or un-fun. And after you raise the level of language abstraction so that you're detecting more bugs at compile-time, there will still be plenty of honest run-time bugs left to go around, so you won't miss the ones you don't have to debug anymore! :-) > > Ada has > > packages, > > C was ment to be used modular and for reuse : put your code in separate .c > and .h files. > > > private types, > > C has that! > > > exceptions, > > you can program exceptions in C. I never said that C has all these things, > but you can program them, and there are masses of libraries available that > have just what you need. > > > generics, > > use structs and callback functions and you have perfect generic types! > > > tasks, > > threads > > true enumeration types, true arrays, > enumeration types in Ada are no different than they are in C. Just because > you have some fancy attributes (SUCC, PRED) doesn't mean that they are > different or more powerful! You can write functions that do exactly the > same, even more, these functions have already been written numerous times > and are available. > With true arrays, do you mean out of bound checking, etc? This can be done > with _proper_ programming in C! So, we have: In Ada In C ----- --- packages "header file / implementation file" convention private types ??? exceptions "you can program it", setjmp()/longjmp(), whatever generics structs & callbacks tasks threads Yet, the Ada advocate claimed advantages for Ada because it has the things in the first column. What could be the explanation for this? I see three possibilities: 1) The Ada advocate is not aware of the things in the second column. E.g., he doesn't know that you can declare things in a shared header file, and then supply the definitions of those things in a ".c" file. 2) The things in column 2 are equivalent to the things in column 1, or they are just as good or better, e.g. threads give you everything that Ada tasks do, and just as well, but the Ada advocate does not understand this. For some reason, he persists in his belief that the Ada version is better. He just doesn't get it. 3) The things in column 1 really are better than the things in column 2. I think (1) can be safely dismissed. You can probably assume, for example, that Ada people know about #include. Don't forget, a lot of Ada advocates are C++ experts. Believe me, those people understand #include. Deeply. In all its glory. Same with the example of tasking. I don't think that when people cite tasking as an advantage of Ada, they mean nothing more than "you can do concurrent programming in Ada". So it's gotta be either (2) or (3). Let me take on the package thing, just as an example. Let's say I have some C-style module, with the interface in "foo.h" and the implementation in "foo.c". OK, superficially, I have something analogous to an Ada package spec and body. But the thing is, "analogous" is as far as it goes. In important ways, they are not "equivalent"... For instance... it's legal for foo.c to be incomplete. It can even be empty. How does foo.c "know" that it's supposed to be the "implementation" of foo.h? It doesn't. It can't. Only the programmer knows. C++ fixes this a little bit, and in a not-very-satisfactory way, with its scopes (classes and namespaces). But then, if you are using C++ and you have a function declaration in a .h file that is at the global scope, then there's another problem, which is that thanks to overloading you can get the definition wrong (doesn't match the prototype) and it's quite legal. As far as C++ is concerned, you didn't get it "wrong" (because only you know what "right" is...), all you did was supply an additional, legal overloading at file scope within the '.c' file. For all these problems, you get to find out about it when you link the program -- but only if there is an actual unresolved reference (think about the implications of that for component development). This is an irritation for small, self-contained, one-person projects, but for large projects with multiple teams, a product integration cycle, etc., it's more than an irritation. In C, everything is made to depend upon the linker. This, along with textual inclusion (independent compilation), is kind of the sine qua non of C/C++. At the linker level, which is primary in the C conception of modularity, the fundamental unit is not the module, but the symbol. The information loss between what the compiler knows and what the linker can know is considerable. (In combination with C++'s template design, this results in some truly inscrutable link-time error messages!) In Ada, the package spec is like a contract that the body has to fulfill, and if it doesn't, it won't compile. How does the body of Foo know that it has anything to do with the spec of Foo? Simple, it says so right there: "package body Foo is..." That's not all... When you put stuff in a .h file, you are making it available for other things to #include. And when they do that, they are dumping the stuff in your .h file into their global scope, which is flat. There's no way to say "The Initialize() from foo.h" vs. "the Initialize() from bar.h" -- in fact, you can't have both and include both. The second one #included will result in a redeclaration error. So everyone has to use ad hoc naming conventions, like "FooInitialize()". So what's wrong with that, besides being hokey? Well, does your concept of a "module" really line up with the reality of a global scope that gets extended when you #include the next thing? Isn't the point of modularity supposed to have something to do with encapsulation? What place does "defensive naming" have in any theory of modules? Now, a C++ class defines a scope, so it gives you some namespace control. But a class is also a data type, so using one where all you want is namespace control is awkward. What was needed was a pure namespace that does not define a data type. This is why the C++ 'namespace' construct was invented. But C++ namespaces are not that great. For one thing, they are textually disjoint. Each file that declares namespace "foo" extends that namespace. Namespaces partition the global namespace, but that's it. They are not like Ada packages. There is no concept of separating interface and implementation, yet enforcing the relationship at compile-time. So while namespaces provide one of the elements of a true module, they are not modules! Also, neither namespaces nor the .h/.c convention give you vsibility control (as in Ada's private declarations). Only classes give you something like this. So C++ has three different mechanisms (header files, classes, and namespaces) that overlap each other to some degree, and none of which gives you what you should expect from a "module". They only approximate it in different ways. Not only that, but each #include of foo.h is subject to whatever #defines are in effect at the point of the inclusion, which are not necessarily the same at all the places where foo.h is #included. Well, that's about all I have for now... Respectfully, Mark Lundquist