स्विफ्ट से सी ++ कक्षाओं के साथ बातचीत


86

मेरे पास C ++ में लिखित कक्षाओं का एक महत्वपूर्ण पुस्तकालय है। मैं स्विफ्ट कोड के रूप में उन्हें फिर से लिखने के बजाय स्विफ्ट के भीतर किसी प्रकार के पुल के माध्यम से उनका उपयोग करने की कोशिश कर रहा हूं। प्राथमिक प्रेरणा यह है कि C ++ कोड एक मुख्य पुस्तकालय का प्रतिनिधित्व करता है जो कई प्लेटफार्मों पर उपयोग किया जाता है। प्रभावी रूप से, मैं बस ओएस एक्स के तहत काम करने के लिए कोर कार्यक्षमता की अनुमति देने के लिए एक स्विफ्ट आधारित यूआई बना रहा हूं।

अन्य प्रश्न पूछ रहे हैं, "मैं स्विफ्ट से C ++ फ़ंक्शन कैसे कॉल कर सकता हूं।" यह मेरा सवाल नहीं है । C ++ फ़ंक्शन को ब्रिज करने के लिए, निम्न कार्य ठीक है:

"C" के माध्यम से एक ब्रिजिंग हेडर को परिभाषित करें

#ifndef ImageReader_hpp
#define ImageReader_hpp

#ifdef __cplusplus
extern "C" {
#endif

    const char *hexdump(char *filename);
    const char *imageType(char *filename);

#ifdef __cplusplus
}
#endif

#endif /* ImageReader_hpp */

स्विफ्ट कोड अब सीधे फ़ंक्शन को कॉल कर सकता है

let type = String.fromCString(imageType(filename))
let dump = String.fromCString(hexdump(filename))

मेरा प्रश्न अधिक विशिष्ट है। मैं स्विफ्ट के भीतर से C ++ क्लास को तत्काल कैसे हेरफेर और हेरफेर कर सकता हूं ? मुझे इस पर प्रकाशित कुछ भी नहीं मिल रहा है।


8
मैंने व्यक्तिगत रूप से सादे ऑब्जेक्टिव-सी ++ रैपर फ़ाइलों को लिखने का सहारा लिया है जो एक ऑब्जेक्टिव-सी क्लास को उजागर करता है जो सभी प्रासंगिक सी ++ कॉल को पुन: उत्पन्न करता है और बस उन्हें सी ++ वर्ग के एक आयोजित उदाहरण के लिए फॉरवर्ड करता है। मेरे मामले में C ++ क्लासेस और कॉल की संख्या कम है, इसलिए यह विशेष रूप से श्रम गहन नहीं है। लेकिन मैं इस उम्मीद में एक जवाब के रूप में इस बात की वकालत करूंगा कि कोई बेहतर काम करेगा।
टॉमी

1
खैर, यह कुछ है ... चलो प्रतीक्षा करें और देखें (और आशा)।
डेविड होल्ज़र

मुझे एक स्विफ्ट रैपर क्लास लिखने के लिए IRC के माध्यम से एक सुझाव मिला है जो वास्तविक C ++ ऑब्जेक्ट के लिए एक शून्य पॉइंटर को बनाए रखता है और आवश्यक तरीकों को उजागर करता है, जो प्रभावी रूप से, बस C ब्रिज और पॉइंटर के माध्यम से ऑब्जेक्ट तक जाते हैं।
डेविड Hoelzer

4
इसके अपडेट के रूप में, स्विफ्ट 3.0 अब बाहर है और पिछले वादों के बावजूद, C ++ इंटरऑपरेबिलिटी को अब "आउट ऑफ स्कोप" के रूप में चिह्नित किया गया है।
डेविड होल्ज़र

जवाबों:


61

मैंने पूरी तरह से प्रबंधनीय जवाब दिया है। आप कितना साफ होना चाहते हैं यह पूरी तरह से इस बात पर आधारित है कि आप कितना काम करने को तैयार हैं।

सबसे पहले, अपने C ++ क्लास को लें और इसके साथ इंटरफेस करने के लिए C "आवरण" फ़ंक्शन बनाएं। उदाहरण के लिए, यदि हमारे पास यह C ++ वर्ग है:

