Mixing C++ and Objective-C

The Mac is in a unique position of having most of its relevant UI APIs be in a different language than the one used for its core code. Navigating boundaries between C++ and Objective-C can be tricky.

Use Objective-C++

Using Objective-C++ works well for Mac-only implementation files. If a file is named with a .mm extension, then it will be compiled as an Objective-C++ file. Within such a file usage of Objective-C and C++ can be intermixed.

If Objective-C++ works in the context needed, it is the preferred way to accomplish mixing of C++ and Objective-C.

Use the pimpl idiom

The pimpl idiom is a standard way to hide the implementation of a C++ class from its users, exposing nothing but an implementation pointer in the header file. Usually it is used for compatibility (e.g. hiding implementation details), but it's useful in Chromium for hiding the Objective-C implementation details in the .mm implementation file and removing them from the .h file which might need to be included in a different .cc implementation file and which thus cannot have any Objective-C in it, even in a private: block.

The standard boilerplate for doing this is named ObjCStorage.

In the header file, a nested struct is forward-declared for use by a std::unique_ptr:

class UtilityObjectMac {
  // ...
 private:
  struct ObjCStorage;
  std::unique_ptr<ObjCStorage> objc_storage_;
};

and in the implementation .mm file, have that nested class host all the Obj-C instance variables:

struct UtilityObjectMac::ObjCStorage {
  id appkit_token;
  NSWindow* window;
};

UtilityObjectMac::UtilityObjectMac()
    : objc_storage_(std::make_unique<ObjCStorage>()) {
  objc_storage_->appkit_token = [NSFramework registerTheThing: //...
  // ...

This moves all of the Objective-C code into an Objective-C++ file at the cost of a secondary allocation and indirection on use.

Double-declaration (dangerous)

If none of these techniques will work, a double-declaration can be used. An example can be seen in native_widget_types.h:

#ifdef __OBJC__
@class NSCursor;
@class NSEvent;
@class NSFont;
@class NSImage;
@class NSView;
@class NSWindow;
@class NSTextField;
#else
class NSCursor;
class NSEvent;
class NSFont;
class NSImage;
class NSView;
class NSWindow;
class NSTextField;
#endif  // __OBJC__

With this double-declaration, these types can be used from both C++ and Objective-C. However, the price that is paid is that this is a (mostly) benign violation of the ODR and thus should be avoided if possible.

Specifically, this can get dangerous with Objective-C ARC enabled, where a pointer to a type declared this way will be treated by C++ as a raw pointer while it will be treated by Objective-C as a smart pointer with retain/release semantics.

Because of Chromium's history as a non-ARC app, the approach of using double-declarations was found to be more acceptable of a tradeoff than it is nowadays, so there is a lot of double-declaration. Revising code to remove double-declaration improves the code; please do so when it makes sense.

Do not include <objc/objc.h>. It has all the pitfalls of double-declaration for the id type (note that even though it defines id as struct objc_object*, the Objective-C compiler does not see them as equivalent), but has the additional pitfall of defining away the ARC ownership annotations if not compiling with Objective-C ARC. The inclusion of it is therefore banned, as it causes conflicts if included in header files, and while C++ implementation files should not be involving themselves with Objective-C types anyway, Objective-C implementation files implicitly have it included through their inclusion of framework headers.