Мне нужна помощь в смешивании кода C ++ и кода Objective-C

Я пишу драйвер устройства для AV-устройства Blackmagic Design в XCode, и у меня возникают проблемы с включением класса BMD SyncController из их сокращенного примера кода (ниже) в мой проект чисто Objective-C.

Их файл DecklinkAPI.h богат кодом C ++, поэтому, когда я пытаюсь включить этот заголовочный файл как есть в класс Objective-C, компилятор задыхается глубоко в API: Неизвестное имя типа «класс»; Вы имели в виду «класс»?

Я пытался связать биты C ++ в расширение класса Obj-C, как отмечалось Вот, но без особого успеха. Я никогда не занимался программированием на C ++ (и никогда не использовал расширения классов Obj-C), поэтому для меня это новая территория.

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

Я хотел бы иметь возможность сделать #include "SyncController.h" (или его оболочка) в классе Objective-C без наличия дросселя компилятора.

Любая помощь в этом будет принята с благодарностью.

Прежде всего, вот мой текущий файл SyncController.h:

#import <Cocoa/Cocoa.h>
#import "DeckLinkAPI.h"  // this is rich in C++ code

class PlaybackDelegate;

@interface SyncController : NSObject {
PlaybackDelegate*           playerDelegate;

IDeckLink*                  deckLink;
IDeckLinkOutput*            deckLinkOutput;
}

- (void)scheduleNextFrame:(BOOL)prerolling;
- (void)writeNextAudioSamples;

@end

class PlaybackDelegate : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback
{
SyncController*             mController;
IDeckLinkOutput*            mDeckLinkOutput;

public:
PlaybackDelegate (SyncController* owner, IDeckLinkOutput* deckLinkOutput);

// IUnknown needs only a dummy implementation
virtual HRESULT     QueryInterface (REFIID iid, LPVOID *ppv)    {return E_NOINTERFACE;}
virtual ULONG       AddRef ()                                   {return 1;}
virtual ULONG       Release ()                                  {return 1;}

virtual HRESULT     ScheduledFrameCompleted (IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult result);
virtual HRESULT     ScheduledPlaybackHasStopped ();
virtual HRESULT     RenderAudioSamples (bool preroll);
};

void    ScheduleNextVideoFrame (void);

Далее, вот мой (упрощенный) файл SyncController.mm:

#import <CoreFoundation/CFString.h>
#import "SyncController.h"
@implementation SyncController

- (instancetype)init
{
self = [super init];
return self;
}

- (void)dealloc
{
}

- (void)scheduleNextFrame:(BOOL)prerolling
{
}

- (void)writeNextAudioSamples
{
}

@end

PlaybackDelegate::PlaybackDelegate (SyncController* owner, IDeckLinkOutput* deckLinkOutput)
{
mController = owner;
mDeckLinkOutput = deckLinkOutput;
}

HRESULT PlaybackDelegate::ScheduledFrameCompleted (IDeckLinkVideoFrame* completedFrame, BMDOutputFrameCompletionResult result)
{
[mController scheduleNextFrame:NO];
return S_OK;
}

HRESULT     PlaybackDelegate::ScheduledPlaybackHasStopped ()
{
return S_OK;
}

HRESULT     PlaybackDelegate::RenderAudioSamples (bool preroll)
{
[mController writeNextAudioSamples];
if (preroll)
mDeckLinkOutput->StartScheduledPlayback(0, 100, 1.0);

return S_OK;
}

2

Решение

Я пытался связать биты C ++ в расширение класса Obj-C, как отмечалось здесь, но без особого успеха.

Если вы ориентируетесь на 64-битную версию, метод расширения класса должен быть довольно простым.

Следующее эквивалентно коду, который вы публикуете, но перемещает все объявления C ++ в отдельный заголовок:

SyncController.h:

#import <Cocoa/Cocoa.h>

@interface SyncController : NSObject
- (void)scheduleNextFrame:(BOOL)prerolling;
- (void)writeNextAudioSamples;
@end

SyncController_CPP.h

