Embind用于将C ++和JavaScript交互

官方文档

Embind

Embind is used to bind C++ functions and classes to JavaScript, so that the compiled code can be used in a natural way by “normal” JavaScript. Embind also supports calling JavaScript classes from C++.

Embind has support for binding most C++ constructs, including those introduced in C++11 and C++14. Its only significant limitation is that it does not currently support raw pointers with complicated lifetime semantics.

This article shows how to use EMSCRIPTEN_BINDINGS() blocks to create bindings for functions, classes, value types, pointers (including both raw and smart pointers), enums, and constants, and how to create bindings for abstract classes that can be overridden in JavaScript. It also briefly explains how to manage the memory of C++ object handles passed to JavaScript.

Tip

In addition to the code in this article:

  • There are many other examples of how to use Embind in the Test Suite.
  • Connecting C++ and JavaScript on the Web with Embind (slides from CppCon 2014) contains more examples and information about Embind’s design philosophy and implementation.

Note

Embind was inspired by Boost.Python and uses a very similar approach for defining bindings.

A quick example

The following code uses an EMSCRIPTEN_BINDINGS() block to expose the simple C++ lerp() function() to JavaScript.

// quick_example.cpp
#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);
}

To compile the above example using embind, we invoke emcc with the bind option:

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

The resulting quick_example.js file can be loaded as a node module or via a <script> tag:

<!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>

Note

We use the onRuntimeInitialized callback to run code when the runtime is ready, which is an asynchronous operation (in order to compile WebAssembly).

Note

Open the developer tools console to see the output of console.log.

The code in an EMSCRIPTEN_BINDINGS() block runs when the JavaScript file is initially loaded (at the same time as the global constructors). The function lerp()’s parameter types and return type are automatically inferred by embind.

All symbols exposed by embind are available on the Emscripten Module object.

Important

Always access objects through the Module object object, as shown above.

While the objects are also available in the global namespace by default, there are cases where they will not be (for example, if you use the closure compiler to minify code or wrap compiled code in a function to avoid polluting the global namespace). You can of course use whatever name you like for the module by assigning it to a new variable: var MyModuleName = Module;.

Classes

Exposing classes to JavaScript requires a more complicated binding statement. For example:

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)
    ;
}

The binding block defines a chain of member function calls on the temporary class_ object (this same style is used in Boost.Python). The functions register the class, its constructor(), member function()class_function() (static) and property().

Note

This binding block binds the class and all its methods. As a rule you should bind only those items that are actually needed, as each binding increases the code size. For example, it would be rare to bind private or internal methods.

An instance of MyClass can then be created and used in JavaScript as shown below:

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

Memory management

JavaScript, specifically ECMA-262 Edition 5.1, does not support finalizers or weak references with callbacks. Therefore there is no way for Emscripten to automatically call the destructors on C++ objects.

Warning

JavaScript code must explicitly delete any C++ object handles it has received, or the Emscripten heap will grow indefinitely.

var x = new Module.MyClass;
x.method();
x.delete();

var y = Module.myFunctionThatReturnsClassInstance();
y.method();
y.delete();

Value types

Manual memory management for basic types is onerous, so embind provides support for value types. Value arrays are converted to and from JavaScript Arrays and value objects are converted to and from JavaScript Objects.

Consider the example below:

struct Point2f {
    float x;
    float y;
};

struct PersonRecord {
    std::string name;
    int age;
};

PersonRecord findPersonAtLocation(Point2f);

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

    value_object<PersonRecord>("PersonRecord")
        .field("name", &PersonRecord::name)
        .field("age", &PersonRecord::age)
        ;

    function("findPersonAtLocation", &findPersonAtLocation);
}

The JavaScript code does not need to worry about lifetime management.

var person = Module.findPersonAtLocation([10.2, 156.5]);
console.log('Found someone! Their name is ' + person.name + ' and they are ' + person.age + ' years old');

Advanced class concepts

Raw pointers

