I need to wrap a c++ object inside an Objective-C class - initially I did that by putting the C++ objects inside the #implementation in the .mm file like this:
MyObjCClass.h:
#interface MyObjCClass : NSObject
#end
MyObjCClass.mm:
#implementation MyObjCClass {
MyCppClass member;
}
#end
Sadly I had to give this up since this code is also used in a project which targets 32-bit architectures and thus this feature is not supported. So I did a forward struct declaration, the struct is defined in the .mm file and wraps the C++ object:
MyObjCClass.h:
struct MyObjCClassPrivate;
#interface MyObjCClass : NSObject {
MyObjCClassPrivate* memberPrivate;
}
-(void)dealloc;
-(id)init;
#end
MyObjCClass.mm:
struct MyObjCClassPrivate {
MyCppClass member;
}
#implementation MyObjCClass
-(void)dealloc
{
delete memberPrivate;
memberPrivate = nullptr;
}
-(id)init
{
if (self = [super init])
{
memberPrivate = new MyObjCClassPrivate; // what happens if new throws std::bad_alloc ?
}
return self;
}
#end
The question is - should I care if the allocation with new fails? What are the consequences (apart from the exception being thrown?) - do I need to do some additional cleanup for the Ojbective-C class instance?
I dont want to put the c++ stuff in the header since this class is also used by plain Objective-C code, which then fails to compile.
Related
I'm working on a app using openCV and a utility class "Detector" created in C++. I want to create a objective-c++ wrapper "DetectorWrapper" for the c++ class and use this wrapper in Swift (bridging header). But know when I try to call a fonction from DetectorWrapper, my app crash with the error : EXC_BAD_ACCESS
I read sowewhere that to be allow to use the objective-c++ class in Swift, I cannot include c++ file in the DetectorWrapper.h so I use the type id.
Here is my c++ class: "Detector.h"
#include "opencv2/opencv.hpp"
class Detector
{
public:
// Constructor
Detector(int inputPlayerIdx);
// Scan the input video frame
void scanFrame(cv::Mat frame);
// Tracking API
bool isTracking();
};
My wrapper: "DetectorWrapper.h"
#interface DetectorWrapper : NSObject
#end
#interface DetectorWrapper ()
#property (nonatomic, readwrite, assign) id bld;
- (id)init: (int) inputPlayerIdx;
- (void)dealloc;
- (void) scanFrame: (UIImage*) frame;
- (bool) isTracking;
#end
"DetectorWrapper.mm"
#import "DetectorWrapper.h"
#import "Detector.hpp"
#import "UIImage+OpenCV.h"
#implementation DetectorWrapper
#synthesize bld = _bld;
- (id)init: (int) inputPlayerIdx {
self = [super init];
if (self) {
_bld = (__bridge id) new Detector(inputPlayerIdx);
}
return self;
}
- (void)dealloc {
//[self->_bld dealloc];
//[super dealloc];
//delete _bld;
}
- (void) scanFrame: (UIImage*) frame{
[self->_bld scanFrame:frame];
}
- (bool) isTracking{
return [self->_bld isTracking];
}
#end
Using this in Swift:
let detector = DetectorWrapper(2)
detector.isTracking()
with the file "Detector.h" in Bridging Header.
I got the "EXC_BAD_ACCESS" error when calling for .isTracking
I don't understand the problem at all and how to figure it out.
Maybe I just made a mistake coding my objective-c++ wrapper, I'm not used to this language. Any ideas?
One problem here is that a C++ object is used as if it was an Objective-C object, and these kinds of objects are not interchangeable.
For illustration purposes and for simplicity, let's eliminate the scanFrame method. Then DetectorWrapper.h becomes
#interface DetectorWrapper : NSObject
- (id)init: (int) inputPlayerIdx;
- (void)dealloc;
- (bool) isTracking;
#end
and the wrapper implementation:
#implementation DetectorWrapper
{
// Instance variable to hold a C++ object pointer
Detector * ptrDetector;
}
- (id)init: (int) inputPlayerIdx {
self = [super init];
if (self) {
ptrDetector = new Detector(inputPlayerIdx);
}
return self;
}
- (void)dealloc {
// Don't want to leak the C++ Detector instance
delete ptrDetector;
}
- (bool) isTracking{
return ptrDetector->isTracking();
}
#end
Please note that when you re-introduce scanFrame, you won't be able to just pass UIImage* to ptrDetector->scanFrame(), which takes cv::Mat, you'll have to do some magic in your wrapper's scanFrame to convert between the 2 types, but that's a topic in its own right, I think. BTW, I'm assuming that in your example Detector.h and Detector.hpp refer to the same file, it's just a typo.
I am trying to wrap a c++ library with objective-c++ so i can use it in and iOS application. The library is not finished, only the specifications and a draft of the header file that I will be using.
The api has a function
Result initialize(const char* oemUser, const char* oemPassword, Callback* pCallback);
where pCallback will be called with different states and progress.
Callback is a pure virtual Class.
I have been following some guides to wrap the code, and I think I understand how to properly wrap it so I can call the C++ functions from Objective-C++ but not the other way around.
Some code:
foo.hpp (c++ api-headers)
#ifndef FOO_API_H
#define FOO_API_H
namespace foo {
namespace bar {
enum struct Result
{
Ok,
Error,
};
enum struct State
{
Uninitialized,
Done,
kError,
};
class Callback
{
public:
virtual ~Callback() {}
virtual Result enumerateEndpoints(int index,
char* info,
unsigned length) const = 0;
virtual Result processState(State state,
float progress,
bool reqNext) = 0;
};
/** Initialize - initializes the FOO library
\param oemUser OEM user for validating library
\param oemPassword OEM password for validating library
\param pCallback Pointer to an object implementing Callback
interface
\return Result
*/
Result initialize(const char* oemUser,
const char* oemPassword,
Callback* pCallback);
Result terminate();
State getCurrentState();
Result getLastError(char* buffer, unsigned length);
}
}
#endif
FooApiControlWrapper.h (My main api wrapper i obj-c++)
#import <Foundation/Foundation.h>
#include "FooApiCallbackWrapper.h"
#include "foo_api.hpp"
#interface FooApiControlWrapper : NSObject
- (foo::bar::Result)initialize:(NSString*)oemUser with:(NSString*)oemPassword using:(foo::bar::Callback*)callback;
- (foo::bar::Result)terminate;
- (foo::bar::State)getCurrentState;
- (foo::bar::Result)getLastError:(NSString*)buffer lengt:(NSInteger*)length;
#end
FooApiControlWrapper.mm
Here you can see that I am providing foo::bar::Callback to the obj-c++ init parameter, but somehow I think this should be an Obj-c++ Object.
#import "FooApiControlWrapper.h"
#include "foo_api.hpp"
#implementation FooApiControlWrapper
- (foo::bar::Result)initialize:(NSString*)oemUser with:(NSString*)oemPassword using:(foo::bar::Callback*)callback {
return foo::bar::initialize([oemUser UTF8String], [oemPassword UTF8String], callback);
}
- (foo::bar::Result)terminate {
return foo::bar::Result::Ok;
}
- (foo::bar::State)getCurrentState {
return foo::bar::State::Uninitialized;
}
- (foo::bar::Result)getLastError:(NSString*)buffer lengt:(NSInteger*)length {
return foo::bar::Result::Ok;
}
#end
FooApiCallbackWrapper.h (My wrapper for the callback class)
#import <Foundation/Foundation.h>
#include "foo_api.hpp"
#interface FooApiCallbackWrapper : NSObject
- (instancetype) init;
- (foo::bar::Result)enumerateEndpoints:(NSInteger*)index with:(NSString*)info and:(NSInteger*)length;
- (foo::bar::Result)processState:(foo::bar::State)state progress:(float)progress reqNext:(Boolean)reqNext;
#end
FooApiCallbackWrapper.mm
#import "FooApiCallbackWrapper.h"
#include "foo_api.hpp"
#implementation FooApiCallbackWrapper
- (instancetype) init{
self = [super init];
return self;
}
- (void)dealloc{
}
- (foo::bar::Result)enumerateEndpoints:(NSInteger*)index with:(NSString*)info and:(NSInteger*)length {
return foo::bar::Result::Ok;
}
- (foo::bar::Result)processState:(foo::bar::State)state progress:(float)progress reqNext:(Boolean)reqNext {
return foo::bar::Result::Ok;
}
#end
I just want to be able to write a callback in Obj-C++ that later will be called by my C++-Api.
Where and how to proceed?
Remember that C++ is allowed in Objective-C++, so the standard way is to just add a plain C or C++ function in your Objective-C++ part (outside of the library), and let that function - which is compiled as Objective-C++, operate with the Obj-C++ stuff, i.e. perform methods on Obj-C++-Objects. If you need to pass References to Objects, some void * will usually do. Alternatively, some singleton / look-up pattern can be used.
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;
}
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"
Not a complete answer, however errors like:
Unknown type name 'class'; did you mean 'Class'?
Is a classic issue with Objective-C++ where an Objective-C implementation file is seeing a C++ header file.
I can only provide advice about how to avoid it as you didn't post the complete build output.
Don't put C++ headers is the pre-compiled header.
Try to only include C++ headers within Objective-C++ implementation files and not in their counterpart header file which might, in turn, be included into an Objective-C file.
Hide the use of C++ from any header files, for example using private instance variables:
#import <vector>
#implementation MyObjCppClass {
std::vector<int> _stuff;
}
- (id)init {
...
}
#end
If you are mixing Objective-C and Objective-C++ then you might find you need to provide Objective-C wrappers to C++ classes (which look from the outside as Objective-C but are actually implemented in Objective-C++).
Rename your .m files (objective-c) to .mm (objective-c++). this should allow you to then mix objc and c++ by including c++ headers and referencing c++ code from your objc.
---EDIT---
Any header file you include from objective-c must contain only objective-c. Remove any c++ from the header in your wrapper class to get the other objc classes to build. In modern objc, you can split your ivars between the .h and .m files; keep all your methods in the .h for other objc classes to use, and declare your c++ ivars in the .mm. Stick your c++ delegate class in its own .h that is only included from the .mm wrapper.
Use #if __cplusplus.
For example,
#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
The header file can be used with Objective-C and Objective-C++. But you can not use C++ class signature in SyncController Objective-C class declaration in the header. Use void * instead of PlaybackDelegate * with proper type cast.
Also using void * means that C++ stuff in the header is no longer needed.
#import <Cocoa/Cocoa.h>
#interface SyncController : NSObject {
void* playerDelegate; // should be cast as C++ PlaybackDelegate class.
...
}
#end
In Objective-C++ code,
// initialize
syncController.playerDelegate = new PlaybackDelegate();
// use the pointer
PlaybackDelegate *playbackDelegate = (PlaybackDelegate *)syncController.playerDelegate;
initialize
syncController.playerDelegate = new PlaybackDelegate();
// use the pointer
PlaybackDelegate *playbackDelegate = (PlaybackDelegate *)syncController.playerDelegate
I'm going to write a lot of C++ functions for my Objective-C code (due to third-party functions). And I was thinking it may be a good idea to abstract out the C++ code by having a intermediate Objective-C++ file wrapper between the Objective-C code and the C++ code. The layout as I have it currently is the ViewController.m file creates an instance of my wrapper Objective-C++ class. This class calls it's instance methods which in turn call the C++ code. A simple version of this is given below. Is this a bad way to do this? Is there a more appropriate way to approach this? Any other criticisms with the code as is? Thank you much!
ViewController.m
#import "ViewController.h"
#import "CppWrapper.h"
#interface ViewController ()
#end
#implementation ViewController
- (void)viewDidLoad
{
[super viewDidLoad];
CppWrapper *wrap = [[CppWrapper alloc] init];
double n = 5;
n = [wrap cppTimesTwo:n];
}
- (void)didReceiveMemoryWarning
{
[super didReceiveMemoryWarning];
}
#end
CppWrapper.mm
#import "CppWrapper.h"
#import "Cpp.h"
#implementation CppWrapper
- (double)cppTimesTwo:(double) number
{
return timesTwo(number);
}
#end
Cpp.cpp
#include "Cpp.h"
double timesTwo(double number)
{
return 2 * number;
}
We did the same thing in a project to reuse some C source code and it worked very well. I think it is a good way to do this.
I have a Cocoa project (a Mac OS X app), all Objective-C. I pulled in one C++ class (which I know works) from another project, and make an Objective-C wrapper for it. The ObjC wrapper class is using a .mm extension. However, the C++ header file contains #includes to standard C++ header files (<vector>, for example), and I get errors on those.
A minimal example would look like the following. CppClass is the C++ class, and CppWrapper is the ObjC class which wraps it.
// CppClass.h
#ifndef _CPP_CLASS_H_
#define _CPP_CLASS_H_
#include <vector>
class CppClass
{
public:
CppClass() {}
~CppClass() {}
private:
std::vector<int> my_ints;
};
#endif /* _CPP_CLASS_H_ */
// CppWrapper.h
#import <Foundation/Foundation.h>
#import "CppClass.h"
#interface CppWrapper : NSObject {
CppClass* myCppClass;
}
#end
// CppWrapper.mm
#import "CppWrapper.h"
#implementation CppWrapper
- (id)init
{
self = [super init];
if (self) {
myCppClass = new CppClass;
}
return self;
}
- (void)dealloc
{
delete myCppClass;
[super dealloc];
}
#end
// The file that uses CppWrapper
// TestAppDelegate.m
#import "TestAppDelegate.h"
#import "CppWrapper.h"
#implementation TestAppDelegate
- (void)applicationDidFinishLaunching:(NSNotification *)aNotification
{
myWrapper = [[CppWrapper alloc] init];
}
#end
The error I'm getting is the #include of vector in CppClass.h. The error is
lexical or Preprocessor issue: 'vector' file not found
This code works fine in another (all C++) project, so I'm pretty sure it's a build setting, or something I've done wrong in the wrapper class. I'm using Xcode 4. I created a default Cocoa Mac OS app project and all settings are default.
Update: I just realized that if I set TestAppDelegate's File Type to Objective-C++ (or rename it to TestAppDelegate.mm), it works. What I don't understand is, this class is pure Objective-C; why does it have to be compiled as Objective-C++? The whole point of having an Objective-C wrapper on my C++ class is so that I don't have to build the entire project as Objective-C++.
The problem with your CppWrapper class is that it doesn't present a pure Objective-C interface. In your CppWrapper.h file, you're importing the C++ class's header file, which means that any Objective-C class that imports the wrapper class will need to be compiled as Objective-C++, including your TestAppDelegate.
Instead, you'd need to do something like this to completely hide the C++ within the CppWrapper.mm file:
// CppWrapper.h
#import <Foundation/Foundation.h>
#interface CppWrapper : NSObject {
void *myCppClass;
}
- (void)doSomethingWithCppClass;
#end
// CppWrapper.mm
#import "CppWrapper.h"
#import "CppClass.h"
#implementation CppWrapper
- (id)init {
self = [super init];
if (self) {
myCppClass = new CppClass;
}
return self;
}
- (void)dealloc {
delete myCppClass;
[super dealloc];
}
- (void)doSomethingWithCppClass {
static_cast<CppClass *>(myCppClass)->DoSomething();
}
#end
Personally, I would
#include "CppClass.h"
instead of importing it.
That's probably not your problem though.