从Swift访问C ++ Enum

时间:2017-08-02 04:03:10

标签: c++ objective-c swift enums bridging-header

我正在尝试从Swift访问用C ++标头编写的enum值。具体来说,我在OpenCV的hpp头文件中有这个enum我希望将其值暴露给Swift。我试图在Swift和Objective-C之间设置一个桥接头,并为我想要公开的C ++枚举值设置一个包装器,但是编译器并不高兴:

imgproc.hpp :C ++头文件

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

桥接标题

#import "OpenCVWrapper.h"

OpenCVWrapper.h :我的Objective-C Wrapper类暴露给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

如果我将此枚举声明和C ++代码(OpenCV)导入到OpenCVWrapper.mm,那么编译器就可以了,我也可以使用它,但是我想将这个枚举暴露给Swift所以它必须在头文件中。但是,当我直接在Objective-C头文件中公开C ++枚举时,有些事情是不正确的。

是否可以直接从Objective-C头部访问C ++常量/枚举,以便它可以桥接到Swift?

我已经看过使用类似thisthis的extern,但我的设置中仍然无法识别C ++常量。

2 个答案:

答案 0 :(得分:2)

OpenCV C ++库中定义的enum值旨在与同一库中定义的API一起使用,并且需要将这些API包装起来以便在Swift中使用。包装器层还可以包含用于在C ++和Swift之间转换enum的代码,以便更改C ++ enum的值不会破坏Swift代码。这是可能的,因为包装器知道Swift和C ++ enum值。

让我们说C ++头文件,称之为CPP.h,有:

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

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

实施对我们的目的并不重要。在CPPWrapper.h中,暴露给Swift的包装器API看起来像

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

这是包装器实现,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

以上代码段不是完整的源代码文件,但应该让您了解正在进行的操作。代码可以通过多种方式变得更加健壮,但这超出了简短答案的范围。

答案 1 :(得分:0)

你唯一能做的就是在你的.h文件中创建一个全新的,独立的枚举,它具有与C ++枚举相同的数值,然后在你的Objective-C ++文件中使用编译时断言(static_assert)来检查值是否相同。

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

显然,输入正确的数字,无论它们是什么。并检查.mm文件,以防原始C ++标头更改。