I have a burning question on writing objective C wrapper for c++. That is an error in my code when i try to build it. I'm not sure what i have do it wrong. Would truly appreciate any help or guide. The following is the sample code that i have written:
///Print.h///
int test1();
///Print.cpp///
int test1()
{
printf ("hello man\n");
}
///cppWrapper.h///
struct Print;
typedef struct Print Print;
#interface cppWrapper : NSObject
{
Print *print;
}
#property (nonatomic, assign) Print *print;
-(id)init;
-(int)runTest;
///cppWrapper.mm///
#import "cppWrapper.h"
#implementation cppWrapper
#synthesize print = _print;
- (id)init
{
self = [super init];
if (self)
{
_print = new Print(); //error occurred here.
}
return self;
}
-(int)runTest
{
self.print->test1();
}
Objective-C wrapper for C++ is not implemented in terms of objective-C code.
To implement it, use pure C functions.
Under pure C functions, implement objective-C code. In C++ code, use C function to call objective-C code.
Objective-C does not understand C++.
Rather use Objective-C++ to use C++ code.
In objective-C++, you can use C++ code. Save your file as .mm rather than .m.
Edit:
The error in your code is because, the compiler is not able to find the definition of the structure. Compiler needs to know what is the size of the structure. Without its definition, it cannot create the object. This is also true even if you create object on stack.
Related
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'm writing an Objective-C++ class interface that has to be usable from both Objective-C and Objective-C++. The problem is that, because it must be usable from Objective-C, I cannot simply use a C++ type. I want to do it using pointers and I came up with this:
#interface SXDiff : NSObject {
#private
#ifdef __cplusplus
dtl::Diff<std::string, std::vector<std::string> > *_diff;
#else
void *_diff;
#endif
}
...
#end
Can any problems occur when doing this? Is there a better way of doing this?
Note that the use of pointers is just to get the size of the ivar to be the same in both Objective-C and Objective-C++. The ivar can only be used from within the class implementation of SXDiff (it's #private). The implementation is written in Objective-C++.
With Apple LLVM version 2.0+ try moving all of your C++ code from the header into a class extension.
//SXDiff.h
#interface SXDiff : NSObject {
}
...
#end
//SXDiff.mm
#include "dtl/dtl.hpp"
#interface SXDiff()
{
dtl::Diff<std::string, std::vector<std::string> > *_diff;
}
#end
#implementation SXDiff
...
#end
I've got a c++ app written using Boost/WXWidgets, targeting Windows and Mac OSX. However, I've got one issue that I can't solve using these libraries. My solution requires me to wrap an Objective C class so that I can call it from one of my c++ modules. My research so far tells me that I need to use Objective C++ written into source files with a .mm extension, allowing XCode to treat the file as a mix of Objective C and c++. I've found lots of articles detailing how to wrap c++ so that it can be called from ObjectiveC, but nothing that gives any detail on the reverse. Any links to articles or, better still, a worked example, would be greatly appreciated.
If you want a reusable pure C++ wrapper around an Objective C class, the Pimpl idiom works pretty well. The Pimpl idiom will make it so that there is no Objective C / Cocoa stuff visible in the header file that will be included by pure C++ code.
// FooWrapper.hpp
// Absolutely no Cocoa includes or types here!
class FooWrapper
{
public:
int bar();
private:
struct Impl; // Forward declaration
Impl* impl;
};
// FooWrapper.mm
#import "FooWraper.hpp"
#import "Foundation/NSFoo.h"
struct FooWrapper::Impl
{
NSFoo* nsFoo;
};
FooWrapper::FooWrapper() : impl(new Impl)
{
impl->nsFoo = [[NSFoo alloc] init];
}
FooWrapper::~FooWrapper()
{
[impl->nsFoo release];
delete impl;
}
int FooWrapper::bar()
{
return [impl->nsFoo getInteger];
}
Just mix it (but don't forget setting up the pool). It works.
// obj-c code, in .mm file
void functionContainingObjC(){
NSAutoreleasePool*pool=[[NSAutoreleasePool alloc] init];
[lots of brackets!];
[pool release];
}
// c++ code, in .cc file
functionContainingObjC();
objc provides c interfaces for the basic interfaces and types (#include <objc/headers_you_need.h>. therefore, you can use these interfaces in pure c/c++ TUs. then include libs like Foundation or AppKit in the mm and use objc types and messaging in the implementation.
the following is a very basic interface which is not typesafe, but i encourage you to make it typesafe for the objc type you are wrapping. this should be enough to get you started in the right direction.
// .hpp
namespace MON {
// could be an auto pointer or a dumb pointer; depending on your needs
class t_MONSubclassWrapper {
public:
// usual stuff here...
// example wrapper usage:
void release();
private:
id d_objcInstance;
};
} /* << MON */
// .mm
...
#include <Foundation/Foundation.h>
MON::t_MONSubclassWrapper::t_MONSubclassWrapper() :
d_objcInstance([[MONSubclass alloc] init]) {
}
...
void MON::t_MONSubclassWrapper::release() {
[d_objcInstance release];
}
I have a small C++ application which I imported Objective-C classes. It works as Objective-C++ files, .mm, but any C++ file that includes a header which may end up including some Objective-C header must be renamed to a .mm extension for the proper GCC drivers.
Is there a way to write either a purely C++ wrapper for Objective-C classes or can I separate the Objective-C objects out somehow and just link them separately? Maybe even if the Objective-C classes became a small library I could statically re-link at compile time?
The problem is that this code is cross-platform, and it is more difficult to compile on systems that normally do not use Objective-C (i.e. not Macs). Even though preprocessor commands restrict any implementation of Objective-C code on Windows or Linux, the original code still has .mm extensions and GCC still treats the code as Objective-C++.
Usually you simply wrap your Objective-C classes with C++ classes by e.g. using opaque pointers and forwarding calls to C++ methods to Objective-C methods.
That way your portable C++ sources never have to see any Objective-C includes and ideally you only have to swap out the implementation files for the wrappers on different platforms.
Example:
// c++ header:
class Wrapper {
struct Opaque;
Opaque* opaque;
// ...
public:
void f();
};
// Objective-C++ source on Mac:
struct Wrapper::Opaque {
id contained;
// ...
};
void Wrapper::f() {
[opaque->contained f];
}
// ...
Yes, that's easily possible, both ways, if you know a few tricks:
1) The "id" type is actually defined in a plain C header. So you can do the following:
In your header:
#include <objc/objc.h>
class MyWindow
{
public:
MyWindow();
~MyWindow();
protected:
id mCocoaWindow;
};
In your implementation (.mm):
#include "MyWindow.h"
#include <Cocoa/Cocoa.h>
MyWindow::MyWindow()
{
mCocoaWindow = [[NSWindow alloc] init];
}
MyWindow::~MyWindow()
{
[mCocoaWindow release];
mCocoaWindow = nil;
}
2) There are two preprocessor constants you can use to exclude C++/ObjC-specific code when a source file includes them that is one of the two, but not ObjC++:
#if __OBJC__
// ObjC code goes here.
#endif /* __OBJC__*/
#if __cplusplus
// C++ code goes here.
#endif
Just be careful, you can't just add/remove ivars or virtual methods with an #ifdef, that will create two classes with different memory layouts and make your app crash in very weird ways.
3) You can use a pointer to a struct without declaring its contents:
In your header:
#interface MyCppObjectWrapper : NSObject
{
struct MyCppObjectWrapperIVars *ivars; // This is straight ObjC, no ++.
}
#end
In your implementation file (.mm):
struct MyCppObjectWrapperIVars
{
std::string myCppString1;
std::string myCppString2;
std::string myCppString3;
};
#implementation MyCppObjectWrapper
-(id) init
{
if(( self = [super init] ))
{
ivars = new MyCppObjectWrapperIVars;
}
return self;
}
-(void) dealloc
{
delete ivars;
ivars = NULL;
[super dealloc];
}
#end
This will make your header simple standard C or ObjC, while your implementation file gets constructors/destructors of all ivars called without you having to create/delete each one as an object on the heap.
This is all only the Mac side of things, but this would mean that you could keep the ObjC stuff out of your headers, or at least make it compile when used from cross-platform client files of the Mac implementation of your C++ portability layer.