tutorial objective introduccion historia español caracteristicas c++ objective-c

introduccion - ¿Llamando al método Objective-C desde el método C++?



xcode manual pdf (8)

Tengo una clase ( EAGLView ) que llama a un método de clase C++ sin problemas. Ahora, el problema es que necesito llamar a esa clase C++ una función objective-C [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer]; que no puedo hacer en sintaxis C++ .

Podría envolver esta llamada de Objective-C a la misma clase de Objective-C , que en primer lugar llamó a la clase de C ++, pero luego necesito llamar de alguna manera a ese método desde C++ , y no puedo encontrar la manera de hacerlo.

Intenté dar un puntero al objeto EAGLView para el método C ++ e incluir el " EAGLView.h " en mi encabezado de clase C++ , pero obtuve 3999 errores.

Entonces ... ¿cómo debería hacer esto? Un ejemplo sería bueno. Solo encontré ejemplos puros de C de hacer esto.


A veces, renombrar .cpp a .mm no es una buena idea, especialmente cuando el proyecto es una plataforma cruzada. En este caso para el proyecto xcode, abro el archivo de proyecto xcode a través de TextEdit, cadena encontrada que contiene el archivo de interés, debe ser como:

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

y luego cambie el tipo de archivo de sourcecode.cpp.cpp a sourcecode.cpp.objcpp

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

Es equivalente a cambiar el nombre de .cpp a .mm


Además, puede llamar al tiempo de ejecución de Objective-C para llamar al método.


La solución más sencilla es simplemente decirle a Xcode que compile todo como Objective C ++.

Establezca la configuración de su proyecto o destino para compilar las fuentes en cuanto a Objectivo C ++ y vuelva a compilar.

Luego puede usar C ++ o Objective C en todas partes, por ejemplo:

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

Esto tiene el mismo efecto que cambiar el nombre de todos sus archivos fuente de .cpp o .m a .mm.

Hay dos desventajas menores en esto: clang no puede analizar el código fuente de C ++; un código C relativamente extraño no compila bajo C ++.


Necesita hacer que su archivo C ++ se trate como Objective-C ++. Puede hacer esto en xcode cambiando el nombre de foo.cpp a foo.mm (.mm es la extensión obj-c ++). Luego, como otros han dicho, la sintaxis estándar de obj-c funcionará.


Puede compilar su código como Objective-C ++: la forma más simple es cambiarle el nombre a .cpp como .mm. Luego compilará correctamente si incluye EAGLView.h (estaba recibiendo tantos errores porque el compilador de C ++ no entendía ninguna de las palabras clave específicas de Objective-C), y puede (en su mayor parte) mezclar Objective-C y C ++ como quieras.


Puede mezclar C ++ con Objectiv-C (Objective C ++). Escriba un método C ++ en su clase Objective C ++ que simplemente llame a [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer]; y llámalo desde tu C ++.

No lo he intentado antes que a mí mismo, pero pruébalo y comparte los resultados con nosotros.


Puedes mezclar C ++ con Objective-C si lo haces con cuidado. Hay algunas advertencias, pero en general se pueden mezclar. Si desea mantenerlos separados, puede configurar una función contenedora C estándar que le otorgue al objeto Objective-C una interfaz usable de estilo C a partir del código Objective-C (elija mejores nombres para sus archivos, he elegido estos nombres para verbosidad):

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

La función contenedora no necesita estar en el mismo archivo .m que la clase Objective-C, pero el archivo en el que existe debe compilarse como código Objective-C . El encabezado que declara la función de envoltura debe incluirse tanto en el código CPP como en el código Objective-C.

(NOTA: si el archivo de implementación de Objective-C tiene la extensión ".m" no se vinculará bajo Xcode. La extensión ".mm" le dice a Xcode que espere una combinación de Objective-C y C ++, es decir, Objective-C ++. )

Puede implementar lo anterior de una manera orientada a objetos utilizando la expresión idiomática PIMPL . La implementación es solo ligeramente diferente. En resumen, coloca las funciones de contenedor (declaradas en "MyObject-C-Interface.h") dentro de una clase con un puntero de vacío (privado) a una instancia 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 los métodos de contenedor ya no requieren el puntero de vacío a una instancia de MyClass; ahora es un miembro privado de MyClassImpl. El método init se usa para instanciar una instancia de 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 se crea una instancia con una llamada a MyClassImpl :: init. Puede instanciar MyClass en el constructor de MyClassImpl, pero generalmente no es una buena idea. La instancia de MyClass se destruye del destructor de MyClassImpl. Al igual que con la implementación de estilo C, los métodos de envoltura simplemente difieren a los métodos respectivos de 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." ); } }

Ahora tiene acceso a las llamadas a MyClass a través de una implementación privada de MyClassImpl. Este enfoque puede ser ventajoso si estuviera desarrollando una aplicación portátil; simplemente podría cambiar la implementación de MyClass por una específica para la otra plataforma ... pero, sinceramente, si esta es una mejor implementación es más una cuestión de gusto y necesidades.


Paso 1

Cree un archivo objetivo c (archivo .m) y su correspondiente archivo de encabezado.

// Archivo de encabezado (lo llamamos "ObjCFunc.h")

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

// Archivo correspondiente al Objective C (Lo llamamos "ObjCFunc.m")

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

Paso 2

¡Ahora implementaremos una función c ++ para llamar a la función objetivo c que acabamos de crear! Así que para eso definiremos un archivo .mm y su archivo de cabecera correspondiente (el archivo ".mm" se usará aquí porque podremos usar la codificación de Objective C y C ++ en el archivo)

// Archivo de encabezado (lo llamamos "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__) */

// Archivo correspondiente de Objective C ++ (Lo llamamos "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 }

Paso 3

Llamar a la función c ++ (que en realidad llama al método objetivo 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__

// Llamada final

#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 esto funcione!