Вызов метода Objective C от функции членства C++?

Разделение интересов является злом:)

Только отдельные проблемы, если у вас есть для этого веские основания. В противном случае не разделяйте их.

Я встречал слишком много случаев разделения только ради разделения. Вторая половина высказывания Дейкстры «Минимальное сцепление, максимальное сцепление» не должна быть забыта. :)

Рад обсудить это дальше.

108
задан Drew Dormann 3 August 2018 в 20:07
поделиться

5 ответов

Вы можете смешивать C ++ с Objective-C, если будете делать это осторожно. Есть несколько предостережений, но в целом их можно смешивать. Если вы хотите сохранить их отдельно, вы можете настроить стандартную функцию-оболочку C, которая дает объекту Objective-C удобный интерфейс в стиле C из кода, отличного от Objective-C (выберите более удачные имена для ваших файлов, я выбрал эти имена для многословности):

MyObject-C-Interface.h

#ifndef __MYOBJECT_C_INTERFACE_H__
#define __MYOBJECT_C_INTERFACE_H__

// This is the C "trampoline" function that will be used
// to invoke a specific Objective-C method FROM C++
int MyObjectDoSomethingWith (void *myObjectInstance, void *parameter);
#endif

MyObject.h

#import "MyObject-C-Interface.h"

// An Objective-C class that needs to be accessed from C++
@interface MyObject : NSObject
{
    int someVar;
}

// The Objective-C member function you want to call from C++
- (int) doSomethingWith:(void *) aParameter;
@end

MyObject.mm

#import "MyObject.h"

@implementation MyObject

// C "trampoline" function to invoke Objective-C method
int MyObjectDoSomethingWith (void *self, void *aParameter)
{
    // Call the Objective-C method using Objective-C syntax
    return [(id) self doSomethingWith:aParameter];
}

- (int) doSomethingWith:(void *) aParameter
{
    // The Objective-C function you wanted to call from C++.
    // do work here..
    return 21 ; // half of 42
}
@end

MyCPPClass.cpp

#include "MyCPPClass.h"
#include "MyObject-C-Interface.h"

int MyCPPClass::someMethod (void *objectiveCObject, void *aParameter)
{
    // To invoke an Objective-C method from C++, use
    // the C trampoline function
    return MyObjectDoSomethingWith (objectiveCObject, aParameter);
}

Функция оболочки не требует, чтобы был в тот же файл .m , что и класс Objective-C, но файл, который он существует в , должен быть скомпилирован как код Objective-C . Заголовок, объявляющий функцию-оболочку, должен быть включен как в код CPP, так и в код Objective-C.

(ПРИМЕЧАНИЕ: если файлу реализации Objective-C присвоено расширение «.m», он не будет ссылаться в Xcode. Расширение «.mm» сообщает Xcode ожидать комбинации Objective-C и C ++, то есть Objective-C ++.)


Вы можете реализовать описанное выше объектно-ориентированным способом, используя идиому PIMPL . Реализация немного отличается. Короче говоря, вы помещаете функции-оболочки (объявленные в «MyObject-C-Interface.h») внутри класса с (частным) указателем void на экземпляр MyClass.

MyObject-C-Interface.h (PIMPL)

#ifndef __MYOBJECT_C_INTERFACE_H__
#define __MYOBJECT_C_INTERFACE_H__

class MyClassImpl
{
public:
    MyClassImpl ( void );
    ~MyClassImpl( void );

    void init( void );
    int  doSomethingWith( void * aParameter );
    void logMyMessage( char * aCStr );

private:
    void * self;
};

#endif

Обратите внимание, что для методов оболочки больше не требуется указатель void на экземпляр MyClass; теперь он является частным членом MyClassImpl. Метод init используется для создания экземпляра MyClass:

MyObject.h (PIMPL)

#import "MyObject-C-Interface.h"

@interface MyObject : NSObject
{
    int someVar;
}

- (int)  doSomethingWith:(void *) aParameter;
- (void) logMyMessage:(char *) aCStr;

@end

MyObject. mm (PIMPL)

#import "MyObject.h"

@implementation MyObject

