Как я могу сделать делегат Objective-C для класса CPP?
Я застрял, пытаясь объединить openGL-es (шаблон игры xcode openGL с библиотекой ogles2tools из powervr 3.0 sdk. Моя проблема - строка кода, куда я загружаю файл эффекта:
/*
Load the effect.
We pass 'this' as an argument as we wish to receive callbacks as the PFX is loaded.
This is optional and supplying NULL implies that the developer will take care
of all texture loading and binding to to the Effect instead.
*/
if(m_pEffect->Load(*m_pEffectParser, "Effect", c_szPfxFile, NULL, uiUnknownUniforms, &error) != PVR_SUCCESS)
{
NSLog(@"%s",error.c_str());
return;
}
Я должен передать указатель "this", чтобы я мог получать обратные вызовы. Метод делегата, который мне нужно реализовать:
EPVRTError OGLES2IntroducingPFX::PVRTPFXOnLoadTexture(const CPVRTStringHash& TextureName, GLuint& uiHandle, unsigned int& uiFlags)
{
/*
This is an optional callback function for PVRTPFXEffect and can be used to automate
the texture loading process.
If multiple effects are to be loaded and they share textures it would be
prudent to have a caching system in place so texture memory is not wasted.
Please see OGLES2MagicLantern for an example of this.
*/
if(PVRTTextureLoadFromPVR(TextureName.String().c_str(), &uiHandle) != PVR_SUCCESS)
return PVR_FAIL;
return PVR_SUCCESS;
}
Я думаю, что большая проблема для меня заключается в том, как мне обеспечить метод делегата cpp в target-c? Я кое-что прочитал по этому вопросу, но казалось, что то, что я читал, шло в другую сторону. То есть делегат цели c в cpp. Это довольно странно, но вот моя мысль...
Я создаю класс cpp, реализующий нужный мне метод. Я добавляю это в мой класс viewController и передаю указатель на этот класс cpp в вызове m_pEffect->Load. Это кажется правильным?
Благодарю.
PS Извините, если у меня плохое форматирование кода. Я еще учусь.
Изменить: вот пример, который я нашел относительно смешивания target-c и cpp. Это похоже на то, что я хочу сделать.
Обновление: вот некоторая дополнительная информация (запрошено пользователем 1118321)
Класс CPP, которому нужен делегат, называется CPVRTPFXEffect (PVRTPFXParserAPI.h - из powerVR SDK 3.0). Я бы добавил ссылку, но я не уверен, разрешено ли это. Вот ссылка на заголовок класса, но эта версия (и другие в сети) не включала атрибут pDelegate для метода загрузки. Я предполагаю, что они являются примерами предыдущей версии. Дайте мне знать, если это нормально, чтобы опубликовать этот файл класса, и я сделаю это.
Я нашел хороший пример того, что я должен делать, прочитав эту ветку. Итак, вот что у меня так далеко:
Мой класс делегатов CPP...
class myCppDelegate : public PVRTPFXEffectDelegate {
public:
myCppDelegate() {};
EPVRTError PVRTPFXOnLoadTexture(const CPVRTStringHash& TextureName, GLuint& uiHandle, unsigned int& uiFlags) {
return PVR_FAIL;
};
};
Мой класс-оболочка Obj-C (только что измененный из приведенной выше ссылки на пример)...
struct RNWrapOpaque;
@interface RNWrap : NSObject {
struct RNWrapOpaque *_cpp;
}
- (id)init;
@end
реализация...
#import "RNWrap.h"
#import "Wrap.h"
@interface RNWrap ()
@property (nonatomic, readwrite, assign) RNWrapOpaque *cpp;
@end
@implementation RNWrap
@synthesize cpp = _cpp;
struct RNWrapOpaque
{
public:
RNWrapOpaque() : wrap() {};
myCppDelegate wrap;
};
- (id)init
{
self = [super init];
if (self != nil)
{
self.cpp = new RNWrapOpaque();
}
return self;
}
- (void)dealloc
{
delete _cpp;
_cpp = NULL;
// [super dealloc];
}
@end
В основном я могу скомпилировать код и отладить, но когда класс CPVRTPFEffect делает этот вызов:
if(pDelegate->PVRTPFXOnLoadTexture(pTexDesc->FileName, uiHandle, uiFlags) != PVR_SUCCESS)
Я получаю EXC_BAD_ACCESS. Я предполагаю, что он не находит мой метод обратного вызова, потому что я установил точку останова, и строка никогда не вызывается.
Вот мой обновленный код, который вызывает CPVRTPFXEffect::Load с использованием команды bridge для параметра делегата.
if(m_pEffect->Load(*m_pEffectParser, "Effect", c_szPfxFile,(__bridge myCppDelegate*)opaqueCppWrap, uiUnknownUniforms, &error) != PVR_SUCCESS)
Спасибо за вашу помощь!
Обновление 2: проект использует ARC. Вот как выглядит мой интерфейс viewController:
@interface ViewController : GLKViewController {
...
RNWrap* opaqueCppWrap;
...
}
@property (strong) RNWrap *opaqueCppWrap;
Добавление @property не помогло с EXC_BAD_ACCESS. Я не уверен, как "увидеть" значение pDelegate, когда я отслеживаю код CPP. Xcode ничего не показывает, когда я наводю курсор мыши на переменную.
Я добавил следующую строку кода в метод CPVRTPFXEffect::Load (непосредственно перед строкой, в которой происходит сбой):
*pReturnError += PVRTStringFromFormattedStr("Here is your class typeid: %s.\n", typeid(pDelegate).name());
return PVR_FAIL;
Вот что отображается в окне вывода отладки:
Вот ваш класс typeid: P21PVRTPFXEffectDelegate.
Я не уверен, что означает "P21" (если что-нибудь), но похоже, что я близок к тому, чтобы это заработало. Я не знаю, может быть, это так близко, как это получается. Все еще терпит крах и не находит мой метод.
2 ответа
Во-первых, вы можете захотеть взглянуть на последнюю статью в серии об упаковке C++. Большинство из них стало намного проще в последних версиях Clang. Вам, вероятно, больше не нужна половина этого кода. Объекты ObjC++ теперь могут иметь частные свойства C++ без каких-либо хитростей, сохраняя при этом чистый интерфейс ObjC.
Вот как вы хотите думать об этой проблеме:
- Создайте объект C++, который является делегатом. Напишите весь код, связанный с настройкой делегирования и т. Д., На C++. Так что, когда он говорит "передать
this
указатель "вы действительно должны передаватьthis
указатель (потому что вы должны делать это в коде C++). Тот факт, что вы делаете_bridge
приведение в C++ вызов - реальная подсказка, что что-то идет не так. - Пусть ObjC владеет объектом C++ как свойством.
- Напишите делегированные обратные вызовы в C++ внутри объекта C++. Если это полезно, вы можете позволить объекту C++ затем делать вызовы в объект ObjC по мере необходимости, но это может быть проще, если объект C++ выполняет всю работу делегата.
Я наконец-то начал работать, но для этого мне пришлось удалить класс-обёртку obj-c из моего viewController. Вот как выглядит код:
ViewController.h
struct Opaque;
@interface ViewController : GLKViewController {
...
//RNWrap* opaqueCppWrap; // this didn't work
struct Opaque *opaqueCpp; // try this
...
}
ViewController.mm
// declare the Opaque structure
struct Opaque {
public:
Opaque() : cppobject() {};
myCppDelegate cppobject;
};
viewDidLoad
// ... create opaque member on initialization
opaqueCpp = new Opaque();
//opaqueCppWrap = [[RNWrap alloc] init]; // old way of doing things using wrapper
передать делегат в метод Load
// old way using bridge cast and wrapper
//if(m_pEffect->Load(*m_pEffectParser, "Effect", c_szPfxFile,(__bridge myCppDelegate*)opaqueCppWrap, uiUnknownUniforms, &error) != PVR_SUCCESS)
// this works...
if(m_pEffect->Load(*m_pEffectParser, "Effect", c_szPfxFile, (myCppDelegate*)opaqueCpp, uiUnknownUniforms, &error) != PVR_SUCCESS)
Не уверен, почему класс-обертка не работает, но я рад, что мой обратный вызов работает (appy no crashy!)
Фу, это было грубо. Есть мысли / комментарии?