从 C++ 成员函数调用 Objective-C 方法?

声明:本页面是StackOverFlow热门问题的中英对照翻译,遵循CC BY-SA 4.0协议,如果您需要使用它,必须同样遵循CC BY-SA许可,注明原文地址和作者信息,同时你必须将它归于原作者(不是我):StackOverFlow 原文地址: http://stackoverflow.com/questions/1061005/
Warning: these are provided under cc-by-sa 4.0 license. You are free to use/share it, But you must attribute it to the original authors (not me): StackOverFlow

提示:将鼠标放在中文语句上可以显示对应的英文。显示中英文
时间:2020-08-27 18:37:41  来源:igfitidea点击:

Calling Objective-C method from C++ member function?

c++objective-c

提问by juvenis

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-Cfunction[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer];which I cannot do in C++syntax.

我有一个类 ( EAGLView) 可以C++毫无问题地调用类的成员函数。现在,问题是我需要在那个C++类 a 中调用objective-Cfunction[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer];我在C++语法上无法做到的。

I could wrap this Objective-Ccall to the same Objective-Cclass 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.

我可以将此Objective-C调用包装到Objective-C最初称为 C++ 类的同一个类,但随后我需要以某种方式从 调用该方法,但C++我不知道该怎么做。

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

我试图给一个指向EAGLViewC++ 成员函数的对象的指针,并EAGLView.h在我的C++类头中包含“ ”,但我得到了 3999 个错误..

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

所以..我应该怎么做?一个例子会很好..我只找到C了这样做的纯粹例子。

回答by dreamlax

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):

如果你小心翼翼,你可以将 C++ 与 Objective-C 混合使用。有一些警告,但总的来说,它们可以混合使用。如果您想将它们分开,您可以设置一个标准的 C 包装函数,该函数为 Objective-C 对象提供一个来自非 Objective-C 代码的可用 C 样式接口(为您的文件选择更好的名称,我选择了这些名称为冗长):

MyObject-C-Interface.h

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

我的对象.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

我的对象.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

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 needto be in the same .mfile 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.

包装函数不需要.m与 Objective-C 类在同一个文件中,但它存在的文件需要编译为 Objective-C 代码。声明包装函数的头文件需要包含在 CPP 和 Objective-C 代码中。

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

(注意:如果 Objective-C 实现文件被赋予扩展名“.m”,它不会在 Xcode 下链接。“.mm”扩展名告诉 Xcode 期待 Objective-C 和 C++ 的组合,即,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.

您可以使用PIMPL idiom以面向对象的方式实现上述内容。实现只是略有不同。简而言之,您将包装器函数(在“MyObject-C-Interface.h”中声明)放置在一个具有指向 MyClass 实例的(私有)void 指针的类中。

MyObject-C-Interface.h (PIMPL)

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;

注意包装方法不再需要指向 MyClass 实例的 void 指针;它现在是 MyClassImpl 的私有成员。init 方法用于实例化一个 MyClass 实例;

MyObject.h (PIMPL)

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)

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.

请注意,MyClass 是通过调用 MyClassImpl::init 来实例化的。您可以在 MyClassImpl 的构造函数中实例化 MyClass,但这通常不是一个好主意。MyClass 实例从 MyClassImpl 的析构函数中析构。与 C 风格的实现一样,包装方法简单地遵循 MyClass 的相应方法。

MyCPPClass.h (PIMPL)

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)

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.

您现在可以通过 MyClassImpl 的私有实现访问对 MyClass 的调用。如果您正在开发可移植的应用程序,这种方法可能是有利的;您可以简单地将 MyClass 的实现替换为特定于另一个平台的实现……但老实说,这是否是更好的实现更取决于品味和需求。

回答by Jesse Beder

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.

您可以将代码编译为 Objective-C++ - 最简单的方法是将 .cpp 重命名为 .mm。如果你包含EAGLView.h(你得到这么多错误是因为 C++ 编译器不理解任何 Objective-C 特定的关键字),它然后会正确编译,并且你可以(在大多数情况下)混合 Objective-C 和 C++,但是你喜欢。

回答by Peter N Lewis

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

最简单的解决方案是简单地告诉 Xcode 将所有内容编译为 Objective C++。

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

将 Compile Sources As 设置为 Objective C++ 的项目或目标设置并重新编译。

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

然后你可以在任何地方使用 C++ 或 Objective C,例如:

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.

这与将所有源文件从 .cpp 或 .m 重命名为 .mm 具有相同的影响。

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

这样做有两个小缺点:clang 无法分析 C++ 源代码;一些相对奇怪的 C 代码不能在 C++ 下编译。

回答by Mishal Shah

Step 1

第1步

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

创建一个目标 c 文件(.m 文件)及其对应的头文件。

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

// 头文件(我们称之为“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")

// 对应的Objective C文件(我们称之为“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

第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)

现在我们将实现一个 c++ 函数来调用我们刚刚创建的目标 c 函数!为此,我们将定义一个 .mm 文件及其相应的头文件(此处使用“.mm”文件,因为我们将能够在文件中使用 Objective C 和 C++ 编码)

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

//头文件(我们称之为“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")

//对应的Objective 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
}

Step 3

第 3 步

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

调用 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__

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

希望这有效!

回答by olliej

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.

您需要将 C++ 文件视为 Objective-C++。您可以通过将 foo.cpp 重命名为 foo.mm(.mm 是 obj-c++ 扩展名)在 xcode 中执行此操作。然后正如其他人所说,标准的 obj-c 消息传递语法将起作用。

回答by Yevgeniy Logachev

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:

有时将 .cpp 重命名为 .mm 并不是一个好主意,尤其是当项目是跨平台时。在这种情况下,对于 xcode 项目,我通过 TextEdit 打开 xcode 项目文件,找到包含感兴趣文件的字符串,它应该是这样的:

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

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

然后将文件类型从sourcecode.cpp.cpp更改为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

相当于将 .cpp 重命名为 .mm

回答by Maxthon Chan

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

此外,您可以调用 Objective-C 运行时来调用该方法。

回答by QuesterZen

@DawidDrozd's answer above is excellent.

@DawidDrozd 上面的回答非常好。

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

我要补充一点。Clang 编译器的最新版本抱怨如果尝试使用他的代码需要“桥接转换”。

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.

这似乎是合理的:使用蹦床会产生一个潜在的错误:因为 Objective-C 类是引用计数的,如果我们将它们的地址作为 void * 传递,如果类被垃圾收集而回调仍然存在,我们就有可能有一个挂起的指针积极的。

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.

解决方案 1) Cocoa 提供了 CFBridgingRetain 和 CFBridgingRelease 宏函数,它们大概可以从 Objective-C 对象的引用计数中加减一。因此,我们应该小心处理多个回调,释放与保留的次数相同的次数。

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

解决方案 2) 替代方案是使用等效的弱引用(即不更改保留计数),而没有任何额外的安全性。

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

Objective-C 语言提供了 __bridge cast 限定符来执行此操作(CFBridgingRetain 和 CFBridgingRelease 似乎分别是 Objective-C 语言构造 __bridge_retained 和 release 上的薄 Cocoa 包装器,但 Cocoa 似乎没有 __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);
}

回答by hhafez

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

您可以将 C++ 与 Objectiv-C(Objective C++)混合使用。在您的目标 C++ 类中编写一个 C++ 方法,该方法只需[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer];从您的 C++调用和调用它。

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

我自己之前还没有尝试过,但请试一试,并与我们分享结果。