Вызов метода Objective-C из метода С++?

У меня есть класс (EAGLView), который вызывает метод класса C++ без проблем. Теперь проблема в том, что мне нужно вызвать в C++ class a objective-C function [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer];, который я не могу сделать в синтаксисе C++.

Я мог бы связать этот вызов objective-C с тем же классом objective-C, который в первую очередь называется классом С++, но тогда мне нужно каким-то образом вызвать этот метод из C++, и я не могу понять, как это сделать он.

Я попытался указать указатель на объект EAGLView на метод С++ и включить "EAGLView.h" в заголовок класса C++, но я получил 3999 ошибок.

Итак, как мне это сделать? Пример был бы приятным. Я нашел только чистые C примеры этого.

Ответ 1

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

MyObject-С-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 и С++, то есть Objective-C ++).


Вы можете реализовать описанное выше в Object-Orientented образом, используя идиому PIMPL. Реализация немного отличается. Короче говоря, вы размещаете функции оболочки (объявленные в "MyObject-C-Interface.h" ) внутри класса с (private) указателем 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 на одну конкретную для другой платформы... но, честно говоря, будь то более эффективная реализация, это больше зависит от вкуса и потребностей.

Ответ 2

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

Ответ 3

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

Задайте параметры проекта или цели для компиляции источников Что касается Objective С++ и перекомпилируйте.

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

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

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

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

Ответ 4

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

Ответ 5

Шаг 1

Создайте объектный файл c (файл .m) и соответствующий заголовочный файл.

//Заголовочный файл (мы называем его "ObjCFunc.h" )

#ifndef test2_ObjCFunc_h
#define test2_ObjCFunc_h
@interface myClass :NSObject
-(void)hello:(int)num1;
@end
#endif

//Соответствующий объект Objective C (мы называем его ObjCFunc.m)

#import <Foundation/Foundation.h>
#include "ObjCFunc.h"
@implementation myClass
//Your objective c code here....
-(void)hello:(int)num1
{
NSLog(@"Hello!!!!!!");
}
@end

Шаг 2

Теперь мы будем реализовывать функцию С++ для вызова функции объектива c, которую мы только что создали! Итак, для этого мы определим файл .mm и соответствующий его заголовочный файл (здесь будет использоваться файл ". Mm", потому что мы сможем использовать как Objective C, так и С++-кодирование в файле)

//Заголовочный файл (мы называем его "ObjCCall.h" )

#ifndef __test2__ObjCCall__
#define __test2__ObjCCall__
#include <stdio.h>
class ObjCCall
{
public:
static void objectiveC_Call(); //We define a static method to call the function directly using the class_name
};
#endif /* defined(__test2__ObjCCall__) */

//Соответствующий объектный файл С++ (мы называем его "ObjCCall.mm" )

#include "ObjCCall.h"
#include "ObjCFunc.h"
void ObjCCall::objectiveC_Call()
{
//Objective C code calling.....
myClass *obj=[[myClass alloc]init]; //Allocating the new object for the objective C   class we created
[obj hello:(100)];   //Calling the function we defined
}

Шаг 3

Вызов функции С++ (которая на самом деле называет метод объектива c)

#ifndef __HELLOWORLD_SCENE_H__
#define __HELLOWORLD_SCENE_H__
#include "cocos2d.h"
#include "ObjCCall.h"
class HelloWorld : public cocos2d::Layer
{
public:
// there no 'id' in cpp, so we recommend returning the class instance pointer
static cocos2d::Scene* createScene();
// Here a difference. Method 'init' in cocos2d-x returns bool, instead of returning  'id' in cocos2d-iphone
virtual bool init();
// a selector callback
void menuCloseCallback(cocos2d::Ref* pSender);
void ObCCall();  //definition
// implement the "static create()" method manually
CREATE_FUNC(HelloWorld);
};
#endif // __HELLOWORLD_SCENE_H__

//Заключительный вызов

#include "HelloWorldScene.h"
#include "ObjCCall.h"
USING_NS_CC;
Scene* HelloWorld::createScene()
{
// 'scene' is an autorelease object
auto scene = Scene::create();
// 'layer' is an autorelease object
auto layer = HelloWorld::create();
// add layer as a child to scene
scene->addChild(layer);
// return the scene
return scene;
}
// on "init" you need to initialize your instance
bool HelloWorld::init()
{
//////////////////////////////
// 1. super init first
if ( !Layer::init() )
{
    return false;
}
Size visibleSize = Director::getInstance()->getVisibleSize();
Vec2 origin = Director::getInstance()->getVisibleOrigin();

/////////////////////////////
// 2. add a menu item with "X" image, which is clicked to quit the program
//    you may modify it.

// add a "close" icon to exit the progress. it an autorelease object
auto closeItem = MenuItemImage::create(
                                       "CloseNormal.png",
                                       "CloseSelected.png",
                                       CC_CALLBACK_1(HelloWorld::menuCloseCallback,  this));

closeItem->setPosition(Vec2(origin.x + visibleSize.width - closeItem->getContentSize().width/2 ,
                            origin.y + closeItem->getContentSize().height/2));

// create menu, it an autorelease object
auto menu = Menu::create(closeItem, NULL);
menu->setPosition(Vec2::ZERO);
this->addChild(menu, 1);

/////////////////////////////
// 3. add your codes below...

// add a label shows "Hello World"
// create and initialize a label

auto label = Label::createWithTTF("Hello World", "fonts/Marker Felt.ttf", 24);

// position the label on the center of the screen
label->setPosition(Vec2(origin.x + visibleSize.width/2,
                        origin.y + visibleSize.height - label- >getContentSize().height));
// add the label as a child to this layer
this->addChild(label, 1);
// add "HelloWorld" splash screen"
auto sprite = Sprite::create("HelloWorld.png");
// position the sprite on the center of the screen
sprite->setPosition(Vec2(visibleSize.width/2 + origin.x, visibleSize.height/2 +     origin.y));
// add the sprite as a child to this layer
this->addChild(sprite, 0);
this->ObCCall();   //first call
return true;
}
void HelloWorld::ObCCall()  //Definition
{
ObjCCall::objectiveC_Call();  //Final Call  
}
void HelloWorld::menuCloseCallback(Ref* pSender)
{
#if (CC_TARGET_PLATFORM == CC_PLATFORM_WP8) || (CC_TARGET_PLATFORM ==   CC_PLATFORM_WINRT)
MessageBox("You pressed the close button. Windows Store Apps do not implement a close    button.","Alert");
return;
#endif
Director::getInstance()->end();
#if (CC_TARGET_PLATFORM == CC_PLATFORM_IOS)
exit(0);
#endif
}

Надеюсь, что это сработает!

Ответ 6

Иногда переименование .cpp на .mm не очень хорошая идея, особенно когда проект кроссплатформен. В этом случае для проекта xcode я открываю файл проекта xcode через TextEdit, найденную строку, содержащую контент файл, должен выглядеть так:

/* OnlineManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OnlineManager.cpp; sourceTree = "<group>"; };

а затем измените тип файла с sourcecode.cpp.cpp на sourcecode.cpp.objcpp

/* OnlineManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = **sourcecode.cpp.objcpp**; path = OnlineManager.cpp; sourceTree = "<group>"; };

Это эквивалентно переименованию .cpp в .mm

Ответ 7

Кроме того, вы можете позвонить в среду Objective-C для вызова метода.

Ответ 8

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

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