Calling Objective-C method from C++ method?

匿名 (未验证) 提交于 2019-12-03 01:48:02

问题:

I have a class (EAGLView) which calls a method of a C++ class without problems. Now, the problem is that I need to call in that C++ class a objective-C function [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer]; which I cannot do in C++ syntax.

I could wrap this Objective-C call to the same Objective-C class which in the first place called the C++ class, but then I need to somehow call that method from C++, and I cannot figure out how to do it.

I tried to give a pointer to EAGLView object to the C++ method and include the "EAGLView.h" in my C++ class header but I got 3999 errors..

So.. how should I do this? An example would be nice.. I only found pure C examples of doing this.

回答1:

You can mix C++ with Objective-C if you do it carefully. There are a few caveats but generally speaking they can be mixed. If you want to keep them separate, you can set up a standard C wrapper function that gives the Objective-C object a usable C-style interface from non-Objective-C code (pick better names for your files, I have picked these names for verbosity):

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

The wrapper function does not need to be in the same .m file as the Objective-C class, but the file that it does exist in needs to be compiled as Objective-C code. The header that declares the wrapper function needs to be included in both CPP and Objective-C code.

(NOTE: if the Objective-C implementation file is given the extension ".m" it will not link under Xcode. The ".mm" extension tells Xcode to expect a combination of Objective-C and C++, i.e., Objective-C++.)


You can implement the above in an Object-Orientented manner by using the PIMPL idiom. The implementation is only slightly different. In short, you place the wrapper functions (declared in "MyObject-C-Interface.h") inside a class with a (private) void pointer to an instance of 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 

Notice the wrapper methods no longer require the void pointer to an instance of MyClass; it is now a private member of MyClassImpl. The init method is used to instantiate a MyClass instance;

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 

Notice that MyClass is instantiated with a call to MyClassImpl::init. You could instantiate MyClass in MyClassImpl's constructor, but that generally isn't a good idea. The MyClass instance is destructed from MyClassImpl's destructor. As with the C-style implementation, the wrapper methods simply defer to the respective methods of 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." );     } } 

You now access calls to MyClass through a private implementation of MyClassImpl. This approach can be advantageous if you were developing a portable application; you could simply swap out the implementation of MyClass with one specific to the other platform ... but honestly, whether this is a better implementation is more a matter of taste and needs.



回答2:

You can compile your code as Objective-C++ - the simplest way is to rename your .cpp as .mm. It will then compile properly if you include EAGLView.h (you were getting so many errors because the C++ compiler didn't understand any of the Objective-C specific keywords), and you can (for the most part) mix Objective-C and C++ however you like.



回答3:

The easiest solution is to simply tell Xcode to compile everything as Objective C++.

Set your project or target settings for Compile Sources As to Objective C++ and recompile.

Then you can use C++ or Objective C everywhere, for example:

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

This has the same affect as renaming all your source files from .cpp or .m to .mm.

There are two minor downsides to this: clang cannot analyse C++ source code; some relatively weird C code does not compile under C++.



回答4:

You need to make your C++ file be treated as Objective-C++. You can do this in xcode by renaming foo.cpp to foo.mm (.mm is the obj-c++ extension). Then as others have said standard obj-c messaging syntax will work.



回答5:

Step 1

Create a objective c file(.m file) and it's corresponding header file.

// Header file (We call it "ObjCFunc.h")

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

// Corresponding Objective C file(We call it "ObjCFunc.m")

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

Step 2

Now we will implement a c++ function to call the objective c function that we just created! So for that we will define a .mm file and its corresponding header file(".mm" file is to be used here because we will be able to use both Objective C and C++ coding in the file)

//Header file(We call it "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__) */ 

//Corresponding Objective C++ file(We call it "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 } 

Step 3

Calling the c++ function(which actually calls the objective c method)

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

//Final call

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

Hope this works!



回答6:

Sometimes renaming .cpp to .mm is not good idea, especially when project is crossplatform. In this case for xcode project I open xcode project file throught TextEdit, found string which contents interest file, it should be like:

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

and then change file type from sourcecode.cpp.cpp to sourcecode.cpp.objcpp

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

It is equivalent to rename .cpp to .mm



回答7:

Also, you can call into Objective-C runtime to call the method.



回答8:

You can mix C++ in with Objectiv-C (Objective C++). Write a C++ method in your Objective C++ class that simply calls [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer]; and call it from your C++.

I haven't tried it before my self, but give it a shot, and share the results with us.



标签
易学教程内所有资源均来自网络或用户发布的内容,如有违反法律规定的内容欢迎反馈
该文章没有解决你所遇到的问题?点击提问,说说你的问题,让更多的人一起探讨吧!