Jake Crastly Jake Crastly - 1 month ago 9
Objective-C Question

How do I use a C++ static library in Obj-C?

I have a c++ static libray:

.a
file and
staticLibrary.h
file.

In the .h file, there is a class I want to access:

typedef enum
{
eStaticLibOperationUnknown = 0
eStaticLibOperationSystemCheck = 1
} enumStaticLibOperation;

typedef enum
{
eStaticLibResultUnknown = 0,
eStaticLibResultNullParameter = 4,
eStaticLibResultWrongParameter = 5
} enumStaticLibResult;

typedef std::function<void(void)> typeCallBack;

class classResultHelper
{
blah blah
};

class staticLibrary
{
public:
staticLibrary(typeCallBack, const char*);

void requestOperation(const char*, size_t);
void requestOperation(enumStaticLibOperation, const char*, size_t);
enumStaticLibResult getResult(char**, size_t*);
};


I used
#import "staticLibrary.h"
at the top of my viewController.m file. This raised an error as it recognized the C++ to be foreign. I then changed the viewController to a .mm extension, making the file Objective-C++ and removing the error.

But when I try to run
staticLibrary* sL = [[staticLibrary alloc] init];
in viewDidLoad, I get an error at the second staticLibrary on the right side. It says "receiver type is not an objective-c class". What am I doing wrong?

When looking at the documentation for using the static library it says:

1.1. new staticLibrary(callback, “en”);
1.2. requestOperation(“enumSystemcheck”, NULL, 0);
1.3. callback();
1.4. getResult(... , ...);"


I believe this is Java (?), and the first line is to make an instance of staticLibrary with those parameters. How would I do that in objective-C?

Answer

The code you have in the example isn't Java, it's C++. ObjC++ means that you can mix statements made in C++ or ObjC on a line, it doesn't mean you can use C++ objects as if they were ObjC objects, or with the ObjC syntax.

What people usually do is only include C++ headers and write C++ code in the .mm file itself, and not put any in the header. Write an ObjC class that wraps the part of your C++ library that you want. So in your example, that'd be something like:

JCRStaticLibrary.h

@interface JCRStaticLibrary : NSObject
-(instancetype) initWithCallback: (void(^)(void))inObjCCallback;
-(void) requestOperation: (NSString*)what withBuffer: (void*)buf size: (int)theSize;
@end

JCRStaticLibrary.mm

#import "JCRStaticLibrary.h"
#include "staticLibrary.h"

@interface JCRStaticLibrary ()
{
    staticLibrary *_cppStaticLibrary;
}
@end

@implementation JCRStaticLibrary

-(instancetype) initWithCallback: (void(^)(void))inObjCCallback
{
    self = [super init];
    if( self )
    {
        _staticLibrary = new staticLibrary( inObjCCallback, "en"); // ObjC++ knows how to turn an ObjC block into a C++ lambda.
        // Under the hood it generates code like:
        // _staticLibrary = new staticLibrary( [inObjCCallback](){ inObjCCallback(); }, "en");
        // Where the [inObjCCallback](){} part is how C++ does lambdas, its equivalent to blocks.
        // I.e. it's roughly analogous to ObjC's ^(){}.
    }
}

-(void) dealloc
{
    delete _staticLibrary;
}

-(void) requestOperation: (NSString*)what withBuffer: (void*)buf size: (int)theSize
{
    _staticLibrary->requestOperation( [what UTF8String], buf, theSize );
}

@end

Something like that. I don't know what the parameters to requestOperation in your case actually are, so I just made an educated guess.

Instead of passing in the ObjC block to -initWithCallback:, you could also write your own lambda and make it a method, i.e.:

-(instancetype) init
{
    __weak JCRStaticLibrary *weakSelf = self; // Avoid retain circle.
    _staticLibrary = new staticLibrary( [weakSelf](){ [weakSelf doCallbackThing]; }, "en"); // ObjC++ knows how to turn an ObjC block into a C++ lambda.
}

-(void) doCallbackThing
{
    // Do whatever the callback should do here.
}

It really depends on whether the callback changes every time you create an object of this type (e.g. if a JCRStaticLibrary object represents a command sent over the network) or comes from a small set of commands used over and over again (e.g. you receive a video frame and apply a filter to it, then hand it off to another object, so you really only ever have one callback). In the former case you wanna keep the block, in the latter, having a subclass for each filter might make more sense (unless you want to switch between filters on-the-fly).