Because raw pointers have unclear lifetime semantics, embind requires their use to be marked with allow_raw_pointers.

For example:

class C {};
C* passThrough(C* ptr) { return ptr; }
EMSCRIPTEN_BINDINGS(raw_pointers) {
    class_<C>("C");
    function("passThrough", &passThrough, allow_raw_pointers());
}

Note

Currently the markup serves only to whitelist raw pointer use, and show that you’ve thought about the use of the raw pointers. Eventually we hope to implement Boost.Python-like raw pointer policies for managing object ownership.

External constructors

There are two ways to specify constructors for a class.

The zero-argument template form invokes the natural constructor with the arguments specified in the template. For example:

class MyClass {
public:
  MyClass(int, float);
  void someFunction();
};

EMSCRIPTEN_BINDINGS(external_constructors) {
  class_<MyClass>("MyClass")
    .constructor<int, float>()
    .function("someFunction", &MyClass::someFunction)
    ;
}

The second form of the constructor takes a function pointer argument, and is used for classes that construct themselves using a factory function. For example:

class MyClass {
  virtual void someFunction() = 0;
};
MyClass* makeMyClass(int, float); //Factory function.

EMSCRIPTEN_BINDINGS(external_constructors) {
  class_<MyClass>("MyClass")
    .constructor(&makeMyClass, allow_raw_pointers())
    .function("someFunction", &MyClass::someFunction)
    ;
}

The two constructors present exactly the same interface for constructing the object in JavaScript. Continuing the example above:

var instance = new MyClass(10, 15.5);
// instance is backed by a raw pointer to a MyClass in the Emscripten heap

Smart pointers

To manage object lifetime with smart pointers, embind must be told about the smart pointer type.

For example, consider managing a class C’s lifetime with std::shared_ptr<C>. The best way to do this is to use smart_ptr_constructor() to register the smart pointer type:

EMSCRIPTEN_BINDINGS(better_smart_pointers) {
    class_<C>("C")
        .smart_ptr_constructor("C", &std::make_shared<C>)
        ;
}

When an object of this type is constructed (e.g. using new Module.C()) it returns a std::shared_ptr<C>.

An alternative is to use smart_ptr() in the EMSCRIPTEN_BINDINGS() block:

EMSCRIPTEN_BINDINGS(smart_pointers) {
    class_<C>("C")
        .constructor<>()
        .smart_ptr<std::shared_ptr<C>>("C")
        ;
}

Using this definition, functions can return std::shared_ptr<C> or take std::shared_ptr<C> as arguments, but new Module.C() would still return a raw pointer.

unique_ptr

embind has built-in support for return values of type std::unique_ptr.

Custom smart pointers

To teach embind about custom smart pointer templates, you must specialize the smart_ptr_trait template.

Non-member-functions on the JavaScript prototype

Methods on the JavaScript class prototype can be non-member functions, as long as the instance handle can be converted to the first argument of the non-member function. The classic example is when the function exposed to JavaScript does not exactly match the behavior of a C++ method.

struct Array10 {
    int& get(size_t index) {
        return data[index];
    }
    int data[10];
};

val Array10_get(Array10& arr, size_t index) {
    if (index < 10) {
        return val(arr.get(index));
    } else {
        return val::undefined();
    }
}

EMSCRIPTEN_BINDINGS(non_member_functions) {
    class_<Array10>("Array10")
        .function("get", &Array10_get)
        ;
}

If JavaScript calls Array10.prototype.get with an invalid index, it will return undefined.

Deriving from C++ classes in JavaScript

If C++ classes have virtual or abstract member functions, it’s possible to override them in JavaScript. Because JavaScript has no knowledge of the C++ vtable, embind needs a bit of glue code to convert C++ virtual function calls into JavaScript calls.

Abstract methods

Let’s begin with a simple case: pure virtual functions that must be implemented in JavaScript.

struct Interface {
    virtual void invoke(const std::string& str) = 0;
};