class MBR {
    std::string filename;

public:
    MBR (std::string filename);
    const char *hexdump();
    const char *imageType();
    const char *bootCode();
    const char *partitions();
private:
    bool readFile(unsigned char *buffer, const unsigned int length);
};

हम फिर इन सी ++ कार्यों को लागू करते हैं:

#include "MBR.hpp"

using namespace std;
const void * initialize(char *filename)
{
    MBR *mbr = new MBR(filename);

    return (void *)mbr;
}

const char *hexdump(const void *object)
{
    MBR *mbr;
    static char retval[2048];

    mbr = (MBR *)object;
    strcpy(retval, mbr -> hexdump());
    return retval;
}

const char *imageType(const void *object)
{
    MBR *mbr;
    static char retval[256];

    mbr = (MBR *)object;
    strcpy(retval, mbr -> imageType());
    return retval;
}

पुल हैडर में तब शामिल होता है:

#ifndef ImageReader_hpp
#define ImageReader_hpp

#ifdef __cplusplus
extern "C" {
#endif

    const void *initialize(char *filename);
    const char *hexdump(const void *object);
    const char *imageType(const void *object);

#ifdef __cplusplus
}
#endif

#endif /* ImageReader_hpp */

स्विफ्ट से, अब हम ऑब्जेक्ट को इंस्टेंट कर सकते हैं और इसके साथ इंटरैक्ट कर सकते हैं:

let cppObject = UnsafeMutablePointer<Void>(initialize(filename))
let type = String.fromCString(imageType(cppObject))
let dump = String.fromCString(hexdump(cppObject))                
self.imageTypeLabel.stringValue = type!
self.dumpDisplay.stringValue = dump!

इसलिए, जैसा कि आप देख सकते हैं, समाधान (जो वास्तव में सरल है) ऐसे रैपर बनाने के लिए है जो किसी वस्तु को तुरंत बदल देगा और उस वस्तु को एक पॉइंटर लौटाएगा। फिर इसे रैपर फ़ंक्शंस में वापस भेजा जा सकता है जो आसानी से उस क्लास के अनुरूप ऑब्जेक्ट के रूप में व्यवहार कर सकते हैं और सदस्य फ़ंक्शन को कॉल कर सकते हैं।

इसे क्लीनर बनाना

हालांकि यह एक शानदार शुरुआत है और यह साबित करता है कि एक तुच्छ पुल के साथ मौजूदा सी ++ कक्षाओं का उपयोग करना पूरी तरह से संभव है, यह क्लीनर भी हो सकता है।

इसे साफ करने का सीधा मतलब यह होगा कि हम UnsafeMutablePointer<Void>अपने स्विफ्ट कोड के बीच से हटाकर स्विफ्ट क्लास में इनकैप्सुलेट कर दें। अनिवार्य रूप से, हम समान C / C ++ रैपर फ़ंक्शन का उपयोग करते हैं लेकिन उन्हें स्विफ्ट क्लास के साथ इंटरफ़ेस करते हैं। स्विफ्ट वर्ग वस्तु संदर्भ को बनाए रखता है और अनिवार्य रूप से सभी विधि और विशेषता संदर्भ कॉल को पुल के माध्यम से C ++ ऑब्जेक्ट पर भेजता है!

ऐसा करने के बाद, स्विफ्ट क्लास में सभी ब्रिजिंग कोड पूरी तरह से समाप्त हो जाते हैं। भले ही हम अभी भी एक सी ब्रिज का उपयोग कर रहे हैं, हम उद्देश्यपूर्ण-सी या ऑब्जेक्टिव-सी ++ में उन्हें रीकोड करने के लिए बिना सी ++ ऑब्जेक्ट्स का पारदर्शी रूप से उपयोग कर रहे हैं।


12
यहाँ महत्वपूर्ण याद मुद्दों की एक जोड़ी है। पहला यह कि विध्वंसक कभी नहीं कहा जाता है, और वस्तु का रिसाव होता है। दूसरा यह है कि अपवाद कुछ बुरा संकट पैदा कर सकते हैं।
माइकल एंडरसन

