Правильно используйте Objective C++
Я пишу приложение для iOS и недавно # включил заголовочный файл C++ в файл реализации Objective C (.m). Я изменил расширение с.m на.mm и ожидал, что все будет работать гладко.
Неожиданно я получил несколько ошибок компилятора в файле.h моего класса C++.
Например: "C++ требует спецификатора типа для всех объявлений" и "Duplicate member...".
Кто-нибудь знает, что может быть причиной этого?
Изменить - я добавил заголовочный файл C++ для контекста:
#ifndef __CAAudioUnitOutputCapturer_h__
#define __CAAudioUnitOutputCapturer_h__
#include <AudioToolbox/ExtendedAudioFile.h>
/*
Class to capture output from an AudioUnit for analysis.
example:
CFURL fileurl = CFURLCreateWithFileSystemPath(NULL, CFSTR("/tmp/recording.caf"), kCFURLPOSIXPathStyle, false);
CAAudioUnitOutputCapturer captor(someAU, fileurl, 'caff', anASBD);
{
captor.Start();
...
captor.Stop();
} // can repeat
captor.Close(); // can be omitted; happens automatically from destructor
*/
class CAAudioUnitOutputCapturer {
public:
enum { noErr = 0 };
CAAudioUnitOutputCapturer(AudioUnit au, CFURLRef outputFileURL, AudioFileTypeID fileType, const AudioStreamBasicDescription &format, UInt32 busNumber = 0) :
mFileOpen(false),
mClientFormatSet(false),
mAudioUnit(au),
mExtAudioFile(NULL),
mBusNumber (busNumber)
{
CFShow(outputFileURL);
OSStatus err = ExtAudioFileCreateWithURL(outputFileURL, fileType, &format, NULL, kAudioFileFlags_EraseFile, &mExtAudioFile);
if (!err)
mFileOpen = true;
}
void Start() {
if (mFileOpen) {
if (!mClientFormatSet) {
AudioStreamBasicDescription clientFormat;
UInt32 size = sizeof(clientFormat);
AudioUnitGetProperty(mAudioUnit, kAudioUnitProperty_StreamFormat, kAudioUnitScope_Output, mBusNumber, &clientFormat, &size);
ExtAudioFileSetProperty(mExtAudioFile, kExtAudioFileProperty_ClientDataFormat, size, &clientFormat);
mClientFormatSet = true;
}
ExtAudioFileWriteAsync(mExtAudioFile, 0, NULL); // initialize async writes
AudioUnitAddRenderNotify(mAudioUnit, RenderCallback, this);
}
}
void Stop() {
if (mFileOpen)
AudioUnitRemoveRenderNotify(mAudioUnit, RenderCallback, this);
}
void Close() {
if (mExtAudioFile) {
ExtAudioFileDispose(mExtAudioFile);
mExtAudioFile = NULL;
}
}
~CAAudioUnitOutputCapturer() {
Close();
}
private:
static OSStatus RenderCallback( void * inRefCon,
AudioUnitRenderActionFlags * ioActionFlags,
const AudioTimeStamp * inTimeStamp,
UInt32 inBusNumber,
UInt32 inNumberFrames,
AudioBufferList * ioData)
{
if (*ioActionFlags & kAudioUnitRenderAction_PostRender) {
CAAudioUnitOutputCapturer *This = (CAAudioUnitOutputCapturer *)inRefCon;
static int TEMP_kAudioUnitRenderAction_PostRenderError = (1 << 8);
if (This->mBusNumber == inBusNumber && !(*ioActionFlags & TEMP_kAudioUnitRenderAction_PostRenderError)) {
OSStatus result = ExtAudioFileWriteAsync(This->mExtAudioFile, inNumberFrames, ioData);
if (result) DebugMessageN1("ERROR WRITING FRAMES: %d\n", (int)result);
}
}
return noErr;
}
bool mFileOpen;
bool mClientFormatSet;
AudioUnit mAudioUnit;
ExtAudioFileRef mExtAudioFile;
UInt32 mBusNumber;
};
#endif // __CAAudioUnitOutputCapturer_h__
2 ответа
К сожалению, если вы только начинаете делать уроки .mm
любой класс, который использует это .mm
Заголовок также должен стать .mm
, Если вы продолжите просто изменять расширения вашего класса, вы в конечном итоге создадите весь проект Objective-C++. Если это ваше намерение, то вы можете просто изменить настройки сборки для компиляции для Objective-C++ (что может быть для вас болезненным).
Однако, если вы используете магию заголовка, вы избежите многих хлопот. Просто не забудьте изменить свой Compile sources as
построить недвижимость для According to file type
до компиляции.
Вот что я сделал с классом-оберткой, который я написал, чтобы изолировать класс C++ от остальных моих классов Objective-c. C++ класс 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
Вот еще одна вещь, которую я сделал с другим заголовком для управления совместным использованием extern
материал:
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(...);
Я рекомендую прочитать эту запись в блоге об упаковке C++ (которая также содержит ссылки на другие записи в блогах по аналогичной теме): http://robnapier.net/blog/wrapping-c-take-2-1-486
Следуя коду в блоге Роба Напира, я сделал это для CAAudioUnitOutputCapturer. Я думал, что поделюсь этим, чтобы сэкономить время других людей.
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
Вы используете это в своем классе.
- (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];
}
Надеюсь, что это поможет кому-то еще там!
Пирс.