यह JS को आपके Wasm से बाइंड कर देता है!
अपने पिछले वासम लेख में, मैंने बात की थी Wasm के लिए C लाइब्रेरी को कंपाइल करने के बारे में जानकारी, ताकि आप वेब पर उसका इस्तेमाल कर सकें. एक चीज़ जो मुझे (और कई पाठकों को) सबसे अजीब और थोड़ा अजीब तरीका लगा आपको मैन्युअल तौर पर यह बताना होगा कि आपके Wasm मॉड्यूल के किस फ़ंक्शन का इस्तेमाल किया जा रहा है. आपको नई जानकारी देने के लिए, मैं इस कोड स्निपेट के बारे में बात कर रहा/रही हूं:
const api = {
version: Module.cwrap('version', 'number', []),
create_buffer: Module.cwrap('create_buffer', 'number', ['number', 'number']),
destroy_buffer: Module.cwrap('destroy_buffer', '', ['number']),
};
यहां हम उन फ़ंक्शन के नाम बताते हैं जिन्हें हमने मार्क किया है
EMSCRIPTEN_KEEPALIVE
, आइटम लौटाने के तरीके क्या हैं और आइटम किस तरह के हैं
तर्क हैं. इसके बाद, हम api
ऑब्जेक्ट पर मौजूद तरीकों का इस्तेमाल करके, शुरू करने के लिए कर सकते हैं
में ये फ़ंक्शन शामिल हो सकते हैं. हालांकि, इस तरीके से Wasm का इस्तेमाल करने से स्ट्रिंग काम नहीं करती और
आपको मैन्युअल रूप से मेमोरी के हिस्सों को इधर-उधर ले जाने की ज़रूरत होती है, जिससे कई लाइब्रेरी
एपीआई इस्तेमाल करने में बहुत मुश्किल होती है. क्या इससे बेहतर कोई तरीका नहीं है? हां, ऐसा क्यों है, वरना
यह लेख किस बारे में होगा?
C++ नाम मैंगलिंग
हालांकि, डेवलपर के अनुभव से ही आपको ऐसा टूल बनाने में मदद मिलेगी जिससे मदद मिल सके
इन बाइंडिंग के साथ, कॉन्टेंट की एक बड़ी वजह यह होती है: जब C
या C++ कोड का इस्तेमाल करते हैं, तो हर फ़ाइल को अलग-अलग कंपाइल किया जाता है. इसके बाद, लिंकर
इन सभी तथाकथित ऑब्जेक्ट फ़ाइलों को एक साथ मिलाकर उन्हें वेबफ़ॉर्म में बदल देता है
फ़ाइल से लिए जाते हैं. C का इस्तेमाल करने पर, फ़ंक्शन के नाम अब भी ऑब्जेक्ट फ़ाइल में उपलब्ध होते हैं
लिंकर का इस्तेमाल करें. C फ़ंक्शन को कॉल करने के लिए आपको बस नाम,
जिसे हम cwrap()
को स्ट्रिंग के तौर पर दे रहे हैं.
वहीं, C++, फ़ंक्शन ओवरलोडिंग के साथ काम करता है. इसका मतलब है कि आपके पास
हस्ताक्षर अलग होने पर भी एक ही फ़ंक्शन कई बार (जैसे कि
पैरामीटर अलग-अलग ढंग से टाइप करते हैं). कंपाइलर के हिसाब से, add
जैसा एक अच्छा नाम
किसी ऐसी चीज़ में मैनज किया जाएगा जो फ़ंक्शन में सिग्नेचर को कोड में बदलता है
लिंकर का नाम. इस वजह से, हम अपने फ़ंक्शन का इस्तेमाल
अब उसे अपने नाम से जोड़ दिया जाएगा.
एम्बाइंड डालें
एंबाइंड वह Emscripten टूलचेन का हिस्सा है और आपको C++ मैक्रो का एक समूह उपलब्ध कराता है इसकी मदद से, C++ कोड के बारे में जानकारी दी जा सकती है. यह बताया जा सकता है कि कौनसे फ़ंक्शन, enum, JavaScript से क्लास या वैल्यू टाइप का इस्तेमाल करना है. आइए शुरू करें कुछ सामान्य फ़ंक्शन के साथ आसान है:
#include <emscripten/bind.h>
using namespace emscripten;
double add(double a, double b) {
return a + b;
}
std::string exclaim(std::string message) {
return message + "!";
}
EMSCRIPTEN_BINDINGS(my_module) {
function("add", &add);
function("exclaim", &exclaim);
}
मेरे पिछले लेख की तुलना में, हम अब emscripten.h
को इस तरह शामिल नहीं कर रहे हैं
अब हमें अपने फ़ंक्शन के साथ EMSCRIPTEN_KEEPALIVE
की व्याख्या करने की ज़रूरत नहीं है.
इसके बजाय, हमारे पास एक EMSCRIPTEN_BINDINGS
सेक्शन है. इसमें हम इन नामों को
जिन्हें हम अपने फ़ंक्शन को JavaScript में दिखाना चाहते हैं.
इस फ़ाइल को कंपाइल करने के लिए, हम उसी सेटअप का इस्तेमाल कर सकते हैं. हालांकि, अगर आप चाहें, तो वह भी सेट अप कर सकते हैं
डॉकर चित्र) जैसा कि पिछले
लेख में बताया गया है. एंबाइंड का इस्तेमाल करने के लिए,
हम --bind
फ़्लैग जोड़ते हैं:
$ emcc --bind -O3 add.cpp
अब बस एक ऐसी HTML फ़ाइल तैयार हो रही है, जो Wasm मॉड्यूल बनाया गया:
<script src="/a.out.js"></script>
<script>
Module.onRuntimeInitialized = _ => {
console.log(Module.add(1, 2.3));
console.log(Module.exclaim("hello world"));
};
</script>
जैसा कि आप देख सकते हैं, हम अब cwrap()
का इस्तेमाल नहीं कर रहे हैं. यह सीधे काम करता है
क्लिक करें. लेकिन इससे भी महत्वपूर्ण बात, हमें मैन्युअल रूप से कॉपी करने की चिंता नहीं करनी पड़ती
मेमोरी के हिस्से, ताकि स्ट्रिंग काम करने में मदद मिले! embind आपको वह मुफ़्त में देता है,
टाइप की जांच:
यह एक बहुत ही अच्छी बात है. हम किसी भी गड़बड़ी को ठीक करने के बजाय, कुछ गड़बड़ियों का जल्दी पता लगा लेते हैं में कभी-कभी बड़ी गड़बड़ियां हो सकती हैं.
ऑब्जेक्ट
कई JavaScript कंस्ट्रक्टर और फ़ंक्शन, विकल्प ऑब्जेक्ट का इस्तेमाल करते हैं. यह एक अच्छा पैटर्न है, लेकिन मैन्युअल तौर पर इसे महसूस करना काफ़ी मुश्किल है. एम्बाइंड यहां भी मदद कर सकते हैं!
उदाहरण के लिए, मुझे यह शानदार C++ फ़ंक्शन मिला है जो स्ट्रिंग होस्ट कर देता है और मैं तुरंत इसे वेब पर इस्तेमाल करना चाहता/चाहती हूं. मैंने ऐसा इस तरह से किया:
#include <emscripten/bind.h>
#include <algorithm>
using namespace emscripten;
struct ProcessMessageOpts {
bool reverse;
bool exclaim;
int repeat;
};
std::string processMessage(std::string message, ProcessMessageOpts opts) {
std::string copy = std::string(message);
if(opts.reverse) {
std::reverse(copy.begin(), copy.end());
}
if(opts.exclaim) {
copy += "!";
}
std::string acc = std::string("");
for(int i = 0; i < opts.repeat; i++) {
acc += copy;
}
return acc;
}
EMSCRIPTEN_BINDINGS(my_module) {
value_object<ProcessMessageOpts>("ProcessMessageOpts")
.field("reverse", &ProcessMessageOpts::reverse)
.field("exclaim", &ProcessMessageOpts::exclaim)
.field("repeat", &ProcessMessageOpts::repeat);
function("processMessage", &processMessage);
}
मैंने अपने processMessage()
फ़ंक्शन के विकल्पों के लिए एक निर्देश तय किया है. इस
EMSCRIPTEN_BINDINGS
ब्लॉक, मैं JavaScript देखने के लिए value_object
का इस्तेमाल कर सकता हूं
इस C++ वैल्यू का इस्तेमाल ऑब्जेक्ट के तौर पर करें. अगर मैं चाहें, तो value_array
का भी इस्तेमाल किया जा सकता है
इस C++ वैल्यू को अरे के तौर पर इस्तेमाल करें. मैं processMessage()
फ़ंक्शन को भी बाइंड करता/करती हूं, और
बाकी सब जादू है. अब मैं processMessage()
फ़ंक्शन को यहां से कॉल कर सकता/सकती हूं
बिना किसी बॉयलरप्लेट कोड वाली JavaScript:
console.log(Module.processMessage(
"hello world",
{
reverse: false,
exclaim: true,
repeat: 3
}
)); // Prints "hello world!hello world!hello world!"
क्लास
अगर आपको पूरी जानकारी देनी है, तो मुझे आपको यह भी दिखाना चाहिए कि embind, आपको इस तरह की टेक्नोलॉजी पर काम करती है. इससे, ES6 क्लास के साथ काफ़ी तालमेल रहता है. शायद आप अब से पैटर्न दिखने लगेगा:
#include <emscripten/bind.h>
#include <algorithm>
using namespace emscripten;
class Counter {
public:
int counter;
Counter(int init) :
counter(init) {
}
void increase() {
counter++;
}
int squareCounter() {
return counter * counter;
}
};
EMSCRIPTEN_BINDINGS(my_module) {
class_<Counter>("Counter")
.constructor<int>()
.function("increase", &Counter::increase)
.function("squareCounter", &Counter::squareCounter)
.property("counter", &Counter::counter);
}
JavaScript की तरफ़, यह करीब-करीब एक नेटिव क्लास की तरह लगता है:
<script src="/a.out.js"></script>
<script>
Module.onRuntimeInitialized = _ => {
const c = new Module.Counter(22);
console.log(c.counter); // prints 22
c.increase();
console.log(c.counter); // prints 23
console.log(c.squareCounter()); // prints 529
};
</script>
C का क्या होगा?
embind को C++ के लिए लिखा गया है और इसका इस्तेमाल सिर्फ़ C++ फ़ाइलों में किया जा सकता है, लेकिन यह नहीं है
का मतलब है कि आप C फ़ाइलों से लिंक नहीं कर सकते! C और C++ को एक साथ मिलाने के लिए, आपको सिर्फ़
अपनी इनपुट फ़ाइलों को दो ग्रुप में बांटें: एक C के लिए और दूसरा C++ फ़ाइलों के लिए और
emcc
के लिए सीएलआई फ़्लैग को इस तरह बढ़ाएं:
$ emcc --bind -O3 --std=c++11 a_c_file.c another_c_file.c -x c++ your_cpp_file.cpp
नतीजा
embind से आपको काम करते समय डेवलपर के अनुभव को बेहतर बनाने में मदद मिलती है Wasm और C/C++ के साथ किया जा सकता है. इस लेख में, ऑफ़र में हिस्सा लेने के लिए सभी विकल्पों के बारे में नहीं बताया गया है. अगर आपको दिलचस्पी है, तो हमारा सुझाव है कि आप embind's के साथ जारी रखें दस्तावेज़ में दिया गया है. ध्यान रखें कि embind का इस्तेमाल करने से Wasm मॉड्यूल और आपके दोनों हो सकते हैं gzip की मदद से JavaScript ग्लू कोड 11k तक बढ़ता है — सबसे खास बात यह है कि यह छोटे साइज़ में मॉड्यूल देखें. अगर आपके पास बहुत छोटा Wasm प्लैटफ़ॉर्म है, तो जोड़ने की लागत प्रोडक्शन एनवायरमेंट में होना चाहिए! फिर भी, आपको अपने इसे आज़माएं.