2
मैंने इस प्रश्न के उत्तर में मेरे प्रश्नों के एक समूह को शामिल किया stackoverflow.com/questions/2045774/…
माइकल एंडरसन

2
@DavidHoelzer, मैं सिर्फ इस विचार पर ज़ोर देना चाहता था क्योंकि कुछ डेवलपर्स एक संपूर्ण C ++ लाइब्रेरी को लपेटने की कोशिश करेंगे और इसे स्विफ्ट में लिखा गया था। आपने "स्विफ्ट के भीतर से C ++ क्लास" को तुरंत कैसे और कैसे हेरफेर किया, इसका एक शानदार उदाहरण दिया! और मैं सिर्फ यह जोड़ना चाहता था कि इस तकनीक का उपयोग सावधानी से किया जाना चाहिए! आपके उदाहरण के लिए धन्यवाद!
निकोलई नीता

1
ऐसा मत सोचो कि यह "सही" है। मुझे लगता है कि यह लगभग वैसा ही है जैसा आप ऑब्जेक्टिव-सी रैपर लिखते हैं तो स्विफ्ट में इसका इस्तेमाल करें।
Bagusflyer

1
@Bagusflyer यकीन है ... सिवाय इसके कि यह एक उद्देश्य-सी आवरण नहीं है।
डेविड होल्जर

11

स्विफ्ट का वर्तमान में कोई C ++ इंटरोप नहीं है। यह एक दीर्घकालिक लक्ष्य है, लेकिन निकट भविष्य में ऐसा होने की संभावना नहीं है।


25
"सी रैपर का उपयोग करें" को "सी ++
इंटरोप

6
शायद, लेकिन उनका उपयोग करने से आपको C ++ क्लास को तुरंत करने की अनुमति मिलती है और फिर उस पर कॉल के तरीके उपयोगी होते हैं और यह प्रश्न को संतुष्ट करता है।
डेविड होइलर

2
वास्तव में, यह अब दीर्घकालिक लक्ष्य नहीं है, लेकिन इसके बजाय, रोडमैप में "आउट ऑफ स्कोप" के रूप में चिह्नित किया गया है।
डेविड होल्ज़र

4
सी-रैपर का उपयोग करना किसी भी भाषा, अजगर, जावा, आदि के लिए लगभग सभी सी ++ इंटरोप के लिए मानक दृष्टिकोण है
एंड्रयू पॉल सिमंस

8

अपने स्वयं के समाधान के अलावा, इसे करने का एक और तरीका है। आप उद्देश्य-सी ++ में C ++ कोड को कॉल या सीधे लिख सकते हैं।

तो आप अपने C ++ कोड के शीर्ष पर एक उद्देश्य-सी ++ आवरण बना सकते हैं और एक उपयुक्त इंटरफ़ेस बना सकते हैं।

फिर अपने स्विफ्ट कोड से ऑब्जेक्टिव-सी ++ कोड को कॉल करें। उद्देश्य-सी ++ कोड लिखने में सक्षम होने के लिए आपको .m से .mm तक फ़ाइल एक्सटेंशन का नाम बदलना पड़ सकता है

उपयुक्त होने पर अपने C ++ ऑब्जेक्ट द्वारा आवंटित मेमोरी को जारी करना न भूलें।


6

जैसा कि एक अन्य जवाब में कहा गया है, बातचीत करने के लिए ओबजैक ++ का उपयोग करना बहुत आसान है। बस .m और xcode / clang के बजाय अपनी फ़ाइलों को नाम दें। आपको उस फ़ाइल में c ++ एक्सेस देता है।

ध्यान दें कि ObjC ++ C ++ वंशानुक्रम का समर्थन नहीं करता है। मैं ObjC ++ में एक c ++ क्लास को उप-वर्ग करना चाहता हूं, आप नहीं कर सकते। आपको C ++ में उपवर्ग लिखना होगा और इसे एक ObjC ++ वर्ग के चारों ओर लपेटना होगा।

