Properly use Objective C++ - objective-c

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 to run smoothly.
Unexpectedly I got multiple compiler errors in the .h file of my C++ class.
Such as: "C++ requires a type specifier for all declarations" and "Duplicate member...".
Does anyone know what could be causing this?
Edit - I've added the C++ header file for context:
#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__

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.

Related

Wrapping a C API in Objective-C

I'm trying to wrap a C API in Objective-C but am getting EXC_BAD_ACCESS in objc_release.
Field.h:
#ifdef __cplusplus
extern "C" {
#endif
struct sc_field
{
char *title;
};
typedef struct sc_field sc_field_t;
sc_field_t* sc_create_field();
void sc_destroy_field(sc_field_t *field);
const char* sc_get_title(const sc_field_t *field);
void sc_set_title(sc_field_t *field, const char *title);
#ifdef __cplusplus
}
#include <string>
class Field
{
public:
explicit Field();
virtual ~Field();
std::string title() const;
void setTitle(const std::string &title);
private:
class Private;
Private *d;
};
Field.c:
#include <stdio.h>
#include <stdlib.h>
#include <string.h>
#include "Field.h"
sc_field_t* sc_create_field()
{
sc_field_t *field = (sc_field_t*)malloc(sizeof(sc_field_t));
field->title = (char*)calloc(1, sizeof(char));
return field;
}
void sc_destroy_field(sc_field_t *field)
{
if (field)
{
free(field->title);
free(field);
}
}
const char* sc_get_title(const sc_field_t *field)
{
if (!field)
{
fprintf(stderr, "%s: fatal error, field is NULL\n", __FUNCTION__);
return NULL;
}
return field->title;
}
void sc_set_title(sc_field_t *field, const char *title)
{
if (!field)
{
fprintf(stderr, "%s: fatal error, field is NULL\n", __FUNCTION__);
return;
}
field->title = (char*)realloc(field->title, sizeof(char) * sizeof(title));
strcpy(field->title, title);
}
SCField.h:
#import "Field.h"
#import <Foundation/Foundation.h>
#interface SCField : NSObject
{
#private
sc_field_t *field;
}
#property (nonatomic, copy) NSString *title;
- (id)init;
- (id)initWithTitle:(NSString *)title;
- (void)dealloc;
#end
SCField.m:
#import "SCField.h"
#implementation SCField
#synthesize title;
- (id)init
{
self = [super init];
if (self)
{
field = sc_create_field();
}
return self;
}
- (id)initWithTitle:(NSString *)aTitle
{
self = [self init];
if (self)
{
[self setTitle:aTitle];
}
return self;
}
- (void)dealloc
{
sc_destroy_field(field);
[super dealloc];
}
- (NSString *)title
{
return [NSString stringWithUTF8String:sc_get_title(field)];
}
- (void)setTitle:(NSString *)aTitle
{
sc_set_title(field, [aTitle UTF8String]);
}
#end
Test:
SCField *field = [[SCField alloc] initWithTitle:#"Hello world!"];
NSLog(#"%#\n", [field title]);
[field release];
The code seems to work fine until I release my SCField instance. If I remove the title and setTitle: implementations as if I were storing an NSString* directly in the SCField the code works fine (I tried assign instead of copy in the #property, too - no luck). What am I doing wrong?
I think the issue is that calling UTF8String will give you memory that will be autoreleased (according to the documentation).
Rather than using realloc() in sc_set_title(), just malloc() a new buffer and copy it. Also, your realloc() parameters are incorrect anyway, so it probably ends up being a no-op. You need to allocate strlen(title) + 1; sizeof won't do anything for you since you're just asking the compiler to give you 1 (sizeof(char)) * 4 bytes (sizeof(title) - a pointer).
Use getCString:maxLength:encoding:. To get length of string in bytes for a buffer, use lengthOfBytesUsingEncoding: (takes O(n) time) or maximumLengthOfBytesUsingEncoding: (takes O(1) time).

C++ Callbacks with objective-C, an application to RtMidi

I've read several of the previous discussion about the subject but since I'm relatively new to Objective-C, I don't really understand them. (Blocks, selectors, and delegate are very new to me).
Here is the situation:
I'm using those abstract class RtMidi that are written in c++
void MidiInApi :: setCallback( RtMidiIn::RtMidiCallback callback, void *userData )
{
if ( inputData_.usingCallback ) {
errorString_ = "MidiInApi::setCallback: a callback function is already set!";
RtMidi::error( RtError::WARNING, errorString_ );
return;
}
if ( !callback ) {
errorString_ = "RtMidiIn::setCallback: callback function value is invalid!";
RtMidi::error( RtError::WARNING, errorString_ );
return;
}
inputData_.userCallback = (void *) callback;
inputData_.userData = userData;
inputData_.usingCallback = true;
}
I also have a class that Reciever that implements that callback (rtMidiCallback).
And at some point in my main file I need to write something like this
midiIn->setCallback(rtMidiCallback);
This Would be in a c++ context but in Objective-C I have no idea how to do it.
If you have any ideas, pointers I would be glad to discuss about it with you. Thank you very much
Best,
greg.
It's possible to have C++ and Objective-C within the same project and even within the same file (Objective-C++ with file extension .mm). If the code you're using is designed for a C++ callback, it may be easier to provide that and communicate between different object types outside of that mechanism.
// CPPClass.h
#ifndef CPPClass_h
#define CPPClass_h
class CPPClass {
private:
id m_ocObject;
public:
CPPClass(id ocObject);
virtual ~CPPClass();
void verifyCPP();
};
#endif
// OCClass.h
#import <Foundation/Foundation.h>
class CPPClass;
#interface OCClass : NSObject {
CPPClass *cppObject;
}
- (void)ocCallback;
- (void)verifyOC;
#end
// OCClass.mm
#import "OCClass.h"
#import "CPPClass.h"
#include <iostream>
#implementation OCClass
- (id)init {
self = [super init];
if (self) {
cppObject = new CPPClass(self);
}
return self;
}
- (void)dealloc {
delete cppObject;
}
- (void)ocCallback {
NSLog(#"Objective-C called from C++");
}
- (void)verifyOC {
NSLog(#"Objective-C called from Objective-C");
cppObject->verifyCPP();
}
#end
CPPClass::CPPClass(id ocObject) : m_ocObject(ocObject)
{}
CPPClass::~CPPClass() {}
void CPPClass::verifyCPP() {
std::cout << "C++ called from Objective-C" << std::endl;
[m_ocObject ocCallback];
}
To activate this, some other .mm file would have:
#import "OCClass.h"
...
OCClass *test = [[OCClass alloc] init];
[test verifyOC];

how to create and allocate a C buffer as part of an objective C class

best to explain with an example:
in my AudioItem.h
#define ITEM_CAPACITY 100
typedef struct DataStruct {
void * content;
UInt32 size;
} DataStruct;
typedef DataStruct *DataStructRef;
#interface AudioItem : NSObject
{
DataStructRef data;
}
#property (assign, readwrite) DataStructRef data;
in AudioItem.m
#synthesize data;
-(id)initWithID:(NSString *)itemID
{
self = [super init];
data->content = malloc(ITEM_CAPACITY);
return self;
}
The above code looks a lot like this one, but I get a BAD_EXEC_ERROR.. how come? The reason why I would like to use a C buffer rather than some NSMutableData or whatever is b/c I've tried using NSMutableData and I feel like it's slowing down my real time application
it fails because data is a null pointer when you set its content.
the easy way to do this is:
enum { ITEM_CAPACITY = 100 };
typedef struct DataStruct {
char content[ITEM_CAPACITY];
UInt32 size;
} DataStruct;
#interface AudioItem : NSObject
{
#private
DataStruct data;
}
#implementation AudioItem
- (id)initWithID:(NSString *)itemID
{
self = [super init];
if (0 == self) return;
data.size = ITEM_CAPACITY;
return self;
}

passing the static variable as an argument->Objective C

c++ code
--------
#include "stdafx.h"
#include <iostream>
using namespace std;
class A
{
public:
static int a;
void set(int s)
{
a=s;
cout<<a<<endl;
}
void setData(int f)
{
cout<<"I am "<<f<<" years old!!!"<<endl;
}
};
int A::a=0;
int main()
{
A* ab=new A();
ab->set(10);
ab->setData(ab->a);
return 0;
}
I am trying to get the same output for
this equivalent Objective C code.
main.m
---------
#import <Foundation/Foundation.h>
#import "A.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
A* ab = [[A alloc]init];
[ab set:10];
[ab setData:ab.a]; //getting error when passed ab->a or ab.a as an argument
[pool drain];
return 0;
}
A.h
---
#import <Foundation/Foundation.h>
#interface A : NSObject {
}
-(void)set:(int)s;
-(void)setData:(int)f;
#end
A.m
----
#import "A.h"
#implementation A
static int a;
-(void)set:(int)s
{
a=s;
NSLog(#"%d\n",a);
}
-(void)setData:(int)f
{
NSLog(#"%d",f);
}
#end
Error:Request for member 'a' in something not a structure or union.
There are no static instance variables or methods in Objective C. What you want can be done with class methods and static file scope variables. Class methods are those methods sent to class objects rather than instances.
#interface AClass
{
}
+(int) a;
+(void) setA: (int) newValue;
#end
// A.m
static int aStorage = 0;
#implementation AClass
+(int) a
{
return aStorage;
}
+(void) setA: (int) newValue
{
aStorage = newValue;
}
#end
// To use:
int something = [AClass a];
[AClass setA: something * 2];
// Or dot syntax if you prefer
AClass.a = AClass.a * 2;
First, declaring a static int in your implementation file doesn't magically make it a member of class A; your ObjC class A has no member variables.
Second, ab->a isn't how you would access the member of a class in ObjC. Once you have a member, write a getter for a, and use a method call to access it. (Or if you really want it to be static, don't add a member, and just add a getter that returns the static variable.)

static behaviour->objective C

I have this static declaration of m_pData=1099656 in a class.
In main NSLog(#"Testing:%d",m_pData);
Its printing me 0.
How to get the same value of m_pData in the class in the main function too.
I cant use obj.m_pData or obj->m_pData to acces the variable.
EDITED:
test2.m
----------
#import <Foundation/Foundation.h>
#import "data_derived.h"
int main (int argc, const char * argv[]) {
NSAutoreleasePool * pool = [[NSAutoreleasePool alloc] init];
data* dat = [data alloc];
requestSession* session = [requestSession alloc];
[session init];
[dat TxCreateImage:RM_REQUEST_SESSION];
NSLog(#"Testing:%d",m_pData); //the static variable is not printing the value its holding.Its printing Zero.If printed the same variable inside the class it gives some numbers.
[dat dataBuffer:&m_pData withLen:&m_uDataSize]; //here the actual values of static values are not passed.when printed both of them contains zero values.
[pool drain];
return 0;
}
data.h
--------
#import <Foundation/Foundation.h>
#import "remote.h"
static int m_nMessageId; //Message ID
static int m_uSessionId; //Session ID
static int m_chSequenceChar; //Sequence ID
static int* m_pData; //Integer buffer to carry data
static int m_uDataSize; //Datasize
#interface data : NSObject {
#public
}
- (id)initWithID:(int) uMessageId withData:(id)pData withSize:(size_t) uDataSize;
+ (void)initialize;
- (void)dealloc;
- (id) dataBuffer:(int**)m_Data withLen:(int**)uLen;
- (BOOL) TxCreateImage:(int)messageId;
#end
data.m
---------
#import "data.h"
#define ENCODED_MSG_DATA_OFFSET 8
#implementation data
+ (void)initialize
{
m_uSessionId = 0;
m_chSequenceChar= 0;
// Initialize values from derived class
m_nMessageId = 0;
m_pData = 0;
m_uDataSize = 0;
}
- (id) initWithID:(int) uMessageId withData:(id)pData withSize:(size_t) uDataSize
{
if(self=[super init])
{
// Initialize the member variables
m_uSessionId = 0xFF;
m_chSequenceChar= 10;
// Initialize values from derived class
m_nMessageId = uMessageId;
m_pData = (int*)pData;
m_uDataSize = (int)uDataSize;
}
NSLog(#"Data size:%d",uDataSize);
NSLog(#"m_pData:%d",m_pData);
NSLog(#"pData:%d",pData);
return self;
}
- (id) dataBuffer:(int**)m_Data withLen:(int**)uLen
{
if ( m_uDataSize <= RMH_MAX_ENCODED_LENGTH )
{
int abBigEndian[RMH_MESSAGE_MAX_SIZE];
memcpy(abBigEndian,m_Data,m_uDataSize);
NSLog(#"m_Data:%d",*m_Data);
NSLog(#"abBigEndian:%d",abBigEndian);
uLen += ENCODED_CRC_BYTE_LEN + 1;
NSLog(#"%d",*uLen);
}
NSLog(#"END!");
return self;
}
- (BOOL) TxCreateImage:(int)messageId
{
char pData[4096];
sprintf(pData,"%x %d %d %d %x",ASCII_STX,m_uSessionId,m_chSequenceChar,m_nMessageId,ASCII_ETX); //uLen = ENCODED_MSG_DATA_OFFSET;
NSLog(#"%s",pData);
return YES;
}
- (void)dealloc
{
[super dealloc];
}
#end
data_derived.h
---------------------
#import <Foundation/Foundation.h>
#import "data.h"
#define DECLARE_RS232_NEWMSG(ClassID)\
enum \
{ \
ID = ClassID \
}; \
#interface requestSession : data {
#public
DECLARE_RS232_NEWMSG(RM_REQUEST_SESSION);
struct RMH_REQUEST_SESSION_MSG st;
}
-(id)init;
-(void)dealloc;
#end
data_derived.m
---------------------
#import "data_derived.h"
#implementation requestSession
- (id)init
{
size_t asize = sizeof(st);
st.uDeviceID = RS232_PROTOCOL_DEVICE_ID;
st.uProtocolVersion = RS232_VERSION;
memset(st.uReserved,0x00,sizeof(st.uReserved));
NSLog(#"Address of the structure:%d",&st);
self=[super initWithID:ID withData:(id)&st withSize:asize];
if (self) {
}
return self;
}
- (void)dealloc
{
[super dealloc];
}
#end
Don't place your static members in .h file. Move them to data.m.
Also about static keyword - in C (and Objective C, which is clear superset to C), when using static with global variables (like you do), it only indicates that this variables will be local to the file you declared these variable in. So global variables anyway have only one instance, with static modifier or without. If you don't use static, then you can access these variables from other files with external declaration, like:
// file1.m
int variable = 4;
// file2.m
external int variable; // variable == 4
That's why your code is printing you 0. m_pData in test2.m is not the same m_pData you have in data.m. Without static modifier you would get a linker error.
And you might want to write getters/setters for static members. Like:
+ (int *)pData {
return m_pData; // or smth like memcpy
}