JS'yi wasm'nize bağlar.
Son Wasm makalemde, web'de kullanabilmeniz için C kitaplığını wasm olarak derleme konusunda bilgi verilmektedir. Bir şey Bu benim için (ve birçok okuyucu için) öne çıkan şey, Kaba ve biraz tuhaf bir wasm modülünüzün hangi işlevlerini kullandığınızı manuel olarak bildirmeniz gerekir. Konuştuğumuz kod snippet'i şöyledir:
const api = {
version: Module.cwrap('version', 'number', []),
create_buffer: Module.cwrap('create_buffer', 'number', ['number', 'number']),
destroy_buffer: Module.cwrap('destroy_buffer', '', ['number']),
};
Burada,
EMSCRIPTEN_KEEPALIVE
, bunların dönüş türleri ve bunların türleri
belirlemektir. Sonra, çağırmak için api
nesnesindeki yöntemleri kullanabiliriz
bu işlevlere göz atın. Ancak, wasm'ın bu şekilde kullanılması dizeleri ve
bellek parçalarını manuel olarak taşımanızı gerektirir. Bu da,
API'lerin kullanımı çok zahmetli. Daha iyi bir yol yok mu? Neden evet, aksi takdirde
bu makalenin konusu neydi?
C++ ad birleştirme
Geliştirici deneyimi, kullanıcılara yardımcı olan bir araç geliştirmek için
daha acil bir neden vardır: C
veya C++ kodu varsa her dosya ayrı olarak derlenir. Ardından bağlayıcı,
bütün bu nesne dosyalarını bir araya getirmek ve bunları bir wasm'a dönüştürmek
dosyası olarak kaydedebilirsiniz. C ile, işlevlerin adları nesne dosyasında kullanılmaya devam eder
kullanabilirsiniz. C işlevini çağırmak için ihtiyacınız olan tek şey adıdır.
cwrap()
öğesine bir dize olarak sağlıyoruz.
Öte yandan C++, işlev aşırı yüklemesini destekler. Bu da,
imza farklı olduğu sürece aynı işlevi birden çok kez tekrarlamanız gerekir (ör.
farklı şekilde yazılmış parametreler). Derleyici düzeyinde add
gibi bir güzel ad
işlevindeki imzayı kodlayan bir şeye parçalanır
bağlayıcının adını girin. Bunun sonucunda, işlevimizi arama
artık bir adı yok.
Embind girin
embind Emscripten araç zincirinin bir parçasıdır ve size birçok C++ makrosu sağlar Bunlar sayesinde C++ kodunu ekleyebilirsiniz. Dört hafta içinde hangi fonksiyonların, enumların, kullanmayı planladığınız sınıfları veya değer türlerini gösterir. Hadi başlayalım bazı basit işlevlerle daha basittir:
#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);
}
Önceki makalemle karşılaştırıldığında emscripten.h
artık dahil edilmiyor.
artık işlevlerimize EMSCRIPTEN_KEEPALIVE
ile ek açıklama eklememiz gerekmiyor.
Bunun yerine, adları şunun altında listeleyen bir EMSCRIPTEN_BINDINGS
bölümümüz vardır:
açık hale getirmek isteriz.
Bu dosyayı derlemek için aynı kurulumu (veya isterseniz
Docker görüntüsü) önceki örnekte
makalesine bakın. Embind'i kullanmak için
--bind
işaretini ekliyoruz:
$ emcc --bind -O3 add.cpp
Şimdi tek yapmanız gereken yeni wasm modülü oluşturuldu:
<script src="/a.out.js"></script>
<script>
Module.onRuntimeInitialized = _ => {
console.log(Module.add(1, 2.3));
console.log(Module.exclaim("hello world"));
};
</script>
Gördüğünüz gibi artık cwrap()
kullanmıyoruz. Bu şekilde hemen kullanabilirsiniz.
ekleyebilirsiniz. Ancak daha da önemlisi, yükleme işlemini manuel olarak
parça parça hafızaya ayırabiliriz. embind, bunu size ücretsiz olarak sağlar.
şu tür kontrolleri uygulayın:
Bazı hataları ele almak yerine erkenden tespit edebildiğimiz için bu gerçekten ve zaman zaman elverişsiz olan wasm hatalarını düzeltmeye çalışın.
Nesneler
Birçok JavaScript oluşturucu ve işlevi, seçenek nesnelerini kullanır. Bu güzel bir ancak wasm'da manuel olarak gerçekleştirmek son derece yorucudur. Embind bu konuda da yardımcı olabilir!
Örneğin, dizelerim var ve bunu web'de acilen kullanmak istiyorum. Bunu şu şekilde yaptım:
#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()
işlevimin seçenekleri için bir struct tanımlıyorum.
EMSCRIPTEN_BINDINGS
blok, JavaScript'in görmesini sağlamak için value_object
kullanabilirim
bu C++ değerini nesne olarak değiştirir. İstersem value_array
kullanabilirim
bu C++ değerini bir dizi olarak kullanın. Ayrıca processMessage()
işlevini de bağlıyorum ve
geri kalan her şey sihir ile harikadır. Artık processMessage()
işlevini şuradan çağırabilirim:
Ortak metin kodu olmayan JavaScript:
console.log(Module.processMessage(
"hello world",
{
reverse: false,
exclaim: true,
repeat: 3
}
)); // Prints "hello world!hello world!hello world!"
Sınıflar
Bütünlük kazanmak amacıyla, embind'in içeriğinizi Google'ın bu da ES6 sınıflarıyla büyük bir sinerji yaratıyor. Muhtemelen bir kalıp görmeye başlayın:
#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 açısından, bu neredeyse yerel bir sınıf hissi verir:
<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>
Peki ya C?
embind C++ için yazılmıştır ve yalnızca C++ dosyalarında kullanılabilir, ancak
C dosyalarına bağlantı veremezsiniz! C ve C++ özelliklerini birlikte kullanmak için
giriş dosyalarınızı iki gruba ayırın: C için biri, C++ dosyaları ve
emcc
için CLI işaretlerini şu şekilde genişletin:
$ emcc --bind -O3 --std=c++11 a_c_file.c another_c_file.c -x c++ your_cpp_file.cpp
Sonuç
embind, çalışırken geliştirici deneyiminde harika iyileştirmeler sunar. ile başladı. Bu makalede, embind tekliflerinin tüm seçenekleri ele alınmamaktadır. Bu konu ilginizi çekiyorsa, embind's dokümanlarına göz atın. Embind kullanarak hem wasm modülünüzü hem de gzip ile sıkıştırıldığında 11.000'e kadar daha büyük JavaScript yapışkan kodu (özellikle küçük boyutlarda) modüllerinde yer alır. Çok küçük bir wasm yüzeyiniz varsa embind'in maliyeti üretim ortamında buna değer. Yine de, abonelik seçeneklerinin deneyin.