2017-08-02 123 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