【问题标题】:Properly use Objective C++正确使用 Objective C++
【发布时间】:2012-05-10 17:40:38
【问题描述】:

我正在为 iOS 编写一个应用程序,我最近在一个 Objective C 实现 (.m) 文件中#included了一个 C++ 头文件。我将扩展名从 .m 更改为 .mm,并希望一切运行顺利。

我的 C++ 类的 .h 文件中出现多个编译器错误。

如:“C++ 要求所有声明的类型说明符”和“重复成员...”。

有人知道是什么原因造成的吗?

编辑 - 我为上下文添加了 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__

【问题讨论】:

  • 有什么具体的例子吗?我猜某些类型尚未定义,或者与其中一种 Objective-C 运行时类型存在名称冲突。但这就是问题所在,我只能猜测。
  • @pmjordan 我刚刚添加了有问题的头文件。也许这会有所帮助。
  • 请告诉我们您收到的实际错误信息;没有人愿意通过数百行代码来猜测哪一行可能是错误的!将 .mm 文件提供给我们也会有所帮助(或者至少向我们展示您在此 .h 文件之前还包含哪些内容)。

标签: objective-c ios xcode objective-c++


【解决方案1】:

按照 Rob Napier 博客中的代码,我为 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];
}

希望这对其他人有帮助!

码头。

【讨论】:

    【解决方案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(...);
    

    我推荐阅读这篇关于 wrapping c++ 的博客文章(其中也有类似主题的其他博客文章的链接):http://robnapier.net/blog/wrapping-c-take-2-1-486

    【讨论】:

    • 只要你在头文件中没有任何 C++ 特定的东西,你就不需要把其他所有东西都变成 Objective-C++。
    • 如果 c++ 标头非常简单,那么是的。正如我在回答中提到的那样,您可能只需将文件更改为 .mm 并更新编译器设置即可。
    • 感谢您的回答@TReddy。经过一些研究后,我阅读了它,这使我尝试将我的编译器设置和我的 .m 文件都更改为 .mm,但都没有奏效。我只是包含了我的 c++ 类的标题,以防添加任何视角。我将尽快为您提供包装解决方案!
    • @TReddy 根据您提供给我的链接和我的研究,看起来您的回答应该可以解决问题。然而,这是我第一次接触 C++,在理解如何在我的案例中实现您的解决方案时,我肯定遇到了挑战! +1虽然。谢谢。
    • @TReddy 在 Rob Napier 的博客中,您将我指向一位评论者问这个问题:“为什么不直接将类声明为结构?C++ 类和结构是兼容的,因此您可以简单地执行以下操作:'struct裹;'在你的objective-c++头文件中,而不是必须为它声明一个包装器。C(Objective-c)会将它视为一个结构,而C++会将它视为一个类。 Rob 同意,如果类不使用命名空间,这将起作用,并且“现在您可以在 @ 实现块中声明 ivars,不再需要这种技术的大部分”。你能帮我解释清楚吗?
    猜你喜欢
    • 1970-01-01
    • 1970-01-01
    • 2013-02-03
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多