struct InterfaceWrapper : public wrapper<Interface> {
    EMSCRIPTEN_WRAPPER(InterfaceWrapper);
    void invoke(const std::string& str) {
        return call<void>("invoke", str);
    }
};

EMSCRIPTEN_BINDINGS(interface) {
    class_<Interface>("Interface")
        .function("invoke", &Interface::invoke, pure_virtual())
        .allow_subclass<InterfaceWrapper>("InterfaceWrapper")
        ;
}

allow_subclass() adds two special methods to the Interface binding: extend and implementextend allows JavaScript to subclass in the style exemplified by Backbone.js. implement is used when you have a JavaScript object, perhaps provided by the browser or some other library, and you want to use it to implement a C++ interface.

Note

The pure_virtual annotation on the function binding allows JavaScript to throw a helpful error if the JavaScript class does not override invoke(). Otherwise, you may run into confusing errors.

extend example

var DerivedClass = Module.Interface.extend("Interface", {
    // __construct and __destruct are optional.  They are included
    // in this example for illustration purposes.
    // If you override __construct or __destruct, don't forget to
    // call the parent implementation!
    __construct: function() {
        this.__parent.__construct.call(this);
    },
    __destruct: function() {
        this.__parent.__destruct.call(this);
    },
    invoke: function() {
        // your code goes here
    },
});

var instance = new DerivedClass;

implement example

var x = {
    invoke: function(str) {
        console.log('invoking with: ' + str);
    }
};
var interfaceObject = Module.Interface.implement(x);

Now interfaceObject can be passed to any function that takes an Interface pointer or reference.

Non-abstract virtual methods

If a C++ class has a non-pure virtual function, it can be overridden — but does not have to be. This requires a slightly different wrapper implementation:

struct Base {
    virtual void invoke(const std::string& str) {
        // default implementation
    }
};

struct BaseWrapper : public wrapper<Base> {
    EMSCRIPTEN_WRAPPER(BaseWrapper);
    void invoke(const std::string& str) {
        return call<void>("invoke", str);
    }
};

EMSCRIPTEN_BINDINGS(interface) {
    class_<Base>("Base")
        .allow_subclass<BaseWrapper>("BaseWrapper")
        .function("invoke", optional_override([](Base& self, const std::string& str) {
            return self.Base::invoke(str);
        }))
        ;
}

When implementing Base with a JavaScript object, overriding invoke is optional. The special lambda binding for invoke is necessary to avoid infinite mutual recursion between the wrapper and JavaScript.

Base classes

Base class bindings are defined as shown:

EMSCRIPTEN_BINDINGS(base_example) {
    class_<BaseClass>("BaseClass");
    class_<DerivedClass, base<BaseClass>>("DerivedClass");
}

Any member functions defined on BaseClass are then accessible to instances of DerivedClass. In addition, any function that accepts an instance of BaseClass can be given an instance of DerivedClass.

Automatic downcasting

If a C++ class is polymorphic (that is, it has a virtual method), then embind supports automatic downcasting of function return values.

class Base { virtual ~Base() {} }; // the virtual makes Base and Derived polymorphic
class Derived : public Base {};
Base* getDerivedInstance() {
    return new Derived;
}
EMSCRIPTEN_BINDINGS(automatic_downcasting) {
    class_<Base>("Base");
    class_<Derived, base<Base>>("Derived");
    function("getDerivedInstance", &getDerivedInstance, allow_raw_pointers());
}

Calling Module.getDerivedInstance from JavaScript will return a Derived instance handle from which all of Derived’s methods are available.

Note

Embind must understand the fully-derived type for automatic downcasting to work.

Overloaded functions

Constructors and functions can be overloaded on the number of arguments, but embind does not support overloading based on type. When specifying an overload, use the select_overload() helper function to select the appropriate signature.

struct HasOverloadedMethods {
    void foo();
    void foo(int i);
    void foo(float f) const;
};

