Вызов метода Objective-C из функции-члена C ++?

У меня есть класс (EAGLView) который вызывает функцию-членC++ класс без проблем. Проблема в том, что мне нужноC++ классobjective-C function [context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer]; что я не могу сделать вC++ синтаксис.

Я мог бы обернуть этоObjective-C призываю к тому жеObjective-C класс, который в первую очередь называется классом C ++, но затем мне нужно как-то вызвать этот метод изC++и я не могу понять, как это сделать.

Я пытался дать указатель наEAGLView Объект функции-члена C ++ и включает & quot;EAGLView.h& Quot; в моемC++ заголовок класса, но я получил 3999 ошибок ..

Итак ... как мне это сделать? Пример был бы хорош .. Я нашел только чистыйC примеры этого.

Ответы на вопрос(9)

Шаг

Создайте целевой файл c (файл .m) и соответствующий ему заголовочный файл.

// Заголовочный файл (мы называем его «ObjCFunc.h»)

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

// Соответствующий файл 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

Шаг

Теперь мы реализуем функцию c ++ для вызова целевой функции 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__) */

// Соответствующий файл 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
}

Шаг

Вызов функции 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
}

Надеюсь, это работает!

jective C ++.

Задайте настройки проекта или цели для источников компиляции в соответствии с Objective C ++ и перекомпилируйте.

Затем вы можете использовать C ++ или Objective C везде, например:

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

Это имеет тот же эффект, что и переименование всех ваших исходных файлов из .cpp или .m в .mm.

Есть два незначительных недостатка: clang не может анализировать исходный код C ++; некоторый относительно странный код C не компилируется под C ++.

 Peter N Lewis30 июн. 2009 г., 06:39
Конечно, вы программируете на C ++, поэтому ожидается, что вы будете вести себя соответствующим образом - но, как правило, C ++ лучше, чем C, даже если вы никогда не создаете класс. Это не позволит вам делать глупые вещи, и это позволяет вам делать хорошие вещи (например, лучшие константы и перечисления и тому подобное). Вы все еще можете разыграть точно так же (например, (CFFloat) x).
 dreamlax30 июн. 2009 г., 05:47
Мне просто немного любопытно, когда вы компилируете все как Objective-C ++, вы получаете предупреждения об использовании приведений в стиле C и / или других специфичных для C ++ предупреждений о действительном коде в стиле C?

чтобы ваш файл C ++ рассматривался как Objective-C ++. Вы можете сделать это в xcode, переименовав foo.cpp в foo.mm (.mm - расширение obj-c ++). Тогда, как говорили другие, будет работать стандартный синтаксис сообщений obj-c.

пособ - переименовать ваш .cpp в .mm. Затем он будет правильно скомпилирован, если вы включитеEAGLView.h (вы получили так много ошибок, потому что компилятор C ++ не понимал ни одного из конкретных ключевых слов Objective-C), и вы можете (по большей части) смешивать Objective-C и C ++ так, как вам нравится.

 juvenis30 июн. 2009 г., 18:18
Кажется, я не могу включить EAGLView.h в заголовочный файл C ++, потому что тогда он по какой-то причине ожидает, что код Objective C - C ++, и не понимает @ + другие символы
 Jesse Beder30 июн. 2009 г., 01:19
Вы получаете все эти ошибки компилятора вэт Файл C ++, или они находятся в каком-то другом файле C ++, который включает этот заголовок C ++?
 juvenis30 июн. 2009 г., 01:17
Я уже сделал переименование

C ++ в вашем классе Objective C ++, который просто вызывает[context renderbufferStorage:GL_RENDERBUFFER fromDrawable:(CAEAGLLayer*)self.layer]; и позвони со своего C ++.

Я не пробовал это раньше, но попробуй и поделись результатами с нами.

 juvenis30 июн. 2009 г., 01:16
Но проблема в том, как я могу это назвать ... потому что, если я включу "EAGLview.h" в мой класс C ++, я получу тысячи ошибок.

особенно когда проект кроссплатформенный. В этом случае для проекта xcode я открываю файл проекта xcode через TextEdit, нашел строку, которая содержит интересующий файл, она должна выглядеть следующим образом:

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

, а затем измените тип файла с Sourcecode.cpp.cpp в Sourcecode.cpp.objcpp

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

Это эквивалентно переименованию .cpp в .mm

Я бы добавил одно очко. Последние версии компилятора Clang жалуются на то, что при попытке использовать его код требуется «связующее действие».