MyClassImpl::MyClassImpl( void )
    : self( NULL )
{   }

MyClassImpl::~MyClassImpl( void )
{
    [(id)self dealloc];
}

void MyClassImpl::init( void )
{    
    self = [[MyObject alloc] init];
}

int MyClassImpl::doSomethingWith( void *aParameter )
{
    return [(id)self doSomethingWith:aParameter];
}

void MyClassImpl::logMyMessage( char *aCStr )
{
    [(id)self doLogMessage:aCStr];
}

- (int) doSomethingWith:(void *) aParameter
{
    int result;

    // ... some code to calculate the result

    return result;
}

- (void) logMyMessage:(char *) aCStr
{
    NSLog( aCStr );
}

@end

Обратите внимание, что MyClass создается с помощью вызова MyClassImpl :: init. Вы можете создать экземпляр MyClass в конструкторе MyClassImpl, но обычно это не очень хорошая идея. Экземпляр MyClass уничтожен деструктором MyClassImpl. Как и в случае реализации в стиле C, методы оболочки просто подчиняются соответствующим методам MyClass.

MyCPPClass.h (PIMPL)

#ifndef __MYCPP_CLASS_H__
#define __MYCPP_CLASS_H__

class MyClassImpl;

class MyCPPClass
{
    enum { cANSWER_TO_LIFE_THE_UNIVERSE_AND_EVERYTHING = 42 };
public:
    MyCPPClass ( void );
    ~MyCPPClass( void );

    void init( void );
    void doSomethingWithMyClass( void );

private:
    MyClassImpl * _impl;
    int           _myValue;
};

#endif

MyCPPClass.cpp (PIMPL)

#include "MyCPPClass.h"
#include "MyObject-C-Interface.h"

MyCPPClass::MyCPPClass( void )
    : _impl ( NULL )
{   }

void MyCPPClass::init( void )
{
    _impl = new MyClassImpl();
}

MyCPPClass::~MyCPPClass( void )
{
    if ( _impl ) { delete _impl; _impl = NULL; }
}

void MyCPPClass::doSomethingWithMyClass( void )
{
    int result = _impl->doSomethingWith( _myValue );
    if ( result == cANSWER_TO_LIFE_THE_UNIVERSE_AND_EVERYTHING )
    {
        _impl->logMyMessage( "Hello, Arthur!" );
    }
    else
    {
        _impl->logMyMessage( "Don't worry." );
    }
}

Теперь вы получаете доступ вызывает MyClass через частную реализацию MyClassImpl. Этот подход может быть выгоден, если вы разрабатываете переносимое приложение; вы можете просто поменять реализацию MyClass на одну, предназначенную для другой платформы ... но, честно говоря, лучшая реализация - это скорее вопрос вкуса и потребностей.

Вы можете создать экземпляр MyClass в конструкторе MyClassImpl, но обычно это не очень хорошая идея. Экземпляр MyClass уничтожен деструктором MyClassImpl. Как и в случае реализации в стиле C, методы оболочки просто подчиняются соответствующим методам MyClass.

MyCPPClass.h (PIMPL)

#ifndef __MYCPP_CLASS_H__
#define __MYCPP_CLASS_H__

class MyClassImpl;

class MyCPPClass
{
    enum { cANSWER_TO_LIFE_THE_UNIVERSE_AND_EVERYTHING = 42 };
public:
    MyCPPClass ( void );
    ~MyCPPClass( void );

    void init( void );
    void doSomethingWithMyClass( void );

private:
    MyClassImpl * _impl;
    int           _myValue;
};

#endif

MyCPPClass.cpp (PIMPL)

#include "MyCPPClass.h"
#include "MyObject-C-Interface.h"

MyCPPClass::MyCPPClass( void )
    : _impl ( NULL )
{   }

void MyCPPClass::init( void )
{
    _impl = new MyClassImpl();
}

MyCPPClass::~MyCPPClass( void )
{
    if ( _impl ) { delete _impl; _impl = NULL; }
}

