Embind方便的实现js与C++的交互

本文介绍了如何使用Embind库方便地实现JavaScript与C++之间的交互。通过示例展示了如何导出C++的函数到JavaScript,包括基本类型、字符串操作以及类的方法。同时,也提到了使用`EM_JS`宏来编写JavaScript代码并在C++中调用。文章最后展示了完整的C++代码示例,涵盖了从基本类型到类的实例方法的交互过程。

#include <emscripten.h>
#include <emscripten/bind.h>//EMSCRIPTEN_BINDINGS 的头文件
#include <iostream>
#include <string>
#include <math.h>
#include <stdio.h>
//#include <SDL.h>
using namespace emscripten;//EMSCRIPTEN_BINDINGS 中的 function
using namespace std;
// 注意extern “C” 中的C要大写
extern "C" int EMSCRIPTEN_KEEPALIVE myFunction(int argc, char **argv)
{
    cout<<"这是一个测试函数"<<endl;
    return 0;
}

 extern "C" char* EMSCRIPTEN_KEEPALIVE outName(char *n)
 {
    char xhName[] = "xuanhun";
    strcat(n, xhName);
    cout<<"outName = "<<n<<endl;
    return n;
 }

// 在浏览器控制台 可以执行 Module._myFunction() 函数,注意是extern "C"
// 函数名只会在前面加一个下划线,否则名称就比较繁琐
// main函数也是默认导出的函数 可以使用 Module._main() 来执行

EM_JS(void, call_alert, (), {
  alert('hello world!');
  console.log("EM_JS,nihao!");
  //throw 'all done';
});

EM_JS(int, call_add, (int a,int b), {
    console.log("EM_JS,add!");
  return a + b;
});

//Null-terminated C strings can also be passed into EM_JS functions, 
//but to operate on them, they need to be copied out from the heap to convert to high-level JavaScript strings.
// use connsole.log('hello ' + UTF8ToString(str));
EM_JS(char*, call_add_string, (char* a,char* b), {
    console.log("EM_JS,add_string!");
    console.log(UTF8ToString(a));
    var s = "hello,jsStr";
    //return a;//也可以
    
    var returnStr = "Hello C#!";
    var bufferSize = lengthBytesUTF8(returnStr) + 1;
    var buffer = _malloc(bufferSize);
    stringToUTF8(returnStr, buffer, bufferSize);
    return buffer;
    //由于这里直接return了,本来buffer是要_free(buffer)释放的,因此在实际场景中最好用智能指针包裹起来
});
// 上面的EM_JS执行的是C风格代码,不允许C++的类型 例如 std::string
// stackoverflow的解释:If you use extern "C" with a function, you cannot use C++ types in it's signature.
// So if you want to use std::string, then you can use "Embind" or "WebIDL Binder". Refer here
/* 下面通过 Embind 的方法处理
// This is your routine C++ code
size_t MyStrLen(std::string inStr) {
    return inStr.length();
}

// This is the extra code you need to write to expose your function to JS
EMSCRIPTEN_BINDINGS(my_module) {
    function("MyStrLen", &MyStrLen);
}
Now in JS all you need to do is:

var myStr = "TestString";
Module.MyStrLen(myStr);
Make sure you pass the flag

--bind

when calling emcc.

There is another approach where you can do a Malloc on the C++ heap from JS and then do manipulations, but the above approach should be easier.

*/

// This is your routine C++ code
size_t MyStrLen(std::string inStr) {
    return inStr.length();
}

std::string MyStr(std::string inStr) {
    string str = inStr + "123";
    return str;
}

// This is the extra code you need to write to expose your function to JS
EMSCRIPTEN_BINDINGS(my_module) {
    emscripten::function("MyStrLen", &MyStrLen);
}

EMSCRIPTEN_BINDINGS(my_module_1) {
    emscripten::function("MyStr", &MyStr);
}

EM_JS(void, read_data, (int* data), {
  console.log('Data: ' + HEAP32[data>>2] + ', ' + HEAP32[(data+4)>>2]);
});


EM_JS(void, call_myFunc, (const int argc, const char** argv), {
    var arr = [];
    for (let i = 0; i < argc; i++) {
        const mem = HEAP32[(argv + (i * 4)) >> 2];
        const str = UTF8ToString(mem);
        arr.push(str);
    }
    console.log(arr);
});


extern "C" {

int int_sqrt(int x) {
  return sqrt(x);
}

}

//Embind classes
class MyClass {
public:
  MyClass(int x, std::string y)
    : x(x)
    , y(y)
  {}

