2017-08-02 122 views
2

我想从Swift中访问用C++头文件编写的enum的值。具体来说,我在OpenCV的hpp头文件中有这个enum,我想将它的值暴露给Swift。我试图建立斯威夫特和Objective-C之间的桥接报头,并把包装围绕C++枚举值我想揭露,但是编译器不乐意了:从Swift访问C++枚举

imgproc.hpp: C++的头文件

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

桥接报头

#import "OpenCVWrapper.h" 

OpenCVWrapper.h:我的目标C WRA PPER类暴露于斯威夫特

#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?

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

+1

正如在https://stackoverflow.com/questions/35229149/interacting-with-c-classes-from-swift中指出的,桥接头文件*不能处理C++。如果直接或间接通过另一个.h文件包含.hpp文件,则无关紧要。 –

+0

从C到Swift会更容易吗?在OpenCV 2中,有一个[OpenCV的C API](http://docs.opencv.org/2.4/modules/core/doc/old_basic_structures.html)。不幸的是,OpenCV 3中的维护似乎已经下降:[openCV很快就会降低C API支持](http://answers.opencv.org/question/17546/opencv-will-drop-c-api-support-soon/ )。这是从2013年开始的问答。使用Google搜索“opencv c api”,我找到了一些C API文档。页面的单个类的OpenCV 3. – Scheff

+0

@Scheff,我想避免将来可能停止工作的解决方案,但感谢您的参考。 – HuaTham

回答

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++头部发生变化。

+0

这就是我寻求解决方案来做到这一点的原因:避免对值进行硬编码。如果C++代码稍后更改了值定义,那么Swift代码将因此而中断。 – HuaTham

1

在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(); 
}; 

实现不是我们的目的很重要。该包装的API,在CPPWrapper.h,暴露斯威夫特可以像

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 

上面的代码片段是不完整的源代码文件,但应该给你了解发生了什么。代码可以通过多种方式变得更加健壮,但这超出了简短答案的范围。

+0

这几乎是我在问问题几天后所做的。不管怎么说,还是要谢谢你。我已经回复了你的答案,但仍将继续保持这个问题的可能性,以便将来可能采取更简化的方法。 – HuaTham