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

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

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

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

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

Вы можете смешать C ++ с 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 что и class Objective-C, но файл, в котором он существует, должен быть скомпилирован как код Objective-C . Заголовок, объявляющий функцию обертки, должен быть включен как в код CPP, так и в Objective-C.

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


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

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

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

Задайте параметры проекта или цели для компиляции источников в 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 ++.

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

Шаг 1

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

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

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

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

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

Шаг 2

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

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

 #ifndef __test2__ObjCCall__ #define __test2__ObjCCall__ #include  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__) */ 

// Соответствующий объектный файл C ++ (мы называем его «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 ++ (которая на самом деле называет метод цели c)

 #ifndef __HELLOWORLD_SCENE_H__ #define __HELLOWORLD_SCENE_H__ #include "cocos2d.h" #include "ObjCCall.h" class HelloWorld : public cocos2d::Layer { public: // there's no 'id' in cpp, so we recommend returning the class instance pointer static cocos2d::Scene* createScene(); // Here's 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's 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's 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 } 

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

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

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

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

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

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

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

@ Ответ DawidDrozd выше отличный.

Я бы добавил один пункт. Недавние версии компилятора Clang жалуются на необходимость «моста» при попытке использовать его код.

Это кажется разумным: использование батута создает потенциальную ошибку: поскольку classы Objective-C подсчитываются по ссылке, если мы передаем их адрес вокруг как void *, мы рискуем иметь указатель на висячие строки, если class является сборкой мусора, в то время как обратный вызов по-прежнему активный.

Решение 1) Какао предоставляет CFBridgingRetain и CFBridgingRelease макрофункции, которые предположительно добавляют и вычитают один из счетчика ссылок объекта Objective-C. Поэтому мы должны быть осторожны с несколькими обратными вызовами, чтобы выпускать столько же раз, сколько мы сохраняем.

 // C++ Module #include  void cppFnRequiringCallback(std::function callback) { callback(); } //Objective-C Module #import "CppFnRequiringCallback.h" @interface MyObj : NSObject - (void) callCppFunction; - (void) myCallbackFn; @end void cppTrampoline(const void *caller) { id callerObjC = CFBridgingRelease(caller); [callerObjC myCallbackFn]; } @implementation MyObj - (void) callCppFunction { auto callback = [self]() { const void *caller = CFBridgingRetain(self); cppTrampoline(caller); }; cppFnRequiringCallback(callback); } - (void) myCallbackFn { NSLog(@"Received callback."); } @end 

Решение 2) Альтернативой является использование эквивалента слабого задания (т. Е. Никакого изменения количества удержания) без дополнительной безопасности.

Язык Objective-C предоставляет classификатор листинга __bridge для этого (CFBridgingRetain и CFBridgingRelease кажутся тонкой оболочкой cocoa над конструкциями языка Objective-C __bridge_retained и release соответственно, но Cocoa, похоже, не имеет эквивалента для __bridge).

Необходимые изменения:

 void cppTrampoline(void *caller) { id callerObjC = (__bridge id)caller; [callerObjC myCallbackFn]; } - (void) callCppFunction { auto callback = [self]() { void *caller = (__bridge void *)self; cppTrampoline(caller); }; cppFunctionRequiringCallback(callback); } 

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

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

  • Пробел над пользовательским представлением NSMenuItem
  • Как сравнить две даты в Objective-C
  • Как использовать C ++ с Objective-C в XCode
  • Как называется эта форма определения?
  • Как получить столбец datetime в SQLite с Objective C
  • Почему последняя часть имени объекта Objective-C принимает аргумент (когда имеется более одной части)?
  • GCD для выполнения задачи в основной теме
  • Как вы планируете запуск блока на следующей итерации цикла запуска?
  • Добавление объекта C ++ в Objective-C Class
  • POST multipart / form-data с Objective-C
  • Преимущества и недостатки #define против констант?
  • Interesting Posts

    Отображение обоев, когда рабочий стол заблокирован в Windows 7

    Запланируйте работу в определенное время с помощью WorkManager

    Внедрение ресурсов в исполняемый файл с использованием GCC

    Ссылка на несколько полей и элементов из одной ячейки – GETPIVOTDATA

    Последовательность изображений для видеоstreamа?

    Слабая переменная NSString не равна нулю после установки единственной сильной ссылки на nil

    Должны использоваться бесполезные classификаторы типов по типам возврата для ясности?

    Ошибка возврата значения JSON

    Ящик Android Navigation над вкладками

    Если вы реализуете IDisposable.Dispose (), чтобы он никогда не выбрасывал?

    Есть ли (бесплатный) способ настройки удаленного доступа к рабочему столу без настройки маршрутизатора?

    Вложенные грани в группах ggplot2

    Существуют ли программы Windows для изменения размеров разделов?

    Перезагрузка после сбоя питания для Mac mini под управлением Ubuntu

    Связывание в WPF с элементом массива, заданным свойством

    Давайте будем гением компьютера.