  void incrementX() {
    ++x;
  }

  int getX() const { return x; }
  void setX(int x_) { x = x_; }

  static std::string getStringFromInstance(const MyClass& instance) {
    return instance.y;
  }

private:
  int x;
  std::string y;
};

// Binding code
EMSCRIPTEN_BINDINGS(my_class_example) {
  class_<MyClass>("MyClass")
    .constructor<int, std::string>()
    .function("incrementX", &MyClass::incrementX)
    .property("x", &MyClass::getX, &MyClass::setX)
    .class_function("getStringFromInstance", &MyClass::getStringFromInstance)
    ;
}

    
int main() {
    cout<<"hello,world"<<endl;
    EM_ASM({
        window.onload = function () {
            cout<<"onload"<<endl;
      // 初始化模块后的调用
      Module.onRuntimeInitialized = function () {
        _myFunction();
      }
    }
    });
    

    int value = 10;
    EM_ASM({
        console.log("xxxxx");
        alert("xxx");
        console.log("value = ",$0);
        Module._myFunction();
    },value);  

    int x = EM_ASM_INT({
        return 11;
    });

    cout<<"x = "<<x<<endl;

    call_alert();
    
    // 需要增加编译标识 -s EXTRA_EXPORTED_RUNTIME_METHODS=["ccall","cwrap"]
    int rSqrt = EM_ASM_INT({
         int_sqrt = Module.cwrap('int_sqrt', 'number', ['number']);
         var s = int_sqrt(12);
         console.log('s = ',s);
         return int_sqrt(28);
    },x);
    
    cout<<"rSqrt = "<<rSqrt<<endl;
    
    int result = call_add(20,10);
    cout<<"result = "<<result<<endl;
    
    char* str = call_add_string("123+","456");
    cout<<"str = "<<str<<endl;
    
    int arr[2] = { 30, 45 };
    read_data(arr);
    
    const int argc = 4;
    const char* argv[] = { "ab", "cd", "ef", "gh" };
    call_myFunc(argc, argv);
    EM_ASM({
        var myStr = "TestString";
        console.log(myStr);
        var len = Module.MyStrLen(myStr);
        console.log("len = ",len);
        var str = Module.MyStr(myStr);
        console.log("str = ",str);
    });
    // emcc src/main.cpp -o bin/function.html -s EXPORTED_FUNCTIONS="['_int_sqrt','_main']" --bind
    
    
    
    EM_ASM({
        
         var returnStr = "玄魂";
        var bufferSize = lengthBytesUTF8(returnStr) + 1;
        var buffer = _malloc(bufferSize);
        stringToUTF8(returnStr, buffer, bufferSize);
         var retPtr = Module._outName(buffer);
         console.log(UTF8ToString(retPtr));

          _free(buffer);
    });
    
    EM_ASM({
        var instance = new Module.MyClass(10, "hello");
        instance.incrementX();
        instance.x; // 11
        instance.x = 20; // 20
        var str = Module.MyClass.getStringFromInstance(instance); // "hello"
        console.log('instance.str = ',str);
        instance.delete();
    });

}

// emcc编译命令:emcc src/main.cpp -o bin/function.html -s EXPORTED_FUNCTIONS="['_int_sqrt','_main']"

// Embind启用 : --bind

// ccall 和 cwrap 启用:-s EXTRA_EXPORTED_RUNTIME_METHODS=["ccall","cwrap"]

//emcc: warning: EXTRA_EXPORTED_RUNTIME_METHODS is deprecated, please use EXPORTED_RUNTIME_METHODS instead [-Wdeprecated]

// 启用Pointer_stringify -s EXPORTED_RUNTIME_METHODS=["Pointer_stringify"] 这个没用 最后换成了stringToUTF8 + _malloc + _free

// 最终的emcc编译命令:emcc src/main.cpp -o bin/function.html -s EXPORTED_FUNCTIONS="['_int_sqrt','_main']" -s EXPORTED_RUNTIME_METHODS=["ccall","cwrap"]

评论
成就一亿技术人!
拼手气红包6.0元
还能输入1000个字符
 
红包 添加红包
表情包 插入表情
 条评论被折叠 查看
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包

打赏作者

编程经验随笔

你的鼓励将是我创作的最大动力

¥1 ¥2 ¥4 ¥6 ¥10 ¥20
扫码支付:¥1
获取中
扫码支付

您的余额不足,请更换扫码支付或充值

打赏作者

实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值