从C ++方法调用Objective-C方法?
我有一个类( EAGLView
)调用一个C++
类的方法没有问题。 现在,问题是我需要在C++
类中调用一个objective-C
function
[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer];
我不能用C++
语法来做。
我可以将这个Objective-C
调用包装到同一个Objective-C
类中,这个类首先调用C ++类,但是我需要以某种方式从C++
调用这个方法,我不知道该怎么做。
我试图给EAGLView
对象指向C ++方法,并在我的C++
类头包括“ EAGLView.h
”,但我得到了3999错误..
所以..我该怎么做呢? 一个例子会很好..我只find了纯C
例子。
如果你仔细的做,你可以混用C ++和Objective-C。 有几个警告,但一般来说,他们可以混合。 如果你想保持它们的独立性,你可以设置一个标准的C包装函数,为Objective-C对象提供一个可用于非Objective-C代码的C风格的接口(为你的文件select更好的名称,我select了这些名称为详细):
为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); }
包装函数不需要与Objective-C类在同一个.m
文件中,但它存在的文件需要编译为Objective-C代码 。 声明包装函数的头文件需要包含在CPP和Objective-C代码中。
(注意:如果Objective-C实现文件的扩展名是“.m”,它将不会在Xcode下链接。“.mm”扩展名告诉Xcode期望Objective-C和C ++,即Objective-C ++的组合。 )
您可以使用PIMPL惯用法以Object-Orientented的方式实现上述内容。 实施只是略有不同。 简而言之,您将封装函数(在“MyObject-C-Interface.h”中声明)放置在具有指向MyClass实例的(专用)void指针的类中。
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
注意包装器方法不再需要指向MyClass实例的void指针; 它现在是MyClassImpl的私人成员。 init方法用于实例化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
注意MyClass是通过调用MyClassImpl :: init来实例化的。 你可以在MyClassImpl的构造函数中实例化MyClass,但这通常不是一个好主意。 MyClass实例是从MyClassImpl的析构函数中析构的。 和C风格的实现一样,wrapper方法只是按照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." ); } }
您现在通过MyClassImpl的私有实现访问MyClass。 如果您正在开发便携式应用程序,这种方法可能是有利的; 你可以简单地将MyClass的实现换成另一个特定的实现,但是说实话,这是否是一个更好的实现更多的是品味和需求。
您可以将代码编译为Objective-C ++ – 最简单的方法是将.cpp重命名为.mm。 如果包含EAGLView.h
那么它会很好地编译(因为C ++编译器不了解任何Objective-C特定的关键字,所以出现了很多错误),并且可以(大部分)将Objective-C和C ++,但你喜欢。
最简单的解决scheme是简单地告诉Xcode将所有东西都编译成Objective C ++。
针对Objective C ++设置编译源的项目或目标设置并重新编译。
那么你可以在任何地方使用C ++或Objective C,例如:
void CPPObject::Function( ObjectiveCObject* context, NSView* view ) { [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)view.layer] }
这与将.cpp或.m中的所有源文件重命名为.mm具有相同的效果。
有两个小缺点:铛不能分析C ++源代码; 一些比较奇怪的C代码不能在C ++下编译。
您需要将您的C ++文件视为Objective-C ++。 您可以通过将foo.cpp重命名为foo.mm(.mm是obj-c ++扩展名)来在xcode中执行此操作。 然后像其他人所说的标准obj-c消息传递语法将工作。
步骤1
创build一个客观的c文件(.m文件),它是相应的头文件。
//头文件(我们称之为“ObjCFunc.h”)
#ifndef test2_ObjCFunc_h #define test2_ObjCFunc_h @interface myClass :NSObject -(void)hello:(int)num1; @end #endif
//相应的目标C文件(我们称之为“ObjCFunc.m”)
#import <Foundation/Foundation.h> #include "ObjCFunc.h" @implementation myClass //Your objective c code here.... -(void)hello:(int)num1 { NSLog(@"Hello!!!!!!"); } @end
第2步
现在我们将实现一个c ++函数来调用我们刚刚创build的目标函数c! 因此,我们将定义一个.mm文件及其相应的头文件(这里使用“.mm”文件是因为我们可以在文件中使用Objective C和C ++编码)
//头文件(我们称之为“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__) */
//相应的目标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 }
第3步
调用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__
//最后呼叫
#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 }
希望这个作品!
有时将.cpp重命名为.mm不是个好主意,特别是当项目是跨平台时。 在这种情况下,对于xcode项目,我通过TextEdit打开xcode项目文件,find其中的内容兴趣文件的string,它应该是这样的:
/* OnlineManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.cpp.cpp; path = OnlineManager.cpp; sourceTree = "<group>"; };
然后将文件types从sourcecode.cpp.cpp更改为sourcecode.cpp.objcpp
/* OnlineManager.cpp */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = **sourcecode.cpp.objcpp**; path = OnlineManager.cpp; sourceTree = "<group>"; };
这相当于将.cpp重命名为.mm
另外,您可以调用Objective-C运行时来调用该方法。
您可以将C ++与Objectiv-C(Objective C ++)混合。 在Objective C ++类中编写一个C ++方法,它只需调用[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer];
并从你的C ++调用它。
我还没有尝试过,而是一试身手,与我们分享成果。