Swift 6.3 या उसके बाद के संस्करण में, किसी ग्लोबल फ़ंक्शन को @c से चिह्नित करें, उसका संगतता हेडर जनरेट करें, कॉलर को C के रूप में कंपाइल करें और swiftc से लिंक करें। यह चरण-दर-चरण मार्गदर्शिका एक छोटा नेटिव Mac कमांड-लाइन प्रोग्राम बनाती है, जो हर सीमा को दर्शाता है।
पाठक का काम स्पष्ट है: किसी साधारण .c फ़ाइल से Swift की गणना को कॉल करना, बिना उस फ़ाइल को Objective-C या C++ के रूप में कंपाइल किए। Swift के आधिकारिक रिलीज़ दस्तावेज़ बताते हैं कि @c Swift 6.3 में शुरू की गई समर्थित C इंटरऑपरेबिलिटी सुविधा है। प्रयोगात्मक एट्रिब्यूट के बारे में पुरानी सलाह अपनाने से पहले अपने इंस्टॉल किए गए कंपाइलर की जाँच करें। Swift 6.3 जारी
Swift फ़ंक्शन को C से कॉल करने योग्य क्या बनाता है?
तीन चीज़ों का मेल होना ज़रूरी है: C कंपाइलर द्वारा पढ़ी जाने वाली घोषणा, इम्प्लीमेंटेशन द्वारा इस्तेमाल किया जाने वाला कॉलिंग कन्वेंशन, और लिंकर को दी गई ऑब्जेक्ट फ़ाइलें। हेडर पहली चीज़ का समाधान करता है; वह अनुपस्थित इम्प्लीमेंटेशन उपलब्ध नहीं करा सकता और न ही किसी मौजूदा फ़ंक्शन का ABI बदल सकता है।
@c एट्रिब्यूट किसी ग्लोबल Swift फ़ंक्शन को C कॉलिंग कन्वेंशन देता है और उसके सिग्नेचर को C में दर्शाए जा सकने वाले प्रकारों तक सीमित करता है। एक वैकल्पिक एट्रिब्यूट आर्ग्युमेंट उसका C नाम चुनता है। Swift संबंधित घोषणा को अपने जनरेट किए गए संगतता हेडर के C-संगत भाग में लिखता है। ये व्यवहार लागू किए गए Swift Evolution प्रस्ताव में निर्दिष्ट हैं। C-संगत फ़ंक्शन और enum
इस उदाहरण में, इंटरफ़ेस एक C integer स्वीकार करता है और लौटाता है। यह उदाहरणात्मक गणना डिस्प्ले प्रतिशत को शून्य से 100 तक की समावेशी सीमा में रखती है। इसमें कोई बफ़र, आवंटित परिणाम या ऑब्जेक्ट लाइफ़टाइम समन्वित करने की ज़रूरत नहीं है। इससे पहली बिल्ड का ध्यान कंपाइलेशन और लिंकिंग पर रहता है।
पहले Mac टूलचेन जाँचें
आपको macOS डेवलपमेंट वातावरण चाहिए, जिसमें Clang, macOS SDK और Swift 6.3 या उसके बाद का संस्करण हो। Apple Xcode के साथ कमांड-लाइन टूल देता है और अलग Command Line Tools पैकेज भी उपलब्ध कराता है। अगर आपके पास पहले से Xcode है, तो Apple के अनुसार अलग टूल इंस्टॉलेशन की ज़रूरत नहीं है। कमांड-लाइन टूल इंस्टॉल करना
Terminal में ये जाँचें चलाएँ:
xcode-select --print-path
xcrun --sdk macosx swiftc --version
xcrun --sdk macosx clang --version
खुद Swift कंपाइलर का संस्करण देखें। नया SDK इंस्टॉल होने मात्र से पुराने कंपाइलर में @c सुविधा नहीं आ जाती। अगर Xcode के कई संस्करण मौजूद हैं, तो Apple सक्रिय संस्करण चुनने के लिए Xcode > Settings > Locations > Command Line Tools में चयन करने, या शेल सत्र के लिए DEVELOPER_DIR से उसे ओवरराइड करने का तरीका बताता है। कमांड-लाइन टूल सेटिंग कॉन्फ़िगर करना
नीचे दिए गए कमांड चुने गए डेवलपमेंट टूल चलाने के लिए xcrun का इस्तेमाल करते हैं। Apple अपनी कमांड-लाइन बिल्ड संदर्भ सामग्री में इस टूल-खोज भूमिका का दस्तावेज़ देता है। Xcode FAQ के साथ कमांड लाइन से बिल्ड करना
एक खाली कार्यशील डायरेक्टरी बनाएँ और नीचे दी गई दोनों स्रोत फ़ाइलें उसमें सहेजें। बिल्ड कमांड उसी डायरेक्टरी से चलाएँ। यह स्थानीय Mac के लिए नेटिव बिल्ड है; इससे यूनिवर्सल बाइनरी नहीं बनती और न ही किसी अन्य प्लेटफ़ॉर्म के लिए कॉन्फ़िगरेशन होता है।
Swift इम्प्लीमेंटेशन और C कॉलर लिखें
एक्सपोर्ट किया गया Swift फ़ंक्शन परिभाषित करें
इस पूरी Swift स्रोत फ़ाइल को Percent.swift नाम से सहेजें:
@c(demo_clamp_percent)
public func clampPercent(_ value: CInt) -> CInt {
if value < 0 {
return 0
}
if value > 100 {
return 100
}
return value
}
Swift नाम clampPercent है; C की ओर दिखने वाला नाम demo_clamp_percent है। CInt, C के int से मैप होता है। इस स्पष्ट जोड़ी का इस्तेमाल करने से यह मानने की ज़रूरत नहीं पड़ती कि Swift Int का मतलब C int है: Swift Int के लिए दस्तावेज़ित मैपिंग ptrdiff_t है। C-संगत फ़ंक्शन और enum
फ़ंक्शन मानों की तुलना करता है और इनपुट या एक स्थिर मान लौटाता है। इसकी गणना में अंकगणितीय ओवरफ़्लो नहीं होता, यहाँ तक कि प्रतिशत सीमा से बाहर के इनपुट पर भी नहीं। एक्सपोर्ट किए गए नाम का प्रीफ़िक्स एक बड़े प्रोग्राम में इस इंटरफ़ेस को दूसरे फ़ंक्शन से अलग पहचानने की प्रचलित विधि का उदाहरण है।
पूरे C प्रोग्राम से इसे कॉल करें
इस फ़ाइल को main.c नाम से सहेजें। बिल्ड में शामिल किया गया हेडर बनाया जाएगा:
#include <stddef.h>
#include <stdio.h>
#include "Percent-Swift.h"
int main(void) {
const int inputs[] = {-12, 42, 140};
const int expected[] = {0, 42, 100};
const size_t count = sizeof inputs / sizeof inputs[0];
for (size_t i = 0; i < count; ++i) {
const int actual = demo_clamp_percent(inputs[i]);
printf("%d -> %d\n", inputs[i], actual);
if (actual != expected[i]) {
fprintf(stderr, "Unexpected result at index %zu\n", i);
return 1;
}
}
return 0;
}
तीन इनपुट निचली सीमा, अपरिवर्तित मान और ऊपरी सीमा की जाँच करते हैं। प्रोग्राम परिणामों को स्पष्ट रूप से जाँचता है और किसी गड़बड़ी पर शून्य से अलग स्टेटस लौटाता है, इसलिए इसका आउटपुट केवल कोई संख्या प्रिंट करने वाले प्रदर्शन से ज़्यादा उपयोगी है।
हेडर जनरेट करें, C कंपाइल करें और प्रोग्राम लिंक करें
दोनों फ़ाइलों वाली डायरेक्टरी से शेल में यह क्रम चलाएँ। बिल्ड का कोई चरण विफल होने पर क्रम रुक जाएगा:
(
set -eu
SDK_PATH="$(xcrun --sdk macosx --show-sdk-path)"
xcrun --sdk macosx swiftc \
-sdk "$SDK_PATH" \
-parse-as-library \
-module-name Percent \
-emit-object Percent.swift \
-emit-objc-header-path Percent-Swift.h \
-o Percent.o
xcrun --sdk macosx clang \
-isysroot "$SDK_PATH" \
-x c -std=c11 \
-Wall -Wextra -Werror \
-c main.c -o main.o
xcrun --sdk macosx swiftc \
-sdk "$SDK_PATH" \
main.o Percent.o \
-o percent-demo
./percent-demo
)
पहला कमांड Swift ऑब्जेक्ट और संगतता हेडर बनाता है। Swift का ड्राइवर -emit-object को ऑब्जेक्ट-फ़ाइल आउटपुट और -parse-as-library को लाइब्रेरी स्रोत को स्क्रिप्ट के बजाय लाइब्रेरी की तरह पार्स करने के विकल्प के रूप में परिभाषित करता है। इससे एक्ज़ीक्यूटेबल का एंट्री पॉइंट C फ़ाइल में रहता है। Swift कंपाइलर ड्राइवर विकल्प
नाम ऐतिहासिक होने के बावजूद, -emit-objc-header-path @c घोषणाओं के लिए संगतता हेडर का C सेक्शन भी माँगता है। लागू किया गया प्रस्ताव इस उद्देश्य के लिए उस फ़्लैग को स्पष्ट रूप से बनाए रखता है। जनरेट किए गए हेडर की जगह अपनी ओर से अनुमानित प्रोटोटाइप न लिखें। C-संगत फ़ंक्शन और enum
दूसरा कमांड स्रोत भाषा की निर्णायक जाँच है। Clang दस्तावेज़ बताता है कि -x इनपुट भाषा चुनता है और -c बिना लिंकिंग के ऑब्जेक्ट फ़ाइल बनाता है। यहाँ, -x c -std=c11 कॉलर को C11 के रूप में कंपाइल करते हुए वास्तविक जनरेट किए गए हेडर को पढ़वाता है। Clang कमांड गाइड
अंतिम कंपाइलर आह्वान Swift ड्राइवर का उपयोग करके दोनों ऑब्जेक्ट फ़ाइलें लिंक करता है। Swift की अपनी कंपाइलर गाइड, निचले स्तर का लिंक कमांड हाथ से जोड़ने के बजाय फ़ाइलें लिंक करने के लिए swiftc से कहने की सलाह देती है। कंपाइलर की डीबगिंग
उदाहरण की शाखाओं के आधार पर अपेक्षित आउटपुट यह है:
-12 -> 0
42 -> 42
140 -> 100
इन कमांडों की जाँच प्राप्त दस्तावेज़ों के आधार पर की गई, लेकिन यहाँ इन्हें चलाया नहीं गया: शोध वातावरण Linux है और उसमें न swiftc उपलब्ध है, न Clang। ऊपर दिया गया आउटपुट अपेक्षित परिणाम है, Mac पर चलाए गए परीक्षण का दावा नहीं।
अपने मौजूदा कोड के अनुरूप इंटरफ़ेस चुनें
Swift 6.3 या उसके बाद के संस्करण में नए और छोटे इंटरफ़ेस के लिए, ऊपर वाला जनरेट किए गए हेडर का तरीका घोषणा को सीधे इम्प्लीमेंटेशन से जोड़ता है। रिलीज़ दस्तावेज़ एक दूसरी दिशा भी समर्थित करते हैं: @c @implementation आयातित C हेडर में पहले से घोषित फ़ंक्शन को इम्प्लीमेंट करता है और Swift जाँचता है कि सिग्नेचर मेल खाते हैं। जब किसी मौजूदा C API को इंटरफ़ेस अनुबंध बनाए रखना हो, तब यह उपयोगी है। Swift 6.3 जारी
अगर आसपास का प्रोजेक्ट पहले से C++ के ज़रिए Swift को कॉल करता है, तो Swift की जनरेट की गई C++ बाइंडिंग एक और रास्ता देती हैं। सार्वजनिक Swift फ़ंक्शन किसी मॉड्यूल नेमस्पेस के अंदर inline C++ फ़ंक्शन के रूप में दिखाए जाते हैं। C कॉलर के लिए अलग अडैप्टर चाहिए, जिसे C++ के रूप में कंपाइल किया जाए और जो C की ओर दिखने वाली छोटी घोषणा उपलब्ध कराए। C ट्रांसलेशन यूनिट में उस अडैप्टर का C हेडर शामिल होगा। Swift और C++ को मिलाना
ऐसे अडैप्टर में, extern "C" C++ घोषणा या परिभाषा पर C भाषा लिंकिंग निर्दिष्ट करता है। यह C++ हेडर की सामग्री को C सिंटैक्स में नहीं बदलता। C++ वर्किंग ड्राफ़्ट लिंकिंग स्पेसिफ़िकेशन का अर्थ बताता है और भाषा लिंकिंग को सामान्य घोषणाओं से अलग करता है। C++ वर्किंग ड्राफ़्ट: लिंकिंग स्पेसिफ़िकेशन
इस ट्यूटोरियल के लिए, पुराने कंपाइलर को @c समर्थित संस्करण में अपग्रेड करना सीधा और समर्थित रास्ता है। प्रस्ताव @_cdecl को उसका प्रयोगात्मक पूर्ववर्ती बताता है; नए उदाहरण में वही पुरानी वर्तनी कॉपी करने से संस्करण की एक अहम सीमा छिप जाएगी। C-संगत फ़ंक्शन और enum
विफलताएँ जिस सीमा पर होती हैं, वहीं उनका निदान करें
जाँच को सीमित करने के लिए बिल्ड का पहला विफल चरण देखें। हेडर जनरेशन की विफलता, C पार्सर की त्रुटि और अनसुलझे सिंबल के लिए अलग-अलग सुधार चाहिए।
| पहली विफलता | अगली जाँच |
|---|---|
Swift @c अस्वीकार करता है |
चुने गए Swift कंपाइलर का संस्करण फिर जाँचें; इस उदाहरण के लिए Swift 6.3 या उसके बाद का संस्करण चाहिए। |
C को Percent-Swift.h नहीं मिलता |
पुष्टि करें कि Swift कमांड सफल हुआ और दोनों फ़ाइलें अपेक्षित डायरेक्टरी से बिल्ड हो रही हैं। |
C को demo_clamp_percent नहीं मिलता |
अभी जनरेट किया गया हेडर देखें और एट्रिब्यूट की वर्तनी जाँचें; स्रोत बदलने के बाद फिर बिल्ड करें। |
| C, C++ या Objective-C सिंटैक्स की रिपोर्ट करता है | जाँचें कि उसने कौन-सा हेडर शामिल किया और किस टूलचेन ने उसे जनरेट किया। जाँच के दौरान C के रूप में स्पष्ट कंपाइलेशन बनाए रखें। |
| लिंकिंग एक्सपोर्ट किए गए फ़ंक्शन को नहीं ढूँढ़ पाती | पुष्टि करें कि अंतिम कमांड में Percent.o शामिल है और वह मौजूदा Swift स्रोत से फिर बनाया गया है। |
| एक्ज़ीक्यूटेबल अनपेक्षित परिणाम देता है | प्रिंट हुए इनपुट/परिणाम की जोड़ी पढ़ें और Swift की शाखाओं से उसकी तुलना करें। |
यह क्रम Clang द्वारा दस्तावेज़ित कंपाइलर चरणों का अनुसरण करता है: पार्सिंग से पता चलता है कि स्रोत वैध है या नहीं, ऑब्जेक्ट जनरेशन कंपाइल किया गया कोड बनाता है, और लिंकिंग कंपाइल किए गए इनपुट को जोड़ती है। इसलिए केवल सफल C कंपाइलेशन से काम का एक हिस्सा ही पूरा होना साबित होता है। Clang कमांड गाइड
उदाहरण को अपनाते समय, C की ओर दिखने वाले फ़ंक्शन को छोटा रखें और हर नए पैरामीटर के लिए स्पष्ट अनुबंध तय करें। टेक्स्ट के लिए एन्कोडिंग और लंबाई संभालने का तरीका तय करें; बफ़र के लिए रैपर लिखने से पहले क्षमता और स्वामित्व तय करें। ये इस integer-मात्र प्रदर्शन से आगे के इंटरफ़ेस डिज़ाइन के निर्णय हैं।
स्थानीय जाँच के तीन प्रत्यक्ष परिणाम हैं: स्पष्ट C कंपाइलेशन के दौरान जनरेट किया गया हेडर स्वीकार होता है, दोनों इम्प्लीमेंटेशन एक एक्ज़ीक्यूटेबल में लिंक होते हैं, और कॉलर चुने गए इनपुट को सत्यापित करता है। उदाहरणात्मक गणना को अपने Swift फ़ंक्शन से बदलते समय भी ये जाँचें बनाए रखें।
स्रोत
- Swift 6.3 जारी — Swift.org
- C-संगत फ़ंक्शन और enum — Swift Evolution
- कमांड-लाइन टूल इंस्टॉल करना — Apple
- कमांड-लाइन टूल सेटिंग कॉन्फ़िगर करना — Apple
- Xcode FAQ के साथ कमांड लाइन से बिल्ड करना — Apple
- Swift कंपाइलर ड्राइवर विकल्प — Swift Project
- Clang कमांड गाइड — LLVM Project
- कंपाइलर की डीबगिंग — Swift Project
- Swift और C++ को मिलाना — Swift.org
- C++ वर्किंग ड्राफ़्ट: लिंकिंग स्पेसिफ़िकेशन — C++ Working Draft