फिर ब्रिजिंग हेडर का उपयोग करें जिसे आप आमतौर पर स्विफ्ट से objc कॉल करने के लिए उपयोग करेंगे।


2
आपको बात याद आ गई होगी। इंटरफेस की आवश्यकता है क्योंकि हमारे पास एक बहुत बड़ा, बहु-प्लेटफ़ॉर्म सी ++ एप्लिकेशन है जिसे हम अब ओएस एक्स पर भी समर्थन कर रहे हैं। उद्देश्य सी ++ वास्तव में पूरी परियोजना के लिए एक विकल्प नहीं है।
डेविड होल्ज़र

मुझे यकीन नहीं है कि मैं आपको समझता हूं। जब आप स्विफ्ट और c ++ या इसके विपरीत जाना चाहते हैं, तो आपको बस ObjC ++ की आवश्यकता है। बस सीमाओं को objc ++ में लिखना होगा, न कि पूरी परियोजना को।
नर्नल्स

1
हाँ, मैं आपके प्रश्न पर गया था। आपको लगता है कि C ++ को सीधे स्विफ्ट से जोड़कर देखा जाएगा। मुझे नहीं पता कि यह कैसे करना है।
nnrales

1
यही मेरे पोस्ट किए गए उत्तर को पूरा करता है। C फ़ंक्शंस आपको ऑब्जेक्ट्स को स्मृति के मनमाने तरीके से पास और बाहर जाने की अनुमति देता है और दोनों तरफ विधियों को कॉल करने के लिए।
डेविड होल्ज़र

1
@DavidHoelzer ऐसा लगता है कि यह अच्छा है, लेकिन क्या आप इस तरह से कोड को अधिक जटिल नहीं बना रहे हैं? मुझे लगता है कि यह व्यक्तिपरक है। ObjC ++ आवरण, मुझे साफ लगता है।
nnrales

5

आप सी + + को स्विफ्ट (अन्य भाषाओं के बीच) को स्वचालित रूप से पुल करने के लिए स्कैप भाषा पुल का उपयोग कर सकते हैं । ब्रिज कोड स्वतः सी ++ हेडर फाइलों से सीधे फ्लाई पर उत्पन्न होता है। यहाँ एक उदाहरण है :

सी ++:

#include <scapix/bridge/object.h>

class contact : public scapix::bridge::object<contact>
{
public:
    std::string name();
    void send_message(const std::string& msg, std::shared_ptr<contact> from);
    void add_tags(const std::vector<std::string>& tags);
    void add_friends(std::vector<std::shared_ptr<contact>> friends);
};

स्विफ्ट:

class ViewController: UIViewController {
    func send(friend: Contact) {
        let c = Contact()

        contact.sendMessage("Hello", friend)
        contact.addTags(["a","b","c"])
        contact.addFriends([friend])
    }
}

दिलचस्प। ऐसा लगता है जैसे आपने इसे पहली बार 2019 के मार्च में रिलीज़ किया था।
डेविड होल्ज़र

@ बोरिस-रसिन मैं उदाहरण में तेजी से पुल के लिए प्रत्यक्ष सी ++ नहीं पा सकता हूं। क्या आपके पास योजनाओं में यह सुविधा है?
ऋग्वेद ४

2
हां, इस समय पुल का सीधा C ++ नहीं है। लेकिन C ++ से ObjC पुल स्विफ्ट के लिए पूरी तरह से ठीक है (Apple के ObjC से स्विफ्ट ब्रिज के लिए)। मैं अभी सीधे पुल पर काम कर रहा हूं (यह कुछ मामलों में बेहतर प्रदर्शन प्रदान करेगा)।
बोरिस रसिन

1
अरे हाँ, आप पूरी तरह से सही हैं, लेकिन लिनक्स पर नंगे स्विफ्ट प्रोजेक्ट में ऐसा नहीं है। अपने कार्यान्वयन को देखने के लिए तत्पर हैं।
ऋषि ४४४
हमारी साइट का प्रयोग करके, आप स्वीकार करते हैं कि आपने हमारी Cookie Policy और निजता नीति को पढ़ और समझा लिया है।
Licensed under cc by-sa 3.0 with attribution required.