【问题标题】:Access C++ Enum from Swift从 Swift 访问 C++ 枚举
【发布时间】:2018-01-09 02:44:55
【问题描述】:

我正在尝试从 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++ 常量。

【问题讨论】:

  • 正如stackoverflow.com/questions/35229149/… 中指出的,桥接头文件不能处理C++。无论您是直接包含 .hpp 文件还是通过另一个 .h 文件间接包含该文件。
  • 从 C 过渡到 Swift 会更容易吗?在 OpenCV 2 中,有一个 C API for OpenCV。不幸的是,OpenCV 3 中似乎已经放弃了维护:openCV will drop C API support soon。这是 2013 年的问答。谷歌搜索“opencv c api”,我发现了一些 C API 文档。 OpenCV 3 各个类别的页面。
  • @Scheff,我想避免将来可能停止工作的解决方案,但感谢您的参考。

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


【解决方案1】:

在 OpenCV C++ 库中定义的 enum 值旨在与在同一库中定义的 API 一起使用,并且这些 API 需要被包装以便在 Swift 中使用。包装层还可以包含用于在 C++ 和 Swift 中的 enums 之间进行转换的代码,这样更改 C++ enums 的值不会破坏 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

上面的代码 sn-ps 不是完整的源代码文件,但应该让您了解发生了什么。可以通过多种方式使代码更加健壮,但这超出了简短回答的范围。

【讨论】:

  • 这基本上是我问这个问题几天后所做的。不管怎么说,还是要谢谢你。我已经回复了您的答案,但仍会保留这个问题,以便将来可能采用更简化的方法。
  • @HuaTham:你找到解决问题的方法了吗?我也很挣扎。上面提到的答案不起作用。你会提供你的解决方案,它工作正常吗?我正在使用 Swift4.2
【解决方案2】:

你唯一能做的就是在你的 .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++ 标头发生更改。

【讨论】:

  • 这就是我寻求解决方案的原因:避免对值进行硬编码。如果 C++ 代码稍后更改值定义,则该 Swift 代码将因此而中断。
猜你喜欢
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2021-05-21
  • 1970-01-01
  • 1970-01-01
  • 1970-01-01
  • 2013-12-04
相关资源
最近更新 更多