#import "SyncController.h"#include "DeckLinkAPI.h"
class PlaybackDelegate;

@interface SyncController() {
PlaybackDelegate* playerDelegate;
IDeckLink* deckLink;
IDeckLinkOutput* deckLinkOutput;
}
@end

class PlaybackDelegate  ...
{
...
}

SyncController.mm

#import "SyncController_CPP.h"
@implementation SyncController
...
@end

PlaybackDelegate::PlaybackDelegate (SyncController* owner, IDeckLinkOutput* deckLinkOutput)
{
mController = owner;
mDeckLinkOutput = deckLinkOutput;
}

// etc..

Любые другие классы ObjC, которым нужен доступ к SyncController импортирует «SyncController.h». Любые другие классы ObjC ++ могут импортировать либо «SyncController.h», либо «SyncController_CPP.h».

1

Другие решения

Не полный ответ, однако такие ошибки, как:

Неизвестное имя типа «класс»; Вы имели в виду «класс»?

Классическая проблема с Objective-C ++, где Файл реализации Objective C видит файл заголовка C ++.

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

  • Не помещайте заголовки C ++ — это предварительно скомпилированный заголовок.
  • Старайтесь включать заголовки C ++ только в файлы реализации Objective-C ++, а не в их заголовочный файл, который, в свою очередь, может быть включен в файл Objective-C.
  • Скройте использование C ++ из любых заголовочных файлов, например, используя частные переменные экземпляра:

    #import <vector>
    @implementation MyObjCppClass {
    std::vector<int> _stuff;
    }
    
    - (id)init {
    ...
    }
    
    @end
    
  • Если вы смешиваете Objective-C и Objective-C ++, то вам может потребоваться предоставить оболочки Objective-C для классов C ++ (которые выглядят снаружи как Objective-C, но на самом деле реализованы в Objective-C ++).

1

Переименуйте ваши .m файлы (цель-c) в .mm (цель-c ++). это должно позволить вам затем смешивать objc и c ++, включая заголовки c ++ и ссылаться на код c ++ из вашего objc.

—РЕДАКТИРОВАТЬ—

Любой заголовочный файл, который вы включаете из target-c, должен содержать только target-c. Удалите любой c ++ из заголовка в вашем классе-обертке, чтобы получить другие классы objc для сборки. В современном objc вы можете разделить свои ivars между файлами .h и .m; сохраните все ваши методы в .h для использования другими классами objc и объявите ваши c ++ ivars в .mm. Вставьте ваш класс делегата c ++ в его собственный .h, который входит только в оболочку .mm.

1

использование #if __cplusplus,

Например,

#import <Cocoa/Cocoa.h>

#if __cplusplus
#import "DeckLinkAPI.h"  // this is rich in C++ code
#endif // __cplusplus

@interface SyncController : NSObject {
void* playerDelegate; // should be cast as C++ PlaybackDelegate class.
...
}
@end

#if __cplusplus
class PlaybackDelegate : public IDeckLinkVideoOutputCallback, public IDeckLinkAudioOutputCallback
{
...
};
#endif // __cplusplus

Заголовочный файл может использоваться с Objective-C и Objective-C ++. Но вы не можете использовать сигнатуру класса C ++ в объявлении класса Objective-C SyncController в заголовке. использование void * вместо PlaybackDelegate * с правильным типом броска.

Также используя void * означает, что материал C ++ в заголовке больше не нужен.

#import <Cocoa/Cocoa.h>

@interface SyncController : NSObject {
void* playerDelegate; // should be cast as C++ PlaybackDelegate class.
...
}
@end

В коде Objective-C ++,

// initialize
syncController.playerDelegate = new PlaybackDelegate();

// use the pointer
PlaybackDelegate *playbackDelegate = (PlaybackDelegate *)syncController.playerDelegate;
1

инициализировать

syncController.playerDelegate = new PlaybackDelegate();

// use the pointer
PlaybackDelegate *playbackDelegate = (PlaybackDelegate *)syncController.playerDelegate
1