EMSCRIPTEN_BINDING(overloads) {
    class_<HasOverloadedMethods>("HasOverloadedMethods")
        .function("foo", select_overload<void()>(&HasOverloadedMethods::foo))
        .function("foo_int", select_overload<void(int)>(&HasOverloadedMethods::foo))
        .function("foo_float", select_overload<void(float)const>(&HasOverloadedMethods::foo))
        ;
}

Enums

Embind’s enumeration support works with both C++98 enums and C++11 “enum classes”.

enum OldStyle {
    OLD_STYLE_ONE,
    OLD_STYLE_TWO
};

enum class NewStyle {
    ONE,
    TWO
};

EMSCRIPTEN_BINDINGS(my_enum_example) {
    enum_<OldStyle>("OldStyle")
        .value("ONE", OLD_STYLE_ONE)
        .value("TWO", OLD_STYLE_TWO)
        ;
    enum_<NewStyle>("NewStyle")
        .value("ONE", NewStyle::ONE)
        .value("TWO", NewStyle::TWO)
        ;
}

In both cases, JavaScript accesses enumeration values as properties of the type.

Module.OldStyle.ONE;
Module.NewStyle.TWO;

Constants

To expose a C++ constant() to JavaScript, simply write:

EMSCRIPTEN_BINDINGS(my_constant_example) {
    constant("SOME_CONSTANT", SOME_CONSTANT);
}

SOME_CONSTANT can have any type known to embind.

Memory views

In some cases it is valuable to expose raw binary data directly to JavaScript code as a typed array, allowing it to be used without copying. This is useful for instance for uploading large WebGL textures directly from the heap.

Memory views should be treated like raw pointers; lifetime and validity are not managed by the runtime and it’s easy to corrupt data if the underlying object is modified or deallocated.

#include <emscripten/bind.h>
#include <emscripten/val.h>

using namespace emscripten;

unsigned char *byteBuffer = /* ... */;
size_t bufferLength = /* ... */;

val getBytes() {
    return val(typed_memory_view(bufferLength, byteBuffer));
}

EMSCRIPTEN_BINDINGS(memory_view_example) {
    function("getBytes", &getBytes);
}

The calling JavaScript code will receive a typed array view into the emscripten heap:

var myUint8Array = Module.getBytes()
var xhr = new XMLHttpRequest();
xhr.open('POST', /* ... */);
xhr.send(myUint8Array);

The typed array view will be of the appropriate matching type, such as Uint8Array for an unsigned char array or pointer.

Using val to transliterate JavaScript to C++

Embind provides a C++ class, emscripten::val, which you can use to transliterate JavaScript code to C++. Using val you can call JavaScript objects from your C++, read and write their properties, or coerce them to C++ values like a boolint, or std::string.

The example below shows how you can use val to call the JavaScript Web Audio API from C++:

Note

This example is based on the excellent Web Audio tutorial: Making sine, square, sawtooth and triangle waves (stuartmemo.com). There is an even simpler example in the emscripten::val documentation.

First consider the JavaScript below, which shows how to use the API:

// Get web audio api context
var AudioContext = window.AudioContext || window.webkitAudioContext;

// Got an AudioContext: Create context and OscillatorNode
var context = new AudioContext();
var oscillator = context.createOscillator();

// Configuring oscillator: set OscillatorNode type and frequency
oscillator.type = 'triangle';
oscillator.frequency.value = 261.63; // value in hertz - middle C

// Playing
oscillator.connect(context.destination);
oscillator.start();

// All done!

The code can be transliterated to C++ using val, as shown below:

#include <emscripten/val.h>
#include <stdio.h>
#include <math.h>

using namespace emscripten;

