Можно ли отделить основную функцию и классы C ++ от процедур Objective-C и / или C при компиляции и компоновке? - PullRequest
6 голосов
/ 09 февраля 2010

У меня есть небольшое приложение C ++, которое я импортировал классы Objective-C. Он работает как файлы Objective-C ++, .mm, но любой файл C ++, содержащий заголовок, который может в конечном итоге содержать заголовок Objective-C, должен быть переименован в расширение .mm для соответствующих драйверов GCC.

Есть ли способ написать чисто обертку C ++ для классов Objective-C или я могу как-то отделить объекты Objective-C и просто связать их отдельно? Может быть, даже если бы классы Objective-C стали маленькой библиотекой, я мог бы статически пересвязать во время компиляции?

Проблема заключается в том, что этот код является кроссплатформенным, и его сложнее компилировать в системах, которые обычно не используют Objective-C (т.е. не Mac). Несмотря на то, что команды препроцессора ограничивают любую реализацию кода Objective C в Windows или Linux, исходный код все еще имеет расширения .mm, и GCC все еще обрабатывает код как Objective C ++.

Ответы [ 2 ]

3 голосов
/ 09 февраля 2010

Обычно вы просто заключаете классы Objective-C в классы C ++, например, используя непрозрачные указатели и перенаправляя вызовы методов C ++ в методы Objective-C.

Таким образом, ваши переносимые исходники C ++ никогда не должны видеть какие-либо Objective-C, и в идеале вам нужно только поменять файлы реализации для оболочек на разных платформах.

Пример:

// c++ header:
class Wrapper {
    struct Opaque;
    Opaque* opaque;
    // ...
public:
    void f();
};

// Objective-C++ source on Mac:
struct Wrapper::Opaque {
    id contained;
    // ...
};

void Wrapper::f() {
    [opaque->contained f];
}

// ...
2 голосов
/ 15 февраля 2010

Да, это легко возможно в обоих направлениях, если вы знаете несколько приемов:

1) Тип "id" фактически определен в простом C-заголовке. Таким образом, вы можете сделать следующее:

В вашем заголовке:

#include <objc/objc.h>

class MyWindow
{
public:
    MyWindow();
    ~MyWindow();
protected:
    id        mCocoaWindow;
};

В вашей реализации (.mm):

#include "MyWindow.h"
#include <Cocoa/Cocoa.h>

MyWindow::MyWindow()
{
    mCocoaWindow = [[NSWindow alloc] init];
}

MyWindow::~MyWindow()
{
    [mCocoaWindow release];
    mCocoaWindow = nil;
}

2) Существуют две константы препроцессора, которые можно использовать для исключения кода, специфичного для C ++ / ObjC, когда исходный файл включает их один из двух, но не ObjC ++:

#if __OBJC__
// ObjC code goes here.
#endif /* __OBJC__*/

#if __cplusplus
// C++ code goes here.
#endif

Только будьте осторожны, вы не можете просто добавить / удалить ivars или виртуальные методы с помощью #ifdef, который создаст два класса с разным расположением памяти и приведет к аварийному завершению работы вашего приложения.

3) Вы можете использовать указатель на структуру без объявления ее содержимого:

В вашем заголовке:

@interface MyCppObjectWrapper : NSObject
{
    struct MyCppObjectWrapperIVars    *ivars;    // This is straight ObjC, no ++.
}

@end

В вашем файле реализации (.mm):

struct MyCppObjectWrapperIVars
{
    std::string    myCppString1;
    std::string    myCppString2;
    std::string    myCppString3;
};

@implementation MyCppObjectWrapper

-(id)  init
{
    if(( self = [super init] ))
    {
        ivars = new MyCppObjectWrapperIVars;
    }

    return self;
}

-(void) dealloc
{
    delete ivars;
    ivars = NULL;

    [super dealloc];
}

@end

Это сделает ваш заголовок простым стандартным C или ObjC, в то время как ваш файл реализации получает конструкторы / деструкторы всех ivars, вызываемых без необходимости создавать / удалять каждый из них как объект в куче.

Это все только сторона Mac, но это будет означать, что вы можете не допустить, чтобы ObjC-содержимое не попало в ваши заголовки, или, по крайней мере, заставить его компилироваться при использовании из межплатформенных клиентских файлов реализации Mac вашего C ++. слой переносимости.

...