Как обрабатывать делегирование target-c из C ++? - PullRequest
6 голосов
/ 25 октября 2011

Короткая версия: у меня есть Qt / C ++, к которому мне нужно добавить ограниченное количество кода Какао / Objective-C.Я изменил файл .cpp на файл .mm и добавил код / ​​объектыjective-c в указанный файл, и он компилируется и работает.Теперь мне нужен делегат для одного из объектов, которые я создал - NSPopUpButton (или, точнее, его меню), чтобы быть точным - и я застрял.Как добавить делегата для этого объекта?

Подробности: рассматриваемые файлы:

reportwindow.h, reportwindow.cpp RENAMED TO reportwindow.mm - это файлы, содержащие мою первоначальную реализацию C ++плюс некоторый код Objective-C (откройте NSSavePanel, содержащий NSPopUpButton).reportwindow.h дополнительно включается в файл .cpp, если это имеет значение.

menuHandler.h, menuHandler.mm - эти файлы содержат (в настоящее время пустой) класс target-c, который я намеревался использоватькак делегат

Моя первая мысль была о том, что я мог бы просто сделать класс C ++ делегатом, но это, очевидно, не работает, так как прямой C ++ не понимает делегирование.Затем я подумал, что создам отдельный объектный класс как NSMenuDelegate и добавлю его экземпляр в качестве объекта-члена в мой класс C ++.Поскольку я смог добавить другие объекты target-c в качестве членов, я решил, что это должно сработать.Однако, как только я включил заголовок для моего нового класса target-c в файл заголовка класса C ++, я получил несколько сотен ошибок об «ожидаемом неквалифицированном идентификаторе перед '@' токеном» - из файлов заголовков яблока (NSValue.h, NSObject.h и т. Д.) Так что, очевидно, это не сработало, по крайней мере, не как есть.Я получаю тот же результат, когда включаю ЛЮБОЙ заголовок какао в мой файл заголовка класса.

Затем я подумал, что попробую предварительное объявление класса target-c (именно так я получил другую цель-c).объекты работают).тем не менее, это тоже не сработало - если я объявил его как «class myClassName», я получил ошибку о повторном определении класса как символа другого типа (предположительно, класс c ++ против протокола Objective-C).Если я пытаюсь переслать объявление в качестве @protocol myClassName, я получаю сообщение об ошибке «ожидаемый неквалифицированный идентификатор перед маркером @».Так как я могу сделать эту работу?

Ответы [ 4 ]

11 голосов
/ 25 октября 2011

Хорошо, чтобы ответить на ваш вопрос:

reportwindow.h дополнительно включен в файл .cpp, если это имеет значение.

Это действительно имеет значение,Любой модуль компиляции (в данном случае файл cpp), который касается кода Objective-C, должен быть переименован в .mm или .m.Включение заголовка, который, в свою очередь, включает в себя материал Objective-C в файле C ++, приведет к проблеме, с которой компилятор C ++ видит код Objective-C, который он не может обработать.

Переименование файла cpp в mm выберет параметр Objective-C во время компиляции (а не когда файл называется cpp или c) и, следовательно, позволит скомпилировать материал с токенами Objective-C (в основном, ").@ "в вашем случае).

Альтернативой может быть не включение класса делегата Objective-C в ваш класс C ++, а включение указателя на ваш класс C ++ в делегате Objective-C (т. е. реализовать егонаоборот).Таким образом, вы можете расположить вещи так, чтобы код Objective-C не касался кода C ++.

Редактировать: На самом деле, я бы предпочел второе предложение.Вот пример:

DelegateClass.h:

class MyCPPClassHandlingStuff;

@interface MyDelegateObject : NSObject <SomeDelegateProtocol> {
  MyCPPClassHandlingStuff *m_handlerInstance;
}

- (id) initWithCPPInstance:(MyCPPClassHandlingStuff*)cppInstance;

- (void) theDelegateProtocolMethod;

@end

DelegateClass.mm

#include "MyCPPClassHandlingStuff.h"

@implementation MyDelegateObject

- (id) initWithCPPInstance:(MyCPPClassHandlingStuff*)cppInstance
{
  self = [super init];
  if (self) {
    m_handlerInstance = cppInstance;
  }
  return self;
}

- (void) theDelegateProtocolMethod
{
  if (m_handlerInstance)
    m_handlerInstance->handleDelegateMethod();
}

@end

А также MyCPPClassHandlingStuff.h:

#ifndef __MyCPPClassHandlingStuff_H__
#define __MyCPPClassHandlingStuff_H__

class MyCPPClassHandlingStuff
{
public:
  MyCPPClassHandlingStuff();
  void handleDelegateMethod();
};

#endif /* __MyCPPClassHandlingStuff_H__ */

MyCPPClassHandlingStuff может быть инициализирован из Objective C, но вы не можете инициализировать любой класс Objective C из кода C ++.Если вам нужно использовать Objective-C в вашем коде C ++, вам придется скомпилировать его как Objective-C (то есть использовать файл .mm).Я оставляю детали .cpp в качестве упражнения для читателя;)

1 голос
/ 25 октября 2011

Создайте класс Objective-C для удовлетворения протокола делегирования, и пусть он делегируется вашему классу C ++. Проблема заключается в том, что AppKit ожидает взаимодействия с объектами Objective-C, поэтому вам необходимо использовать оболочку для делегирования вашим объектам C ++. (Да, вы можете сделать некоторое злоупотребление во время выполнения, определив указатель isa в вашем классе C ++, чтобы сделать его несколько совместимым с объектами ObjC (до такой степени, что вы можете отправлять сообщения классам C ++), но не делайте этого. Это неприятно.)

Так что создайте прокладку, которая инициализируется вашим классом C ++, имеет это как ivar. Он должен реализовывать интересующие вас методы делегата и передавать их вашему классу C ++ таким образом, чтобы он мог их понять. Тада, готово.

0 голосов
/ 12 февраля 2015

Есть некоторая полезная информация о том, как настроить ваши заголовки, чтобы они могли использоваться как из ObjC, так и из C ++ в Могу ли я отделить основную функцию и классы C ++ от процедур Objective-C и / или C при компиляции и компоновке?

0 голосов
/ 25 октября 2011

Затем я подумал, что попробую предварительное объявление классаjective-c (именно так я и заработал другие объекты target-c).однако это тоже не сработало - если я объявил его как «class myClassName», я получил ошибку о повторном определении класса как символа другого типа (предположительно, класс c ++ против протокола Objective-C).

Почему бы не объявить форвард как @class myClassName?

...