There’s a “discussion” on Usenet right now about the relative merits of Objective-C versus C++. I know better than to post to that sort of thread on Usenet, but apparently I don’t know better than to do it in my weblog.
The argument (which you can go and read yourself) boils down to “C++ can do everything Objective-C can” versus “sure it can, but not easily, usably or understandibly.” The usual argument comes down to object models: With Objective-C, you can send any object any message, and if it understands it, it will respond. C++ is restricted by its compile-time type system, so that even if you have arbitrary objects that all implement the member function foo
, there is no way to call that method on a set of them unless they all inherit from the same base class. Except that you can, as exemplified boost::any
and boost::variant
.
To me, here’s the point people are missing: Objective-C has one main model for programming, objects. C++ has two. It has its object system, which is very different than Objective-C’s. C++’s object model simply does not allow you do to the sort of messaging and introspection that Objective-C allows. But C++ has another, far more powerful, programming model: generics (aka templates). With a template, you can call foo
on any object that implements it, regardless of class hierarchy. just method signatures. That’s how boost::variant
works, behind the scenes. In fact, it’s even more powerful, since you can make use of any property that’s shared between two types, not just method names. Generic programming, when you make full use of it, provides object-like polymorphism and overloading without using objects. Note that the STL makes use of inheritance only for code reuse, and classes only for encapsulation. In an interview on the design of the STL, designer Alex Stepanov recommends against member functions, instead recommending that all methods be global.
Thinking about, I realize that well-designed class libraries for each language look completely different, object-wise. A template-based C++ class library (like the STL) makes heavy use of inheritance for code reuse, but does not use dynamic method dispatch (“virtual
“) for polymorphism, relying on templates instead. An Objective-C class library (e.g., Cocoa) uses objects for polymorphism, making heavy use of dynamic dispatch but without much subclassing; code isn’t inherited from one class to another, but classes delegate to one another to share code. This is true even (or especially) compared with non-template-based C++ class libraries. For example, in PowerPlant (a C++ app framework for the Mac), most classes are designed to be subclassed. Your app might inherit from LApplication and implement methods to customize it. Very few of Cocoa’s classes are ever subclassed, on the other hand; an app would implement a separate delegate class that responds to events or answers questions that NSApplication asks about how your app should behave.
Then there’s Java, which is stuck with the inefficiencies of Objective-C’s runtime object model (and lack of generics), but the rigidity of C++’s compile-time type system. When using a Java class library, you can’t simply inherit all the code you need as you might with C++, since Java only has single inheritance, but since it has strong type-checking, you can’t use Cocoa-style delgation either. Last time I did serious Java programming (it was several years ago), I remember having to create dozens of “connector” classes just to implement the interfaces that the APIs needed. Java 1.1’s big new feature was “inner classes”, which did nothing to solve the serious language problem, but at least made it so that you didn’t have to create a separate source file for each of these damned things. I hear that Java is getting “generics” support soon (if it hasn’t already), but my understanding is that it’s just templatized types, which mainly just means you can write type-safe code using off-the-shelf collections without casts. It doesn’t give you any of the expressive power of real generic programming (e.g., C++ templates). Java, under the hood, does use an Objective-C style object model, but you need to use the reflection APIs to get access to it, and they aren’t very “natural” (and weren’t added until Java 1.1, either!) In Objective-C, you can call a method on an object not known to implement it until runtime, simply by calling it.
Once again I’m not quite sure what my point is. I do know that C++ is a far more complicated language than Objective-C. Every time I look at code that makes heavy use of the C++ language, I find out more things that can be done with it. With enough application of template magic, C++ can in fact do almost any language trick, and it is compile-time checked and type-safe in situations where almost any other language would have given up even run-time type safety miles before. Years after I thought I knew everything in the language, C++ continues to impress me with the things it can do. I think there’s something really poetic about a language that’s deep enough that no one can ever fully understand it. On the other hand, when I need to get something done, I’m usually more interested in working code than poetry.
I work in a 4-million(ish)-line code-base, with far too much C++, especially with templates. From a practical standpoint, template support varies fairly widely between compilers, and C++ code linked with one compiler can’t always be used with another compiler. And once you’ve used ObjC, you learn how id and protocols really rock the world!! Plus, the self-documenting method names are a god-send when other developers don’t write good (if any) Doxygen comments. Sigh, now if only objc were more popular.
I assume they were talking about calling a member function by name on a set of arbitrary objects at *run-time* because it is trivial to achieve this at compile-time (though using templates for this may be easier than not). However, I’m new to Boost, so maybe I just don’t understand your citation of “any” or “variant”.
While Boost’s templates and macro magic is impressive, it’s also hard to follow without guidance (e.g. from their docs). “Template magic” is right, and I agree with your conclusion which implies that the complexity of templated C++ may reduce its usefulness. Of course, actually using the templates can be easy once you understand the magic.
Templates are useful, but they are only intended for compile time specialization, and, sadly, they do not extend well into dynamic runtime cases; instead, separate classes with virtual members must be created and explicit code must be written to map from the virtual functions to the templated object functions.
Pingback: NSLog();
C++ is a very powerful language. Templates, which I originally saw as a way to actually overcome its lack of an object model (both java and objectiev C classes derive from a generic common object, allowing them to be passed/contained etc in generic containers) became much much more with the concepts of metaprogramming, traits etc. It must be remembered that templates are really just a compiler trick, when code is built you generate typed instances of the template classes. It saves you from explicitely typing/overriding at development time by instead having you tag them with a so it can spit out a variation at compile time.
Unfortunately, while fun as an academic exercise in ‘cooool’ I have learned that, as useful as that tool is, you must be very careful. I imagine thats why STL contains its use of templates to such a degree. For me, the template killer was the inability of any debug tools I’ve had available to inspect or step through generic code at runtime. Perhaps there are tools out there that will resolve the typed instances of template classes and allow you to debug them, but I haven’t had any available to me and it started to become more of a detriment than a benefit. I wound up using them only in tightly-nit containers and small generic tools, but seldom anywhere in serious programming logic that I would undoubtably be visiting and stepping through.
I also began some time ago to lose touch with what exactly my C++ code was doing at the lowest levels. While this is not important to every developer (especially in the modern world of hoping some framework just magically does stuff for you, which is the fastest time to market), in some industries it is still important to prove your code.
All that said, I’m just getting my feet wet with objective C. What I like so far is that most things are explicitely stated, it seems very self-documenting in nature, and it is C and C++ friendly so that if there are performance issues in bottleneck code (I’m honestly not sure how objective C performs) I can always utilize those languages to perhaps generate a faster variation while still keeping the rest of things under control.
@Chad:
In 2006, what debug tools were you using that didn’t support templates?
Templates don’t obfuscate what is going on at the lowest levels. Like you said, they just add one level of indirection. Replace the template parameter with a concrete type in your mind and you’re back to non-template C++.
I don’t really know if you’re trying to suggest that proving your code is less possible in C++ than other languages? Or that it’s that much harder to understand? But I think lots of experienced developers would disagree with these sentiments.
I am new to C++, and am puzzled by what I am reading here. Alexei suggests that one can use templates instead of virtual functions to achieve polymorphism. Templates is a compile-time mechanism, not a run-time one. If I want run-time polymorphic behavior, I need to use “virtual”. So what gives here?
Almost all of us have heard the term object oriented programming, and most of us have used C++. C++ has a very diverse syntax, many language extensions, and hundreds of quirks.On the other hand, Objective-C is a blend of C and Smalltalk. Most of it is straight C, with embedded Smalltalk.
So, my advice is to use both as you need them. Use the flexibility of Objective-C for most of your work, and use the lightweight C++ objects as tools to help you.
Hmm, amature hour here, from some of the comments I see here developers, don’t really understand templates, and what they can do. For exampe if you wanted to create a class factory using templates, you would create a template called ClassFactory, the ClassFactory, would contain a String map of pointers to functions, each item in the map would point to a classes virtual Create Function. This allows you to instantiate classes from string names. That is a simple idiom, there are many more. Boost allows for string function calls, and all sorts of amazing expressions, plus you can do anything Objective-C can do with C++. However C++ loses some of it’s intuitive features if you go the STL route, and almost all of it’s portability, when you consider the spotty template support, of compilers.
Your’e correct that Objective-C doesn’t have explicit support generics, but it is possible to use Class objects to get this effect.
For example, it is possible to produce a very clean type of generic Array in Objective-C:
ONArray* array = [ONArray of: [ONString class]];
vs.
ONArray array = new ONArray ();
Objective-C renegades this language issue to an API issue. Arguably the pure Objective-C implementation of generics is clearer than This counter example. Being able to treat classes as values is very interesting. You can use them as a replacement for templates; XYZVector, you can do anything C++ can do with Objective-C, and its usually simpler.
Interesting post :).