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

У меня есть небольшое приложение на 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 ++.


person mike_b    schedule 09.02.2010    source источник


Ответы (2)


Обычно вы просто оборачиваете свои классы 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];
}

// ...
person Georg Fritzsche    schedule 09.02.2010
comment
так что тогда структура Wrapper :: Opaque будет включать 'OBJCClass * name = [[OBJCClass alloc] init]', а Wrapper :: f () вызовет [name f], если он будет таким образом связан? - person mike_b; 09.02.2010
comment
Да, вы можете выделить и инициализировать содержащийся класс Objective-C в конструкторе Wrapper или Opaque, в зависимости от того, что вам подходит, и методы класса C ++ будут перенаправлять вызовы в ваш класс Objective-C. - person Georg Fritzsche; 09.02.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, в то время как ваш файл реализации получит конструкторы / деструкторы всех вызываемых ivar без необходимости создавать / удалять каждый из них как объект в куче.

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

person uliwitness    schedule 14.02.2010