19

I'm writing a large project for iOS in Objective-C++. I'm mainly using Objective-C for the UI and other Apple APIs, and C++ for internal audio processing and other information handling. I was wondering about the drawbacks of mixing Objective-C and C++ freely.

Of course, mixing two object models has its inherent limitations and potential for messiness and confusion. I'm more curious about how using Objective-C++ will affect the compilation process, syntactic pitfalls I might run into, problems with readability and how I might avoid those, etc. I'm interested to hear what your experiences with Objective-C++ have been like and tips you might have for approaching this.

Luke
  • 7,110
  • 6
  • 45
  • 74
  • At forefront of my mind is that if you factor a library out as C++ you'll have an easier time porting it out elsewhere (for an Android app, etc.) But this question is too broad. "Common errors made by Objective C programmers trying to program in C++" and "Common errors made by C++ programmers trying to program in Objective C" would probably each be too broad to make good questions, and those are only one bit of what you're asking about!!! Perhaps better to research some of those issues individually and aggregate them in a blog, then fill in gaps with more specific questions asked here... – HostileFork says dont trust SE Nov 08 '11 at 19:21
  • having used objc++ for many years - i think this is a good question. voting to reopen. it doesn't really relate much to the suggested alternatives (assuming the OP knows both langs). – justin Nov 08 '11 at 19:29
  • 1
    @Justin While empathetic to the idea that you might like to read articles written on this topic, consider something here like: "I am a Python programmer. What kind of pitfalls might I run into writing C code?" Certainly interesting to a target audience--I'd agree. But would be a poor fit for StackOverflow's format, and the OP's question is broader and invites more discussion. How many essays would be written here, what points of view, who gets their "answer" checked? – HostileFork says dont trust SE Nov 08 '11 at 19:38
  • 1
    For instance, one of many possible "good" answers would have to be at least as long as this following article (which again, is more narrow than the question but still covered by it): http://www.philjordan.eu/article/strategies-for-using-c++-in-objective-c-projects – HostileFork says dont trust SE Nov 08 '11 at 19:44
  • 1
    @HostileFork Actually, I'd have written an answer. It's a good question because resources on the subject are very scarce. Objective-C++ is merely a language *dialect* supported by Apple's GCC and now clang (not sure if GCC mainline supports it well to date). It allows you to use both C++ and ObjC in the same translation. Based on the question, one can assume the OP knows both languages. – justin Nov 08 '11 at 19:45
  • 2
    @Justin If scarcity of information on a topic makes something a good question (and thereby wide availability of information a poor one) then I suggest you take it up with the jquery tag. Anyway...you can blog on the question and link to it via comment, or go on http://meta.stackoverflow.com/ and debate with powers-that-be why they've decided phrases like "I'm interested to hear what your experiences with Objective-C++ have been like" are cues that it's probably the kind of conversation that should happen outside of SO...even if they are interesting conversations (as this would probably be) – HostileFork says dont trust SE Nov 08 '11 at 19:50
  • 3
    How did this get unclosed? There is clearly no objective (ha!) answer to this question. It's an invitation to discussion, and it is phrased that way. And the only reason I haven't voted to close is because I already did that. – Nicol Bolas Nov 08 '11 at 20:16
  • 1
    HostileFork: If scarcity of information on a topic makes something a good question that doesn't mean that wide availability of information on a different topic makes a poor question. This is a classic example of premature implication... I'm not saying I agree with Justin, I'm just saying that your reasoning is incorrect. – Lajos Arpad Nov 08 '11 at 20:16
  • 1
    The disadvantage of using Objective-C++ is that you have to use Objective-C++ – M. Ryan Nov 08 '11 at 20:25
  • 1
    @Nicol It got unclosed because users voted for it to be reopened. – justin Nov 08 '11 at 20:26
  • 5
    I understand people's criticisms but I think Justin's answer shows that the question can be productive. I'm looking for specific information like that rather than waffly, opinionated "I think such and such about Objective-C++" answers, although I can see how this question could invite the latter. – Luke Nov 08 '11 at 20:37
  • @LajosArpad I was just making a joke at jquery's expense. My point is that I think a lot of cool stuff... like "what's your favorite C++ joke" or "what's a good programming comic" have been deemed to not fit the format, no matter how much people have enjoyed them in the past, and precedent these days is to close 'em even if they could be crowdpleasers: http://stackoverflow.com/questions/184618/what-is-the-best-comment-in-source-code-you-have-ever-encountered – HostileFork says dont trust SE Nov 08 '11 at 21:26

1 Answers1

48

ObjC++ is extremely powerful - you can select and mix the features you need for your problems and interface with C, ObjC, and C++ all at the same time. I've been using it for many years. There are of course, a few caveats, and it's good to be aware of them so you can minimize the issues you might encounter:

Compilation

The compilation times are much higher than ObjC or C++ when you begin creating nontrivial programs.

There are a few common approaches to declaring your C++ types in ObjC types:

  • Opaque types
  • Forward Declarations
  • Forward Declarations with smart pointers
  • By value

