clang - основной компилятор Apple, основан на LLVM.
http://ru.wikipedia.org/wiki/Clang
http://ru.wikipedia.org/wiki/Low_Level_Virtual_Machine
При использовании clang не нужно обеспечивать совместимость с GCC.
Например, есть файл CppObject.h
Попытаемся сделать обертку ObjcObject.h
Здесь мы имеем элементы типа C++ в Objective-C классе. Далее прийдется реализовать методы в ObjcObject.mm. Однако, когда вы импортируете #import "ObjcObject.h" из чистого Objective-C (.m) файла прямо или посредством другого header (.h) файла вы получите ошибки препроцессора и компиляции в ObjcObject.h и CppObject.h. Препроцессор просто делает замену текста поэтому директивы #include и #import эквивалентны рекурсивному копированию-и-вставке содержимого файла вместо директивы. Поэтому в этом случае получается такой код:
И следовательно получим ошибку компилятора:
Unknown type name 'class'; did you mean 'Class'?
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.- http://habrahabr.ru/post/76248/
- http://en.wikipedia.org/wiki/Opaque_pointer
- http://insidecpp.ru/patterns/pimpl_idiom/
- http://en.wikibooks.org/wiki/C%2B%2B_Programming/Idioms#Pointer_To_Implementation_.28pImpl.29
- http://marcmutz.wordpress.com/translated-articles/pimp-my-pimpl-%E2%80%94-reloaded/
- http://www.gotw.ca/gotw/028.htm
- http://www.gotw.ca/gotw/024.htm
- http://c2.com/cgi/wiki?PimplIdiom
- http://msdn.microsoft.com/ru-ru/library/hh438477.aspx
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 файл до:
Отсутствующие части перемещаются в class extension в файле реализации (ObjcObject.mm):
В качестве альтернативы, если нам не нужен interface extension чтобы объявлять дополнительные свойства и методы, то блок ivar также может быть в начале @implementation:
В любом случае мы делаем #import "ObjcObject.h" и используем ObjcObject как любой другой Objective-C класс. Экземпляр CppObject для wrapped ivar будет построен с помощью конструктора по умолчанию, когда будет размещаться (alloc) (не init) ObjcObject, деструктор будет вызван на dealloc. Это не всегда то что нужно, в частности если нет (public) default constructor, в этом случае код не получится скомпилировать.
При использованни C++ исключений, можно обернуть конструирование в try {...} catch {...} блок и обрабатывать любые ошибки. При явном конструировании, нужно также явно разрушать wrapped объект:
--
#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 }
--
- http://www.philjordan.eu/article/mixing-objective-c-c++-and-objective-c++
- http://cocoasamurai.blogspot.com/2012/08/cover-up-those-ivars.html