webassembly链接C++和JS——embind

文章介绍了Embind库,它是Emscripten的一个功能,用于在C++和JavaScript之间进行函数调用和类的绑定。通过示例展示了如何使用Embind绑定C++的函数和类,以便在JavaScript中调用,同时也支持C++调用JS代码。Embind支持C11和C14标准,并详细解释了构造函数、成员函数、静态函数、属性和静态成员函数的绑定方法。

摘要生成于 C知道 ,由 DeepSeek-R1 满血版支持, 前往体验 >

一、前言

em提供了很多方法来实现js和C++之间的函数调用,包括ccall、cwrap、EM_JS等(参考:Interacting with code)。本文主要介绍的是Embind的方式(参考:Embind)。

Embind可以用来绑定C函数(包含构造、静态、成员,非成员函数)和类,供js使用,同时也支持在C代码中调用js类,且支持C11和C14。

二、头文件

emscripten.h: 使用em必须引入的。

bind.h: Embind的头文件。

三、example

1. A quick example

#include <emscripten/bind.h>

using namespace emscripten;

float lerp(float a, float b, float t) {
    return (1 - t) * a + t * b;
}

EMSCRIPTEN_BINDINGS(my_module) {
    function("lerp", &lerp);
}

通过如下命令编译:

emcc ---bind -o quick_example.js quick_example.cpp

通过如下html文件运行:

<!doctype html>
<html>
  <script>
    var Module = {
      onRuntimeInitialized: function() {
        console.log('lerp result: ' + Module.lerp(1, 2, 0.5));
      }
    };
  </script>
  <script src="quick_example.js"></script>
</html>

说明:

EMSCRIPTEN_BINDINGS是用来绑定C++类、函数以及构造函数到js的定义。其中传入的参数是名称,用来标记相关内容的。

2. Class example

#include <string>

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

	void incrementX();

	int getX() const;
	void setX(int x_);

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

private:
	int x;
	std::string y;
};
#include "hello.h"
#include <emscripten/emscripten.h>
#include <emscripten/bind.h>
using	namespace  emscripten;
void MyClass::incrementX(){x++;}

int MyClass::getX() const{return x;}
void MyClass::setX(int x_){x=x_;}
	
int add(int x, int y){
	return x+y;
}
	
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);
	//非成员函数
	function("add", &add);
}

js端使用:

		var instance = new Module.MyClass(10, "hello");
		instance.incrementX();
		console.log("x=",instance.x); // 11
		instance.x = 20; // 20
		console.log("x=",instance.x);
		console.log("y=",Module.MyClass.getStringFromInstance(instance)); // "hello"
		instance.delete();

说明

其中,constructor表示构造,funciton表示普通成员函数(也可以是非成员函数),property是属性(注意:属性的访问函数必须是const),class_function是静态成员函数。

js调用的话,可以直接通过Module.类名.函数名/属性名访问。其中Module表示唯一实例的名称,是否唯一实例以及实例名称都可以修改,参见emsdk/upstream/emscripten/src/settings.js

3. Value types example

可以将C++的数据结构注册到js中直接使用。

#include <emscripten/emscripten.h>
	#include <emscripten/bind.h>
	#include <array>
using	namespace  emscripten;

class Point{
public:
Point(int x_=0){m_x=x_;}
	int getX() const {return m_x;}
	void setX(int x_){m_x=x_;}
private:
	int m_x;
};

struct Point2f {
    float x;
    float y;
};

struct PersonRecord {
    std::string name;
    int age;
	Point2f pos;
	Point p;
};

// Array fields are treated as if they were std::array<type,size>
struct ArrayInStruct {
    int field[2];
};

PersonRecord findPersonAtLocation(Point2f p)
{
	PersonRecord person;
	person.name = "yao";
	person.name =28;
	person.pos = p;
	person.p.setX(3);
	return person;
}

EMSCRIPTEN_BINDINGS(my_value_example) {
    value_array<Point2f>("Point2f")
        .element(&Point2f::x)
        .element(&Point2f::y)
        ;

	emscripten::class_<Point>("Point")
	 .constructor<int>()
	.property("m_x", &Point::getX, &Point::setX);
	
    value_object<PersonRecord>("PersonRecord")
        .field("name", &PersonRecord::name)
        .field("age", &PersonRecord::age)
		.field("pos", &PersonRecord::pos)
		.field("p", &PersonRecord::p)
        ;

    value_object<ArrayInStruct>("ArrayInStruct")
        .field("field", &ArrayInStruct::field) // Need to register the array type
        ;

    // Register std::array<int, 2> because ArrayInStruct::field is interpreted as such
    value_array<std::array<int, 2>>("array_int_2")
        .element(emscripten::index<0>())
        .element(emscripten::index<1>())
        ;

    function("findPersonAtLocation", &findPersonAtLocation);
}

js端调用:

		var person = Module.findPersonAtLocation([10.2, 156.5]);
		console.log(person);
		console.log("p.m_x",person.p.m_x);

注意:这里是允许class与struct嵌套使用的,只要都注册了,在js端都可以直接访问。

4. 容器example

#include <emscripten/bind.h>
#include <string>
#include <vector>

using namespace emscripten;

std::vector<int> returnVectorData () {
  std::vector<int> v(10, 1);
  return v;
}

std::map<int, std::string> returnMapData () {
  std::map<int, std::string> m;
  m.insert(std::pair<int, std::string>(10, "This is a string."));
  return m;
}

EMSCRIPTEN_BINDINGS(module) {
  function("returnVectorData", &returnVectorData);
  function("returnMapData", &returnMapData);

  // register bindings for std::vector<int> and std::map<int, std::string>.
  register_vector<int>("vector<int>");
  register_map<int, std::string>("map<int, string>");
}

js段使用:

var retVector = Module['returnVectorData']();

// vector size
var vectorSize = retVector.size();

// reset vector value
retVector.set(vectorSize - 1, 11);

// push value into vector
retVector.push_back(12);

// retrieve value from the vector
for (var i = 0; i < retVector.size(); i++) {
    console.log("Vector Value: ", retVector.get(i));
}

// expand vector size
retVector.resize(20, 1);

var retMap = Module['returnMapData']();

// map size
var mapSize = retMap.size();

// retrieve value from map
console.log("Map Value: ", retMap.get(10));

// figure out which map keys are available
// NB! You must call `register_vector<key_type>`
// to make vectors available
var mapKeys = retMap.keys();
for (var i = 0; i < mapKeys.size(); i++) {
    var key = mapKeys.get(i);
    console.log("Map key/value: ", key, retMap.get(key));
}

// reset the value at the given index position
retMap.set(10, "OtherValue");

注意:容器注册之后,在js端只能通过set和get方法访问,而不能像数组一样通过[]访问。

相关链接:在js端传入容器类型到C++接口时需要创建对象。

评论 4
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

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

余额充值