int main() {
  val AudioContext = val::global("AudioContext");
  if (!AudioContext.as<bool>()) {
    printf("No global AudioContext, trying webkitAudioContext\n");
    AudioContext = val::global("webkitAudioContext");
  }

  printf("Got an AudioContext\n");
  val context = AudioContext.new_();
  val oscillator = context.call<val>("createOscillator");

  printf("Configuring oscillator\n");
  oscillator.set("type", val("triangle"));
  oscillator["frequency"].set("value", val(261.63)); // Middle C

  printf("Playing\n");
  oscillator.call<void>("connect", context["destination"]);
  oscillator.call<void>("start", 0);

  printf("All done!\n");
}

First we use global() to get the symbol for the global AudioContext object (or webkitAudioContext if that does not exist). We then use new_() to create the context, and from this context we can create an oscillatorset() its properties (again using val) and then play the tone.

The example can be compiled on the Linux/macOS terminal with:

emcc -O2 -Wall -Werror --bind -o oscillator.html oscillator.cpp

Built-in type conversions

Out of the box, embind provides converters for many standard C++ types:

C++ type JavaScript type
void undefined
bool true or false
char Number
signed char Number
unsigned char Number
short Number
unsigned short Number
int Number
unsigned int Number
long Number
unsigned long Number
float Number
double Number
std::string ArrayBuffer, Uint8Array, Uint8ClampedArray, Int8Array, or String
std::wstring String (UTF-16 code units)
emscripten::val anything

For convenience, embind provides factory functions to register std::vector<T> (register_vector()) and std::map<K, V> (register_map()) types:

EMSCRIPTEN_BINDINGS(stl_wrappers) {
    register_vector<int>("VectorInt");
    register_map<int,int>("MapIntInt");
}

A full example is shown below:

#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>");
}

The following JavaScript can be used to interact with the above C++.

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");

Performance

At time of writing there has been no comprehensive embind performance testing, either against standard benchmarks, or relative to WebIDL Binder.

The call overhead for simple functions has been measured at about 200 ns. While there is room for further optimisation, so far its performance in real-world applications has proved to be more than acceptable.

Next  Previous

热门文章

暂无图片
编程学习 ·

使用ssh连接window和 oracle virtualBox中的虚拟机 保姆级教程

目录环境基本连接步骤进一步配置hostname环境虚拟机 VM Virtualbox 6.1 虚拟机系统 debain 10.3虚拟机网卡:1.虚拟机网卡2.主机 windows 10配置好以上环境后开始配置虚拟机,当然host-only网络适配器的ip地址可以自己设置。 基本连接步骤 第一步: 虚拟机中运行 ps -e | gr…
暂无图片
编程学习 ·

Paddle_程序员必备的数学知识_转发

程序员——必备数学知识!!!Attention 本博客转发至百度aistudio的<深度学习7日入门-cv疫情检测>,课程非常棒!本人力推! 博客转发地址:https://aistudio.baidu.com/aistudio/projectdetail/604807 课程报名地址:https://aistudio.baidu.com/aistudio/education/group/i…
暂无图片
编程学习 ·

算法复杂度评价指标(大o表示法)

大O表示法(1)常见的大o数量级函数(2)其他算法复杂度表示法 基本操作数量函数T(n)的精确值并不是特别重要,重要的是Tn(n)中起决定性因素的主导部分。用动态的眼光看,就是当问题规模增大的时候,T(n)中的一些部分会盖过其他部分的贡献。 数量级函数描述了T(n)中随着n增加而…
暂无图片
编程学习 ·

shell(1)

diff -b 忽略空格 -B 忽略空行 -i 忽略大小写 -c 显示文件所有内容并标不同 -r 对比输出 -u 合并输出 patch patch 源文件 布丁文件 -b 备份原文件 cut 检测apache服务器被谁访问的次数最多,显示访问者ip并显示访问次数 cat /etc/httpd/logs/access_log | cut…
暂无图片
编程学习 ·

Java语言基础之基础语法&流程控制语句&数组

Java语言概述 Java语言是一款面向对象的高级语言,是由Sun Microsystems公司(现已被oracle公司收购)。由James Gosling和同事们共同研发,并在1995年正式推出,据oracle官方数据指数,目前全球已有上亿的系统是使用Java开发的。Java是一门面向对象编程语言,不仅吸收了C++语言…
暂无图片
编程学习 ·