I'll just gloss over this, as it is inferred from the OP that you are familiar with both languages. As well, this is one of the more publicly written about introductory topics on ObjC++.

Given the C++ type:

class t_thing { public: int a; };

You have a number of ways to declare your ivars:

Opaque type:

@interface MONClass : NSObject { void* thing; } @end

This should be avoided. It's not good to erase type safety. The two forward options will introduce type safety.

This variant is compatible with ObjC translations.

Forward Declaration:

class t_thing;
@interface MONClass : NSObject { t_thing* thing; } @end

This is better than an opaque type, but the smart pointer is even better - pretty obvious if you are used to writing modern C++.

This variant is compatible with ObjC translations as long as your C++ types are in the global namespace.

Forward Declaration using smart pointers:

class t_thing;
@interface MONClass : NSObject { t_smart_pointer<t_thing> thing; } @end

This one is the best if you intend to set up translation firewalls (e.g. use PIMPL and forwards to reduce dependencies). As well, the ObjC object is already going through locking and allocations, so it's not a bad point to allocate a C++ type. If you have several declarations, you may prefer to create a wrapper type for your implementation to reduce individual allocations.

This variant is not compatible with ObjC translations.

This is a good time to remind you that there is a compiler option with ObjC++ that you should enable: GCC_OBJC_CALL_CXX_CDTORS. What happens when this flag is set? The compiler produces hidden objc methods which invoke your C++ ivars' constructors and destructors. If you use GCC_OBJC_CALL_CXX_CDTORS your C++ ivars must be default constructible. If you do not enable this flag, you must manually construct and destruct your ivars perfectly - if you construct it twice or do not override an initializer of the subclass, then you are facing UB.

By value:

#include "thing.hpp"    
@interface MONClass : NSObject { t_thing thing; } @end

Highest dependency. This is (in general) the route I chose, and I have some regrets about that. I've just moved things over to use more C++ and use composition with smart pointers (outlined above) to reduce dependency.

This variant is not compatible with ObjC translations.

One other thing about the modern ObjC compilers: The compiler lays out your C++ types' ivars/structure in the binary. Believe it or not, this can consume a lot of binary space.

The point here is that there are multiple forms the program can take. You can mix these techniques to reduce dependency, and this is one of the best places to introduce dependency firewalls because ObjC is very dynamic (its methods must be exported in one translation), and object creation requires allocations, locks, introduction into the reference counting system - initialization time for a single object is already relatively high, and the implementation will always be hidden.

If much of your program is still in ObjC and you want to keep it that way, then you will need to resort to forwards of types which are declared in the global namespace or opaque base types which you vend specializations through an object factory. Personally, I just use so much C++ that this was not an ideal option, and wrapping implementations in global types quickly became tiresome.

Meanwhile, since compilation times are high, the inverse is true: If you can keep significant portions of your implementation as C++, then you will save a lot of compilation time. For this reason and ARC (below) you can gain a lot by keeping your primitive Apple types as CF types where possible, so you can continue building C++ programs without the ObjC extensions.

Syntax

I rarely have problems but I keep my C++ classes quite strict:

  • I prohibit copy and assignment by default.
  • I rarely declare customized operators for C++ types.

If you're awesome at C++, then you could avoid this problem, but I prefer the compiler to catch silly mistakes I make.

One evident problem is C++ scope resolution within an ObjC message send. This requires a space:

[obj setValue:::func(a)]; // << bad
[obj setValue: ::func(a)]; // << good

Readability

One problem I have encountered is that I have never found a code formatter that supports the ObjC++ syntax well.

ObjC Messaging

  • ObjC Messaging and return by value: You need to check before messaging nil when returning C++ types by value. If the object you message is nil, then the result will be zeroed memory on the modern runtimes (x86_64 and iOS). If you use that instance, it is undefined behaviour.

  • ObjC Messaging and return by reference: You need to check before messaging nil when returning C++ types by reference. If the object you message is nil, then the result will be undefined behaviour (reference to 0/NULL).

To overcome the ObjC Messaging issues, I typically use a form like this:

- (bool)selector:(std::string&)outValue;

where the return value is false for some internal error, and true for success.

then you can safely write:

if (![obj selector:outString]) { /* bail here */ }

Miscellanea

  • ARC Compatibility: ObjC++ is not good for ARC. The primary reason is that ARC does not follow through mixed object models. Example: If you try to put a ObjC member into a C++ type, the compiler will reject the program under ARC. This is not really an issue because MRC is dead simple with ObjC++ (assuming you also use SBRM), but it may be a concern for the lifetime of your program.

  • Synthesized Properties: You will have to define your properties for C++ types.

  • External Tools: Beyond Xcode's toolset, there are few programs that handle or recognize ObjC++ well. Text editors, IDEs, utilities.

  • Apple's Tools: Within Xcode's utilities, Xcode's support for ObjC++ is a bit low. Refactoring (unavailable), navigation (improved with clang parser), outlining (is rather primitive), ObjC++ can disrupt IB's utilities, project upgrading is often not supported.

justin
  • 104,054
  • 14
  • 179
  • 226