122

I have a class (EAGLView) which calls a member function 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++ member function 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.

9 Answers 9

220

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.

Sign up to request clarification or add additional context in comments.

22 Comments

Hi, I tried it but I get linkage error saying symbol(s) not found. i.e. it can't find the MyObjectDoSomethingWith. any ideas?
You might need to add extern "C" before the int MyObjectDoSomethingWith
tried that already, doesn't work and it makes sense because extern "C" is used when we want to call C++ function from C, in this case we are calling a C function from C++, no?
Also, how does objectiveCObject get instantiated in MyCPPClass.cpp?
Awesome @dreamlax, that is compiling now but I don't know to call that "someMethod". Which should be the parameters that should be added to :int MyCPPClass::someMethod (void *objectiveCObject, void *aParameter)???
|
16

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.

2 Comments

Are you getting all of those compiler errors in this C++ file, or are they in some other C++ file that happens to include this C++ header?
It seems I cannot include the EAGLView.h in the C++ header file because then it for some reason expects that the Objective C code is C++, and does not understand @ + other symbols
15

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++.

2 Comments

I'm just a little curious, when you compile everything as Objective-C++ do you get warnings about using C-style casts and/or other C++-specific warnings about valid C-style code?
Sure, you are programming in C++ so you'll be expected to behave appropriately - but as a general rule, C++ is a better C than C is, even if you never create a class. It wont let you do stupid things, and it lets you do nice things (like better constants and enums and such). You can still cast just the same (eg (CFFloat)x).
12

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 <Foundation/Foundation.h> #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 <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__) */ 

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

Comments

9

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.

Comments

1

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 = "<group>"; }; 

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 = "<group>"; }; 

It is equivalent to rename .cpp to .mm

Comments

1

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

Comments

1

@DawidDrozd's answer above is excellent.

I would add one point. Recent versions of the Clang compiler complain about requiring a "bridging cast" if attempting to use his code.

This seems reasonable: using a trampoline creates a potential bug: since Objective-C classes are reference counted, if we pass their address around as a void *, we risk having a hanging pointer if the class is garbage collected up while the callback is still active.

Solution 1) Cocoa provides CFBridgingRetain and CFBridgingRelease macro functions which presumably add and subtract one from the reference count of the Objective-C object. We should therefore be careful with multiple callbacks, to release the same number of times as we retain.

// C++ Module #include <functional> void cppFnRequiringCallback(std::function<void(void)> 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 

Solution 2) The alternative is to use the equivalent of a weak reference (ie. no change to the retain count), without any additional safety.

The Objective-C language provides the __bridge cast qualifier to do this (CFBridgingRetain and CFBridgingRelease seem to be thin Cocoa wrappers over the Objective-C language constructs __bridge_retained and release respectively, but Cocoa does not appear to have an equivalent for __bridge).

The required changes are:

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

4 Comments

I have to admit that I am somewhat suspicious about whether Solution 1 provides any additional safety despite all of the retain/release theatrics. One point is that we are passing a copy of self into the closure, which could become outdated. The other point is that its not clear how this interacts with automatic reference counting and whether the compiler can figure out what is going on. In practice I didn't manage to create a situation where either version failed in a simple one-module toy example.
Does this not demonstrate calling c++ from obj-c, or is this a partial amend to @DawidDrozd ? I got confused.
@Kinchog Maybe the original post by DawidDrozd has been deleted. The idea is that c++ will be handed a std::function<void>void that it CAN call by Objective-C. This function will unwrap the Objective-C object and call the correct method. The problem is that this will likely mess up the Objective-C memory management. So the question this post answers is how to avoid this problem.
thanks - I found out that there are C++ headers for The functionality I wanted (dispatch) so I was able to avoid jumping between the two anyhow! Thanks anyway.
-1

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.

1 Comment

But the problem is that how can I call it... because if I include "EAGLview.h" in my C++ class I get thousands of errors.

Start asking to get answers

Find the answer to your question by asking.

Ask question

Explore related questions

See similar questions with these tags.