c++objective-cxcodeclass-extensions

I need help mixing C++ code and Objective-C code


I am writing a device driver for a Blackmagic Design AV device in XCode, and I'm having trouble including BMD's SyncController class from their abbreviated sample code (below) into my purely Objective-C project.

Their DecklinkAPI.h file is rich in C++ code, so when I try include this header file as-is in a an Objective-C class, the compiler chokes deep in the API include: Unknown type name 'class'; did you mean 'Class'?

I have tried to to bundle up the C++ bits into a Obj-C class extension as noted here, but without much success. I've never done any C++ programming (and have never used Obj-C class extensions), so this is new territory for me.

I'm not sure if I need to create an additional wrapper class for my SyncController object, or whether I can just do a class extension on this one and shuffle the C++ bits into the .mm file.

I would like to be able to do a #include "SyncController.h" (or its wrapper) in an Objective-C class without having the compiler choke.

Any assistance in doing so would be much appreciated.

First up, here is my current SyncController.h file:

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

Next up, here is my (simplified) SyncController.mm file:

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

Solution

  • I have tried to to bundle up the C++ bits into a Obj-C class extension as noted here, but without much success.

    If you're targeting 64-bit, the class extension method should be fairly simple.

    The following is equivalent to the code you've post, but moves all of the C++ declarations to a separate header:

    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..
    

    Any other ObjC classes that need access to SyncController will import "SyncController.h". Any other ObjC++ classes can import either "SyncController.h" or "SyncController_CPP.h"