I\'m coding an app for iOS and I recently #included a C++ header file in an Objective C implementation (.m) file. I changed the extension from .m to .mm and expected everything
Unfortunately, if you just start making classes .mm
, any class that uses that .mm
's header will also need to become .mm
. If you continue to just change your class extensions, you will eventually make the whole project Objective-c++. If that is your intention, then you can just change your build settings to compile for Objective-c++ (which could be a house of pain for you).
However, if you use some header magic, you will avoid a lot of hassle. Just make sure to change your Compile sources as
build property to According to file type
before compiling.
Here's something I did with a wrapper class I wrote to isolate a c++ class from the rest of my Objective-c classes. The c++ class is MyClass
.
MyClassWrapper.h
//declare c++ impl for Obj-C++
#ifdef __cplusplus
class CppPlanterModel;
namespace com{namespace company{namespace mypackage {class MyClass;}}}
typedef com::company::mypackage::MyClass CppMyClass;
#endif
//declare obj-c impl
#ifdef __OBJC__
#ifndef __cplusplus
typedef void CppMyClass;
#endif
#endif
@interface MyClassWrapper : NSObject {
CppMyClass* _myClass;
}
//etc etc
@end
MyClassWrapper.mm
#include "MyClass.h"
using namespace com:company:mypackage;
class CppMyClass : public MyClass {
CppMyClass() {};
~CppMyClass() {};
//other stuff you might like to have
};
@implementation MyClassWrapper
//etc etc
@end
Here's another thing I did with a different header to handle sharing extern
stuff:
Something.h
#ifdef __cplusplus
#define FV_EXTERN extern "C" __attribute__((visibility ("default")))
#else
#define FV_EXTERN extern __attribute__((visibility ("default")))
#endif
FV_EXTERN const int kMyInt;
FV_EXTERN int GetAnotherInt(...);
I recommend reading this blog entry about wrapping c++ (which also has links to other blog entries of a similar topic): http://robnapier.net/blog/wrapping-c-take-2-1-486
following the code at Rob Napier's blog I did it for CAAudioUnitOutputCapturer. Thought I would share it to save other folks time.
RNWrap.h
//
// RNWrap.h
//
// ObjC wrapper for Wrap.cpp
//
#import <Foundation/Foundation.h>
#import <CoreFoundation/CoreFoundation.h>
#import <AudioUnit/AudioUnit.h>
#import <AudioToolbox/AudioToolbox.h>
#import <AVFoundation/AVFoundation.h>
#import <CoreAudio/CoreAudioTypes.h>
struct RNWrapOpaque;
@interface RNWrap : NSObject {
struct RNWrapOpaque *_cpp;
}
- (id) initWithAudioUnit:(AudioUnit) au andURL:(CFURLRef) outputFileURL andAudioFileTypeID:(AudioFileTypeID) fileType andAudioStreamBasicDescription: (const AudioStreamBasicDescription) asbd;
- (void) Start;
- (void) Close;
- (void) Stop;
@end
RNWrap.mm
//
// RNWrap.mm
// Objective C++ Wrapper Class for CAAudioUnitOutputCapturer.h
//
// Created by Pier 23/10/12
// Copyright 2012 DreamUpApps. All rights reserved.
//
#import "RNWrap.h"
#import "CAAudioUnitOutputCapturer.h"
@interface RNWrap ()
@property (nonatomic, readwrite, assign) RNWrapOpaque *cpp;
@end
@implementation RNWrap
@synthesize cpp = _cpp;
struct RNWrapOpaque
{
public:
RNWrapOpaque(AudioUnit au, CFURLRef outputFileURL, AudioFileTypeID fileType, const AudioStreamBasicDescription format) : outputCapturer(au, outputFileURL, fileType, format, 0) {}; // note added bus number = 0 at the end
CAAudioUnitOutputCapturer outputCapturer;
};
- (id)initWithAudioUnit:(AudioUnit) au andURL:(CFURLRef) outputFileURL andAudioFileTypeID:(AudioFileTypeID) fileType andAudioStreamBasicDescription: (const AudioStreamBasicDescription) format
{
self = [super init];
if (self != nil)
{
self.cpp = new RNWrapOpaque(au, outputFileURL, fileType, format);
}
return self;
}
- (void)dealloc
{
delete _cpp;
_cpp = NULL;
//[super dealloc];
}
- (void) Start
{
self.cpp->outputCapturer.Start();
}
- (void) Stop
{
self.cpp->outputCapturer.Stop();
}
- (void) Close
{
self.cpp->outputCapturer.Close();
}
@end
You use it like this in your class.
- (void) captureInAppAudio:(AudioUnit) auToCapture
{
AudioStreamBasicDescription destFormat;
memset( &destFormat, 0, sizeof(AudioStreamBasicDescription) );
destFormat.mSampleRate = 44100;
destFormat.mFormatID = kAudioFormatLinearPCM;
destFormat.mFormatFlags = ( kAudioFormatFlagIsSignedInteger | kAudioFormatFlagIsPacked | kAudioFormatFlagIsBigEndian );
destFormat.mBytesPerPacket = 2;
destFormat.mFramesPerPacket = 1;
destFormat.mBytesPerFrame = 2;
destFormat.mChannelsPerFrame = 1;
destFormat.mBitsPerChannel = 16;
NSArray *paths = NSSearchPathForDirectoriesInDomains(NSDocumentDirectory, NSUserDomainMask, YES);
NSString *documentsDirectory = [paths objectAtIndex:0];
soundPath = [documentsDirectory stringByAppendingString:@"/recording.caf"] ;
CFURLRef fileurl = CFURLCreateWithFileSystemPath(NULL, (CFStringRef)soundPath, kCFURLPOSIXPathStyle, false);
captor = [[RNWrap alloc] initWithAudioUnit:auToCapture andURL:fileurl andAudioFileTypeID:'caff' andAudioStreamBasicDescription:destFormat];
[captor Start];
}
Hope this helps someone else out there!
Pier.