Это кажется разумным: использование батута создает потенциальную ошибку: поскольку классы Objective-C подсчитываются по ссылкам, если мы передаем их адрес как void *, мы рискуем получить зависающий указатель, если класс будет собран мусором во время обратного вызова. все еще активен.

Solution 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

Решение 2) Альтернативой является использование эквивалента слабой ссылки (т. Е. Без изменений в счет удержания), без какой-либо дополнительной безопасности.

Язык Objective-C предоставляет для этого спецификатор приведения __bridge (CFBridgingRetain и CFBridgingRelease кажутся тонкими оболочками Cocoa над конструкциями языка Objective-C __bridge_retained и release соответственно, но у Cocoa, по-видимому, нет эквивалента для __bridge).

Необходимые изменения:

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);
}
 QuesterZen26 июл. 2018 г., 03:46
Я должен признать, что несколько сомневаюсь в том, обеспечивает ли Решение 1 какую-либо дополнительную безопасность, несмотря на все теорию сохранения / выпуска. Один момент заключается в том, что мы передаем копиюself в закрытие, которое может устареть. Другое дело, что неясно, как это взаимодействует с автоматическим подсчетом ссылок и может ли компилятор выяснить, что происходит. На практике мне не удавалось создать ситуацию, когда любая из версий потерпела неудачу в простом одномодульном игрушечном примере.
Решение Вопроса

если вы делаете это осторожно. Есть несколько предостережений, но, вообще говоря, они могут быть смешанными. Если вы хотите сохранить их отдельно, вы можете настроить стандартную функцию-оболочку C, которая дает объекту Objective-C удобный интерфейс в стиле C из кода не-Objective-C (выберите лучшие имена для ваших файлов, я выбрал эти имена для многословия):

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

Функция обёрткине требуетс быть в том же самом.m файл как класс Objective-C, но файл, который существует в нужно компилировать как код Objective-C. Заголовок, который объявляет функцию-обертку, должен быть включен как в код CPP, так и в код Objective-C.

(ПРИМЕЧАНИЕ: если для файла реализации Objective C задано расширение «.m», он не будет связываться с Xcode. Расширение «.mm» указывает Xcode ожидать комбинации Objective-C и C ++, то есть Objective-C ++ .)

Вы можете реализовать вышеизложенное объектно-ориентированным способом, используя PIMPL идиома. Реализация только немного отличается. Короче говоря, вы помещаете функции-оболочки (объявленные в «MyObject-C-Interface.h») внутри класса с (приватным) пустым указателем на экземпляр 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

Обратите внимание, что методы-оболочки больше не требуют указатель void на экземпляр MyClass; теперь это частный член 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. Вы можете создать экземпляр MyClass в конструкторе MyClassImpl, но обычно это не очень хорошая идея. Экземпляр MyClass уничтожается из деструктора MyClassImpl. Как и в случае реализации в стиле C, методы-оболочки просто относятся к соответствующим методам 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." );
    }
}

Теперь вы получаете доступ к вызовам MyClass через частную реализацию MyClassImpl. Этот подход может быть полезен, если вы разрабатываете переносимое приложение; Вы могли бы просто поменять реализацию MyClass с одной конкретной платформой на другую ... но, если честно, то, является ли это лучшая реализация, больше зависит от вкуса и потребностей.

 dreamlax18 янв. 2010 г., 08:36
Тебе может понадобиться добавитьextern "C" передint MyObjectDoSomethingWith
 jmort25314 сент. 2012 г., 21:35
Hi @dreamlax, я получаю ошибки компоновщика с этим, пытаясь работать через это. Можете ли вы рассказать, что такое MyClass вself = [[MyClass alloc] init]; строка в методе инициализации MyObject.mm. Не понятно, что такое «MyClass», так как он не определен. Вы хотели поставить вместо этого «MyObject»? Или, может быть, «MyClassImpl»? Ценим любые разъяснения, которые ты можешь дать, чтобы облегчить мою путаницу! :)
 Raffi Khatchadourian23 мар. 2012 г., 18:58
Кроме того, каким образом создается экземпляр instanceCObject в MyCPPClass.cpp?
 user8395018 янв. 2010 г., 11:04
уже пытался, не работает, и это имеет смысл, потому что extern "C" используется, когда мы хотим вызвать функцию C ++ из C, в этом случае мы вызываем функцию C из C ++, нет?
 user8395018 янв. 2010 г., 08:23
Привет, я попробовал это, но я получаю ошибку связи, говоря, что символ (ы) не найден. то есть он не может найти MyObjectDoSomethingWith. Любые идеи

вы можете вызвать во время выполнения Objective C, чтобы вызвать метод.

Ваш ответ на вопрос