void MyCPPClass::doSomethingWithMyClass( void )
{
    int result = _impl->doSomethingWith( _myValue );
    if ( result == cANSWER_TO_LIFE_THE_UNIVERSE_AND_EVERYTHING )
    {
        _impl->logMyMessage( "Hello, Arthur!" );
    }
    else
    {
        _impl->logMyMessage( "Don't worry." );
    }
}

Теперь вы получаете доступ вызывает MyClass через частную реализацию MyClassImpl. Этот подход может быть выгоден, если вы разрабатываете переносимое приложение; вы можете просто поменять реализацию MyClass на одну, предназначенную для другой платформы ... но, честно говоря, лучшая реализация - это скорее вопрос вкуса и потребностей.

Вы можете создать экземпляр MyClass в конструкторе MyClassImpl, но обычно это не очень хорошая идея. Экземпляр MyClass уничтожен деструктором MyClassImpl. Как и в случае реализации в стиле C, методы оболочки просто подчиняются соответствующим методам MyClass.

MyCPPClass.h (PIMPL)

#ifndef __MYCPP_CLASS_H__
#define __MYCPP_CLASS_H__

class MyClassImpl;

class MyCPPClass
{
    enum { cANSWER_TO_LIFE_THE_UNIVERSE_AND_EVERYTHING = 42 };
public:
    MyCPPClass ( void );
    ~MyCPPClass( void );

    void init( void );
    void doSomethingWithMyClass( void );

private:
    MyClassImpl * _impl;
    int           _myValue;
};

#endif

MyCPPClass.cpp (PIMPL)

#include "MyCPPClass.h"
#include "MyObject-C-Interface.h"

MyCPPClass::MyCPPClass( void )
    : _impl ( NULL )
{   }

void MyCPPClass::init( void )
{
    _impl = new MyClassImpl();
}

MyCPPClass::~MyCPPClass( void )
{
    if ( _impl ) { delete _impl; _impl = NULL; }
}

void MyCPPClass::doSomethingWithMyClass( void )
{
    int result = _impl->doSomethingWith( _myValue );
    if ( result == cANSWER_TO_LIFE_THE_UNIVERSE_AND_EVERYTHING )
    {
        _impl->logMyMessage( "Hello, Arthur!" );
    }
    else
    {
        _impl->logMyMessage( "Don't worry." );
    }
}

Теперь вы получаете доступ вызывает MyClass через частную реализацию MyClassImpl. Этот подход может быть выгоден, если вы разрабатываете переносимое приложение; вы можете просто поменять реализацию MyClass на одну, предназначенную для другой платформы ... но, честно говоря, лучшая реализация - это скорее вопрос вкуса и потребностей.

деструктор. Как и в случае реализации в стиле C, методы оболочки просто подчиняются соответствующим методам MyClass.

MyCPPClass.h (PIMPL)

#ifndef __MYCPP_CLASS_H__
#define __MYCPP_CLASS_H__

class MyClassImpl;

class MyCPPClass
{
    enum { cANSWER_TO_LIFE_THE_UNIVERSE_AND_EVERYTHING = 42 };
public:
    MyCPPClass ( void );
    ~MyCPPClass( void );

    void init( void );
    void doSomethingWithMyClass( void );

private:
    MyClassImpl * _impl;
    int           _myValue;
};

#endif

MyCPPClass.cpp (PIMPL)

#include "MyCPPClass.h"
#include "MyObject-C-Interface.h"

MyCPPClass::MyCPPClass( void )
    : _impl ( NULL )
{   }

void MyCPPClass::init( void )
{
    _impl = new MyClassImpl();
}

MyCPPClass::~MyCPPClass( void )
{
    if ( _impl ) { delete _impl; _impl = NULL; }
}

void MyCPPClass::doSomethingWithMyClass( void )
{
    int result = _impl->doSomethingWith( _myValue );
    if ( result == cANSWER_TO_LIFE_THE_UNIVERSE_AND_EVERYTHING )
    {
        _impl->logMyMessage( "Hello, Arthur!" );
    }
    else
    {
        _impl->logMyMessage( "Don't worry." );
    }
}

