Как включить C++ библиотеку в Objective-C проект

clang - основной компилятор Apple, основан на LLVM.

http://ru.wikipedia.org/wiki/Clang
http://ru.wikipedia.org/wiki/Low_Level_Virtual_Machine

При использовании clang не нужно обеспечивать совместимость с GCC.

Суть проблемы

Можно целиком переключить проект на Objective-C++ путем переименования всех .m в .mm и свободно смешивать C++ и Objective-C++. Но такое "глубокое" смешивание может привести к жопе. Поэтому обычно С++ типы и функции оборачиваются в Objective-C эквиваленты, которые можно использовать в проекте.

Например, есть файл CppObject.h

#include <string>
class CppObject
{
public:
  void ExampleMethod(const std::string& str);
  // constructor, destructor, other members, etc.
};

Попытаемся сделать обертку ObjcObject.h

#import <Foundation/Foundation.h>
#import "CppObject.h"

@interface ObjcObject : NSObject {
  CppObject wrapped;
}
- (void)exampleMethodWithString:(NSString*)str;
// other wrapped methods and properties
@end

Здесь мы имеем элементы типа C++ в Objective-C классе. Далее прийдется реализовать методы в ObjcObject.mm. Однако, когда вы импортируете #import "ObjcObject.h" из чистого Objective-C (.m) файла прямо или посредством другого header (.h) файла вы получите ошибки препроцессора и компиляции в ObjcObject.h и CppObject.h. Препроцессор просто делает замену текста поэтому директивы #include и #import эквивалентны рекурсивному копированию-и-вставке содержимого файла вместо директивы. Поэтому в этом случае получается такой код:

// [lots and lots of Objective-C code from Foundation/Foundation.h]
// [fail to include <string>] as that header is not in the include path outside of C++ mode
class CppObject
{
public:
  void ExampleMethod(const std::string& str);
  // constructor, destructor, other members, etc.
};

@interface ObjcObject : NSObject {
  CppObject wrapped;
}
- (void)exampleMethodWithString:(NSString*)str;
// other wrapped methods and properties
@end


И следовательно получим ошибку компилятора:
Unknown type name 'class'; did you mean 'Class'?

Потому что в Objective-C нет такого ключевого слова class.

А что была совместимость с Objective-C, наш Objective-C++ заголовочный файл класса должен содержать только Objective-C код, абсолютно никакого C++.

Держите файлы заголовков в чистоте

Решение - PIMPL.

PIMPL является также решением для противоположной задачи - обертка Objective-C кода через C++. Однако в clang есть новый способ исключить C++ из Objective-C заголовков: ivars in class extensions.

Class extensions (не путайте с категориями) существуют в Objective-C уже давно: они позволяют объявлять дополнительные части интерфейса класса вне публичного заголовка перед блоком @implementation.

ObjcObject.mm:
#import "ObjcObject.h"
@interface ObjcObject () // note the empty parentheses
- (void)methodWeDontWantInTheHeaderFile;
@end
@implementation ObjcObject
// etc.

Это уже работает с GCC, но с clang, можно еще добавить блок ivar. Это означает, что мы можем объявлять любые экземпляры переменны типа C++ в extension-е, или вначале блока @implementation. В нашем случае, мы можем сократить ObjcObject.h файл до:

#import <Foundation/Foundation.h>

@interface ObjcObject : NSObject
- (void)exampleMethodWithString:(NSString*)str;
// other wrapped methods and properties
@end

Отсутствующие части перемещаются в class extension в файле реализации (ObjcObject.mm):

#import "ObjcObject.h"
#import "CppObject.h"
@interface ObjcObject () {
  CppObject wrapped;
}
@end

@implementation ObjcObject
- (void)exampleMethodWithString:(NSString*)str
{
  // NOTE: if str is nil this will produce an empty C++ string
  // instead of dereferencing the NULL pointer from UTF8String. 
  std::string cpp_str([str UTF8String], [str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
  wrapped.ExampleMethod(cpp_str);
}

В качестве альтернативы, если нам не нужен interface extension чтобы объявлять дополнительные свойства и методы, то блок ivar также может быть в начале @implementation:

#import "ObjcObject.h"
#import "CppObject.h"

@implementation ObjcObject {
  CppObject wrapped;
}

- (void)exampleMethodWithString:(NSString*)str
{
  // NOTE: if str is nil this will produce an empty C++ string
  // instead of dereferencing the NULL pointer from UTF8String. 
  std::string cpp_str([str UTF8String], [str lengthOfBytesUsingEncoding:NSUTF8StringEncoding]);
  wrapped.ExampleMethod(cpp_str);
}

В любом случае мы делаем #import "ObjcObject.h" и используем ObjcObject как любой другой Objective-C класс. Экземпляр CppObject для wrapped ivar будет построен с помощью конструктора по умолчанию, когда будет размещаться (alloc) (не init) ObjcObject, деструктор будет вызван на dealloc. Это не всегда то что нужно, в частности если нет (public) default constructor, в этом случае код не получится скомпилировать.

Управление жизненным циклом wrapped C++ объекта

Решение:

@interface ObjcObject () {
  CppObject* wrapped; // Pointer! Will be initialised to NULL by alloc.
}
@end
@implementation ObjcObject
- (id)initWithSize:(int)size
{
  self = [super init];
  if (self)
  {
    wrapped = new CppObject(size);
    if (!wrapped) self = nil;
  }
  return self;
}
//...

При использованни C++ исключений, можно обернуть конструирование в try {...} catch {...} блок и обрабатывать любые ошибки. При явном конструировании, нужно также явно разрушать wrapped объект:

- (void)dealloc
{
  delete wrapped;
  [super dealloc]; // omit if using ARC
}

--