Chamando o método Objective-C da function de membro C ++?

Eu tenho uma class ( EAGLView ) que chama uma function de membro de uma class de C++ sem problemas. Agora, o problema é que preciso chamar essa class C++ uma function objective-C [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer]; que não posso fazer na syntax do C++ .

Eu poderia envolver essa chamada Objective-C para a mesma class Objective-C que em primeiro lugar chamado de class C ++, mas então eu preciso de alguma forma chamar esse método de C++ , e não consigo descobrir como fazê-lo.

Eu tentei dar um ponteiro para o object EAGLView para a function de membro C ++ e include o ” EAGLView.h ” no meu header de class C++ mas eu tenho 3999 erros ..

Então, como devo fazer isso? Um exemplo seria bom .. Eu só encontrei exemplos C puro de fazer isso.

Você pode misturar C ++ com Objective-C, se você fizer isso com cuidado. Existem algumas ressalvas, mas em geral elas podem ser misturadas. Se você quiser mantê-los separados, você pode configurar uma function de invólucro C padrão que fornece ao object Objective-C uma interface utilizável no estilo C a partir do código não-Objective-C (escolha nomes melhores para seus arquivos, eu escolhi esses nomes para verbosidade):

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); } 

A function de invólucro não precisa estar no mesmo arquivo .m da class Objective-C, mas o arquivo em que ela existe precisa ser compilado como código Objective-C . O header que declara a function wrapper precisa ser incluído no código CPP e Objective-C.

(NOTA: se o arquivo de implementação do Objective-C receber a extensão “.m”, ele não será vinculado ao Xcode. A extensão “.mm” diz ao Xcode para esperar uma combinação de Objective-C e C ++, ou seja, Objective-C ++. )


Você pode implementar o acima em uma maneira orientada a object usando o idioma PIMPL . A implementação é apenas ligeiramente diferente. Em suma, você coloca as funções do invólucro (declaradas em “MyObject-C-Interface.h”) dentro de uma class com um ponteiro void (privado) para uma instância de 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 

Observe que os methods de wrapper não exigem mais o ponteiro void para uma instância de MyClass; agora é um membro privado do MyClassImpl. O método init é usado para instanciar uma instância 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 

Observe que MyClass é instanciado com uma chamada para MyClassImpl :: init. Você poderia instanciar MyClass no construtor MyClassImpl, mas isso geralmente não é uma boa idéia. A instância MyClass é destruída do destruidor do MyClassImpl. Tal como acontece com a implementação de estilo C, os methods wrapper simplesmente se submetem aos methods respectivos do 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." ); } } 

Agora você acessa chamadas para MyClass através de uma implementação privada de MyClassImpl. Essa abordagem pode ser vantajosa se você estivesse desenvolvendo um aplicativo portátil; você poderia simplesmente trocar a implementação do MyClass por um específico para a outra plataforma … mas honestamente, se esta é uma implementação melhor é mais uma questão de gosto e necessidades.

Você pode compilar seu código como Objective-C ++ – a maneira mais simples é renomear seu .cpp como .mm. Ele irá então compilar corretamente se você include EAGLView.h (você estava recebendo tantos erros porque o compilador C ++ não entendia nenhuma das palavras-chave específicas do Objective-C), e você pode (na maior parte) misturar Objective-C e C ++ como quiser.

A solução mais fácil é simplesmente dizer ao Xcode para compilar tudo como Objective C ++.

Defina suas configurações de projeto ou destino para as fonts de compilation como para o Objective C ++ e recompile.

Então você pode usar C ++ ou Objective C em todos os lugares, por exemplo:

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

Isso tem o mesmo efeito que renomear todos os seus arquivos de origem de .cpp ou .m para .mm.

Há duas pequenas desvantagens: o clang não pode analisar o código fonte do C ++; algum código C relativamente estranho não compila em C ++.

Você precisa fazer com que seu arquivo C ++ seja tratado como Objective-C ++. Você pode fazer isso no xcode renomeando foo.cpp para foo.mm (.mm é a extensão obj-c ++). Então, como outros disseram, a syntax padrão de mensagens obj-c funcionará.

Passo 1

Crie um arquivo c objective (arquivo .m) e seu arquivo de header correspondente.

// Arquivo de header (chamamos de “ObjCFunc.h”)

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

// Arquivo C de Objectivo Correspondente (Chamamos de “ObjCFunc.m”)

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

Passo 2

Agora vamos implementar uma function c ++ para chamar a function object c que acabamos de criar! Então, para isso, vamos definir um arquivo .mm e seu arquivo de header correspondente (o arquivo “.mm” deve ser usado aqui, pois poderemos usar tanto a codificação Objective C quanto a C ++ no arquivo)

// Arquivo de header (chamamos de “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__) */ 

// Arquivo C ++ de Objeto Correspondente (Chamamos de “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 } 

etapa 3

Chamando a function c ++ (que na verdade chama o método c objective)

 #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__ 

//Última chamada

 #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 } 

Espero que isso funcione!

Às vezes, renomear .cpp para .mm não é uma boa ideia, especialmente quando o projeto é crossplatform. Neste caso, para o projeto xcode eu abro o arquivo de projeto xcode através do TextEdit, encontrei uma string cujo arquivo de interesse de conteúdo, deve ser como:

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

e, em seguida, altere o tipo de arquivo de sourcecode.cpp.cpp para sourcecode.cpp.objcpp

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

É equivalente a renomear .cpp para .mm

Além disso, você pode chamar o tempo de execução do Objective-C para chamar o método.

@ Resposta DawidDrozd acima é excelente.

Eu adicionaria um ponto. Versões recentes do compilador Clang queixam-se de exigir um “casting de pontes” se tentar usar seu código.

Isso parece razoável: usar um trampolim cria um bug em potencial: como as classs Objective-C são referenciadas, se passarmos o endereço delas como um void *, corremos o risco de ter um ponteiro pendente se a class for coletada enquanto o callback ainda estiver ativo.

Solução 1) O cacau fornece funções de macro CFBridgingRetain e CFBridgingRelease, que presumivelmente adicionam e subtraem uma da contagem de referência do object Objective-C. Devemos, portanto, ter cuidado com vários retornos de chamada, para liberar o mesmo número de vezes que retemos.

 // 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 

Solução 2) A alternativa é usar o equivalente a uma referência fraca (isto é, sem alterar a contagem de retenção), sem qualquer segurança adicional.

A linguagem Objective-C fornece o qualificador de conversão __bridge para fazer isso (CFBridgingRetain e CFBridgingRelease parecem ser thin Cocoa wrappers sobre as construções de linguagem Objective-C __bridge_retained e release respectivamente, mas Cocoa não parece ter um equivalente para __bridge).

As mudanças necessárias são:

 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); } 

Você pode misturar C ++ com Objectiv-C (Objective C ++). Escreva um método C ++ em sua class Objective C ++ que simplesmente chame [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer]; e chame do seu C ++.

Eu não tentei antes de tudo, mas tente, e compartilhe os resultados conosco.