Теперь вы получаете доступ вызывает MyClass через частную реализацию MyClassImpl. Этот подход может быть выгоден, если вы разрабатываете переносимое приложение; вы можете просто поменять реализацию MyClass на одну, предназначенную для другой платформы ... но, честно говоря, лучшая реализация - это скорее вопрос вкуса и потребностей.

деструктор. Как и в случае реализации в стиле C, методы оболочки просто подчиняются соответствующим методам MyClass.

MyCPPClass.h (PIMPL)

#ifndef __MYCPP_CLASS_H__
#define __MYCPP_CLASS_H__

class MyClassImpl;

class MyCPPClass
{
    enum { cANSWER_TO_LIFE_THE_UNIVERSE_AND_EVERYTHING = 42 };
public:
    MyCPPClass ( void );
    ~MyCPPClass( void );

    void init( void );
    void doSomethingWithMyClass( void );

private:
    MyClassImpl * _impl;
    int           _myValue;
};

#endif

MyCPPClass.cpp (PIMPL)

#include "MyCPPClass.h"
#include "MyObject-C-Interface.h"

MyCPPClass::MyCPPClass( void )
    : _impl ( NULL )
{   }

void MyCPPClass::init( void )
{
    _impl = new MyClassImpl();
}

MyCPPClass::~MyCPPClass( void )
{
    if ( _impl ) { delete _impl; _impl = NULL; }
}

void MyCPPClass::doSomethingWithMyClass( void )
{
    int result = _impl->doSomethingWith( _myValue );
    if ( result == cANSWER_TO_LIFE_THE_UNIVERSE_AND_EVERYTHING )
    {
        _impl->logMyMessage( "Hello, Arthur!" );
    }
    else
    {
        _impl->logMyMessage( "Don't worry." );
    }
}

Теперь вы получаете доступ вызывает MyClass через частную реализацию MyClassImpl. Этот подход может быть выгоден, если вы разрабатываете переносимое приложение; вы можете просто поменять реализацию MyClass на одну, предназначенную для другой платформы ... но, честно говоря, лучшая реализация - это скорее вопрос вкуса и потребностей.

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

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

197
ответ дан 24 November 2019 в 03:29
поделиться

Вы можете скомпилировать свой код как Objective-C ++ - самый простой способ - переименовать ваш .cpp в .mm. Затем он будет правильно скомпилирован, если вы включите EAGLView.h (вы получали так много ошибок, потому что компилятор C ++ не понимал ни одного из ключевых слов Objective-C), и вы можете (по большей части ) смешивайте Objective-C и C ++, как хотите.

15
ответ дан 24 November 2019 в 03:29
поделиться

Самое простое решение - просто указать Xcode скомпилировать все как Objective C ++.

Задайте настройки вашего проекта или цели для Compile Sources As to Objective C ++ и перекомпилируйте.

Затем вы можете используйте везде C ++ или Objective C, например:

void CPPObject::Function( ObjectiveCObject* context, NSView* view )
{
   [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)view.layer]
}

Это имеет тот же эффект, что и переименование всех ваших исходных файлов из .cpp или .m в .mm.

У этого есть два незначительных недостатка: clang не может анализировать исходный код C ++ код; какой-то относительно странный код на C не компилируется под C ++.

12
ответ дан 24 November 2019 в 03:29
поделиться

Вы можете смешивать C ++ с Objectiv-C (Objective C ++). Напишите метод C ++ в своем классе Objective C ++, который просто вызывает [context renderbufferStorage: GL_RENDERBUFFER fromDrawable: (CAEAGLLayer *) self.layer]; и вызывает его из вашего C ++.

Я не пробовал перед собой, но попробуй и поделись с нами результатами.

-1
ответ дан 24 November 2019 в 03:29
поделиться

Вам нужно сделать так, чтобы ваш файл C ++ обрабатывался как Objective-C ++. Вы можете сделать это в xcode, переименовав foo.cpp в foo.mm (.mm - это расширение obj-c ++). Тогда, как говорили другие, будет работать стандартный синтаксис обмена сообщениями obj-c.

9
ответ дан 24 November 2019 в 03:29
поделиться
Другие вопросы по тегам:

Похожие вопросы: