Logo Questions Linux Laravel Mysql Ubuntu Git Menu
 

Access C++ Enum from Swift

I am trying to access enum's values written in C++ header from Swift. Specifically, I have this enum in OpenCV's hpp header file that I would like to expose its values to Swift. I have attempted to set up a bridging header between Swift and Objective-C, and put a wrapper around C++ enum values I would like to expose, but the compiler isn't happy about it:

imgproc.hpp: The C++ header file

enum ThresholdTypes {
    THRESH_BINARY     = 0,
    THRESH_BINARY_INV = 1,
    THRESH_TRUNC      = 2, 
    ...
};

bridging header:

#import "OpenCVWrapper.h"

OpenCVWrapper.h: My Objective-C Wrapper class to be exposed to Swift

#ifdef __cplusplus
#import <opencv2/core.hpp>
#import <opencv2/imgproc.hpp>
#endif

#import <Foundation/Foundation.h>

@interface OpenCVWrapper : NSObject

typedef enum {
    Binary = cv::THRESH_BINARY,  // ERROR: use of undeclared identifier `cv`
    BinaryInv = cv::THRESH_BINARY_INV  // ERROR: use of undeclared identifier `cv`
} ThresholdingType;

...    

@end

If I move this enum declaration and C++ code (OpenCV) import into OpenCVWrapper.mm then the compiler is ok with it, and I can also use it just fine, but I want to expose this enum to Swift so it has to be in the header file. However, something is not right when I expose the C++ enum directly in Objective-C header.

Is it possible to access C++ constants / enums directly from Objective-C header, in such a way that it can be bridge to Swift?

I have looked at using extern like this and this but the C++ constants are still not recognized in my setup.

like image 773
HuaTham Avatar asked Oct 18 '22 08:10

HuaTham


1 Answers

The enum values defined in OpenCV C++ library are intended to be used with APIs defined in the same library, and those APIs will need to be wrapped for use in Swift. The wrapper layer can also include code for translating between enums in C++ and Swift in such a way that changing values of C++ enums won't break Swift code. This is possible because the wrapper knows about both Swift and C++ enum values.

Let's say the C++ header file, call it CPP.h, has:

namespace cv {
    enum ThresholdTypes { 
        THRESH_BINARY     = 0,
        THRESH_BINARY_INV = 111,
        THRESH_TRUNC      = 222
    };

    void useThreshold(ThresholdTypes t);
    ThresholdTypes returnThreshold();
};

The implementation is not important for our purposes. The wrapper API, in CPPWrapper.h, exposed to Swift can look like

typedef enum {
    THRESH_BINARY,
    THRESH_BINARY_INV,
    THRESH_TRUNC,
    THRESH_UNKNOWN
} ThresholdTypesWrapper;

@interface CPPWrapper : NSObject

// The wrapper API operates in terms of wrapper `enum` values only.
// Translation between these and C++ `enum`s happens in the wrapper
// implementation.
+(void)useThreshold: (ThresholdTypesWrapper)thresholdType;
+(ThresholdTypesWrapper)returnThreshold;

@end

And here is the wrapper implementation, CPPWrapper.mm:

cv::ThresholdTypes thresholdWrapped2Native(ThresholdTypesWrapper t) {
    if (t==THRESH_BINARY) return cv::THRESH_BINARY;
    else if (t==THRESH_BINARY_INV) return cv::THRESH_BINARY_INV;
    else if (t==THRESH_TRUNC) return cv::THRESH_TRUNC;
    // This should be very unlikely.
    else throw std::runtime_error("Unknown threshold value detected.");
}

ThresholdTypesWrapper thresholdNative2Wrapped(cv::ThresholdTypes t) {
    if (t==cv::THRESH_BINARY) return THRESH_BINARY;
    else if (t==cv::THRESH_BINARY_INV) return THRESH_BINARY_INV;
    else if (t==cv::THRESH_TRUNC) return THRESH_TRUNC;
    // We could throw instead, but returning unknown is more forgiving if
    // a new C++ enum value is added.
    else return THRESH_UNKNOWN;
}

@implementation CPPWrapper

+(void)useThreshold: (ThresholdTypesWrapper)thresholdType {
    cv::useThreshold(thresholdWrapped2Native(thresholdType));
}

+(ThresholdTypesWrapper)returnThreshold {
    return thresholdNative2Wrapped(cv::returnThreshold());
}

@end

The above code snippets are not complete source code files, but should give you an idea of what's going on. The code could be made more robust in a number of ways, but that's beyond the scope of a short answer.

like image 121
Anatoli P Avatar answered Nov 01 '22 06:11

Anatoli P