jetson测试多个CSI摄像头(python版)

首先,CSI摄像头和USB摄像头的调用方式是不一样的。 遇到过的错误 linano@jetson-nano$:python3 mpy-Copyl.py [ WARN:0 ] qlobal /home/nvidia/host/build opencv/nv opencv/modules/videoio/src/cap qstreamer. Cpp (1757) handleMessage OpenCV | GStreamer warninq: Embedd…
暂无图片
编程学习 ·

Web服务器防护技术你了解多少?

技术的迅速发展,给人们提供便利的同时,也给人们带来了威胁。通常情况下,黑客、病毒会利用系统的漏洞来进行网络攻击,如篡改网页、蔓延病毒等,从而造成用户信息的窃取、重要数据的破坏。因此,要对web服务器的安全问题引起足够的重视,要加大安全防护力度、构建安全防护系统…
暂无图片
编程学习 ·

Maven常用命令总结大纲

Maven常用命令总结大纲一、maven的作用二、maven的下载(国内镜像),安装,配置流程1、打开https://maven.apache.org/并点击下载2、点击箭头所指处的Zip文件3、解压并配置环境变量4、配置国内镜像(阿里云镜像)三、常见的maven命令总结四、多模块springboot项目中的maven打包方式…
暂无图片
编程学习 ·

利用Spring提供的事务监听ApplicationEvent完成事件

前段时间开发项目时,碰到一个数据库事务还没提交,但是发送MQ已经被消费者消费,导致了数据不同步问题。具体是这样子的,一般我们会在@Service类中去处理数据库的操作及其他服务处理,一般都会在涉及到数据库的增删改的方法上添加@Transactional注解,表示这个方法被托管给sp…
暂无图片
编程学习 ·

mvc:resources 配置不生效问题

学习springMVC框架的时候,用到jquery,于是配置mvc:resources标签,可是依然会拦截js文件,网上搜了很久都找不到原因,配置如下:于是仔细观察,原来是mapping和location写反了?修改如下:这样居然就可以了?大家普遍出现的关于mvc:resources标签的问题我就不说了,网上也会…
暂无图片
编程学习 ·

C++--找出三条能构成三角形且周长最大的边的一个普通方法

题目:给定由一些正数(代表长度)组成的数组 A,返回由其中三个长度组成的、面积不为零的三角形的最大周长。如果不能形成任何面积不为零的三角形,返回 0。 #输出示例 输入:[2,1,2] 输出:5 输入:[1,5,1] 输出:0 输入:[3,2,3,4] 输出:10 输入:[3,6,2,3] 输出:8 #inclu…
暂无图片
编程学习 ·

STM32HAL库的串口通讯流程详解

一 查询模式 待定,日后补充 二 中断模式 1 中断接收 1.1中断接收流程 先在启动文件中找到中断向量表(此处以USART1为例子)然后找到USART1_IRQHandler的函数定义在这里就会发现又跳转到了 HAL_UART_IRQHandler(&huart1);,追进去为下图所示:在图中标记的前三步是读取寄存…
暂无图片
编程学习 ·

关于this对象

this对象是是在运行时基于函数的执行环境绑定的 var object = {name: "My Object",getName: function(){return this.name; } } let b = object.getName; console.log(object.getName()); // "My Object" console.log(b()); // undefined 在全局函数中,…
暂无图片
编程学习 ·

JAVA中集合的迭代/遍历方法iterator()

JAVA中集合的迭代/遍历方法以下讲解的遍历方式/迭代方式,是所有Collection通用的一种方式在Map集合中不能使用,在所有的Collection以及子类中使用第一步:创建迭代器对象: Iterator it = e.iterator();第二步:通过以上获取的迭代器对象开始迭代/遍历集合 以下两个方法为迭代…