admin 管理员组

文章数量: 887176


2024年1月12日发(作者:穿梭时空的侠客女主角几个)

NodeJS和C++之间的类型转换

虽然在 官方网站有很多的关于怎么使用这些 API 的文档,但是在 JavaScript 和

C++ 之间传递数据是一件非常麻烦的事情,C++ 是强类型语言(”1024” 是字符串类型而不是整数类型),而 JavaScript 却总是默认的帮我们做一些类型转换。

作者:慎里来源:慎里|2016-12-07 11:23

收藏

分享

我非常喜欢使用 ,但是当涉及到计算密集型的场景时 就不能够很好地胜任了。而在这样的情况下 C++ 是一个很好的选择,非常幸运

官方提供了C/C++ Addons 的机制让我们能够使用 V8 API 把

和 C++ 结合起来。

虽然在 官方网站有很多的关于怎么使用这些 API 的文档,但是在

JavaScript 和 C++ 之间传递数据是一件非常麻烦的事情,C++ 是强类型语言(”1024” 是字符串类型而不是整数类型),而 JavaScript 却总是默认的帮我们做一些类型转换。

JavaScript 的基本类型包括 String,Number,Boolean,null,undefined,V8 使用类继承的方式来定义这类型,这些类型都继承了 Primitive 类,而 Primitive 继承了 Value,v8 也支持整型(包括 Int32 和 Uint32),而所有的类型定义都可以从 V8 类型文档中看到,除了基本的类型,还有 Object,Array,Map 等类型的定义。

基本类型的继承关系如下图:

在 V8 中所有 JavaScript 值都是被放在 Local 对象中,通过这个对象指定了

JavaScript 运行时的内存单元。

下面这段代定义了一个 Number 类型的值,其中 Test 函数中声明的 isolate 变量代表着 V8 虚拟机中的堆内存,当创建新变量的时候就需要用到它,接下来的一行代码就通过 isolate 声明了一个 Number 类型的变量。

1.

#include

2.

#include

3.

4.

using namespace v8;

5.

6.

void Test(const v8::FunctionCallbackInfo& args) {

7.

Isolate* isolate = late();

8.

// 声明变量

9.

Local retval = v8::Number::New(isolate, 1000);

10.

}

11.

12.

void init(Local exports, Local module) {

13.

NODE_SET_METHOD(exports, "getTestValue", Test);

14.

}

15.

16.

NODE_MODULE(returnValue, init)

看了 V8 类型 API 文档 你会发现对于基本的 JavaScript 类型,只有变量的声明而没有变量的赋值。最初想可能觉得这个非常的奇怪,可是仔细想一想后发现这个是合理的。主要由以下几点原因:

JavaScript 的基本类型是不可变类型,变量都是指向一个不可变的内存单元,var a = 10,则 a 指向的内存单元中包含的值为 5,重新赋值 a = 100,没有改变这个内存单元的值,而是使得 a 指向了另外一个内存单元,其中的值为 100。如果声明两个变量 x,y 的值都为 10,则他们指向的是同一个内存单元。

函数的传参都是传值,而不是传引用,当在 JavaScript 中调用 C++ 的函数时,如果参数是基本类型则每次都是把这个值拷贝过去,改变参数的值不会影响原来的值。

使用 Local 声明基本类型的变量都是对内存单元的引用,因为第一条原因不可能改变引用的值使其指向另外一个内存单元,因此不存在变量的重新赋值。

数据流向 C++ -> JavaScript

下面 demo 定义了一些常用的 JavaScript 类型,包括基本类型的以及

Object, Array, Fuction。

1.

#include

2.

#include

3.

4.

using namespace v8;

5.

6.

void MyFunction(const v8::FunctionCallbackInfo& args) {

7.

Isolate* isolate = late();

8.

urnValue().Set(String::NewFromUtf8(isolate, "Hello World!"));

9.

}

10.

11.

void Test(const v8::FunctionCallbackInfo& args) {

12.

Isolate* isolate = late();

13.

14.

// Number 类型的声明

15.

Local retval = v8::Number::New(isolate, 1000);

16.

17.

// String 类型的声明

18.

Local str = v8::String::NewFromUtf8(isolate, "Hello World!");

19.

20.

// Object 类型的声明

21.

Local obj = v8::Object::New(isolate);

22.

// 对象的赋值

23.

obj->Set(v8::String::NewFromUtf8(isolate, "arg1"), str);

24.

obj->Set(v8::String::NewFromUtf8(isolate, "arg2"), retval);

25.

26.

// Function 类型的声明并赋值

27.

Local tpl = v8::FunctionTemplate::New(isolate, MyFunction);

28.

Local fn = tpl->GetFunction();

29.

// 函数名字

30.

fn->SetName(String::NewFromUtf8(isolate, "theFunction"));

31.

obj->Set(v8::String::NewFromUtf8(isolate, "arg3"), fn);

32.

33.

// Boolean 类型的声明

34.

Local flag = Boolean::New(isolate, true);

35.

obj->Set(String::NewFromUtf8(isolate, "arg4"), flag);

36.

37.

// Array 类型的声明

38.

Local arr = Array::New(isolate);

39.

// Array 赋值

40.

arr->Set(0, Number::New(isolate, 1));

41.

arr->Set(1, Number::New(isolate, 10));

42.

arr->Set(2, Number::New(isolate, 100));

43.

arr->Set(3, Number::New(isolate, 1000));

44.

obj->Set(String::NewFromUtf8(isolate, "arg5"), arr);

45.

46.

// Undefined 类型的声明

47.

Local und = Undefined(isolate);

48.

obj->Set(String::NewFromUtf8(isolate, "arg6"), und);

49.

50.

// null 类型的声明

51.

Local null = Null(isolate);

52.

obj->Set(String::NewFromUtf8(isolate, "arg7"), null);

53.

54.

// 返回给 JavaScript 调用时的返回值

55.

urnValue().Set(obj);

56.

}

57.

58.

void init(Local exports, Local module) {

59.

NODE_SET_METHOD(exports, "getTestValue", Test);

60.

}

61.

62.

NODE_MODULE(returnValue, init)

所有的 addon 都需要一个初始化的函数,如下面的代码:

1.

void Initialize(Local exports);

2.

NODE_MODULE(module_name, Initialize)

Initialize 是初始化的函数,module_name 是编译后产生的二进制文件名,上述代码的模块名为returnValue。

上述代码通过 node-gyp 编译后(编译过程官方文档 C/C++ Addons 有详细的介绍),可以通过如下的方式调用。

1.

// 这个文件就是编译后产生的文件,通过 NODE_MODULE(returnValue, init) 决定的文件名

2.

const returnValue = require('./build/Release/');

3.

(tValue());

运行结果如下:

数据流向 javaScript -> C++

上面的 demo 展示了怎样在在 C++ 定义 JavaScript 类型,数据的是从

C++ 流向 JavaScript,反过来数据也需要从 javaScript 流向 C++,也就是调用 C++ 函数的时候需要传入一些参数。

下面的代码展示了参数个数判断,参数类型判断,以及参数类型装换成 V8 类型的过程,包括基本类型以及 Object, Array, Fuction。

1.

#include

2.

#include

3.

#include

4.

5.

using namespace v8;

6.

using namespace std;

7.

8.

void GetArgument(const FunctionCallbackInfo& args) {

9.

Isolate* isolate = late();

10.

11.

// 参数长度判断

12.

if (() < 2) {

13.

isolate->ThrowException(Exception::TypeError(

14.

String::NewFromUtf8(isolate, "Wrong number of arguments")));

15.

return;

16.

}

17.

18.

// 参数类型判断

19.

if (!args[0]->IsNumber() || !args[1]->IsNumber()) {

20.

//抛出错误

21.

isolate->ThrowException(Exception::TypeError(

22.

String::NewFromUtf8(isolate, "argumnets must be number")));

23.

}

24.

25.

if (!args[0]->IsObject()) {

26.

printf("I am not Objectn");

27.

}

28.

29.

if (!args[0]->IsBoolean()) {

30.

printf("I am not Booleann");

31.

}

32.

33.

if (!args[0]->IsArray()) {

34.

printf("I am not Arrayn");

35.

}

36.

37.

if (!args[0]->IsString()) {

38.

printf("I am not Stringn");

39.

}

40.

41.

if (!args[0]->IsFunction()) {

42.

printf("I am not Functionn");

43.

}

44.

45.

if (!args[0]->IsNull()) {

46.

printf("I am not Nulln");

47.

}

48.

49.

if (!args[0]->IsUndefined()) {

50.

printf("I am not Undefinedn");

51.

}

52.

53.

// js Number 类型转换成 v8 Number 类型

54.

Local value1 = Local::Cast(args[0]);

55.

Local value2 = Local::Cast(args[1]);

56.

double value = value1->NumberValue() + value2->NumberValue();

57.

58.

// js String 类型转换成 v8 String 类型

59.

Local str = Local::Cast(args[2]);

60.

String::Utf8Value utfValue(str);

61.

cout<

62.

63.

// js Array 类型转换成 v8 Array 类型

64.

Local input_array = Local::Cast(args[3]);

65.

printf("%d, %f %fn", input_array->Length(), input_array->Get(0)->NumberValue(), input_array->Get(1)->NumberValue());

66.

67.

// js Object 类型转换成 v8 Object 类型

68.

Local obj = Local::Cast(args[4]);

69.

70.

// 根据 key 获取对象中的值

71.

Local a = obj->Get(String::NewFromUtf8(isolate, "a"));

72.

Local b = obj->Get(String::NewFromUtf8(isolate, "b"));

73.

74.

// js Array 类型转换成 v8 Array 类型

75.

Local c = Local::Cast(obj->Get(String::NewFromUtf8(isolate,

"c")));

76.

cout<NumberValue()<<" "<NumberValue()<

77.

printf("%d, %f %fn", c->Length(), c->Get(0)->NumberValue(), c->Get(1)->NumberValue());

78.

79.

// js String 类型转换成 v8 String 类型

80.

Local cString = Local::Cast(c->Get(2));

81.

String::Utf8Value utfValueD(cString);

82.

cout<

83.

84.

// 根据 key 获取对象中的值

85.

Local d = Local::Cast(obj->Get(String::NewFromUtf8(isolate, "d")));

86.

Local dString1 = Local::Cast(d->Get(String::NewFromUtf8(isolate, "m")));

87.

String::Utf8Value utfValued1(dString1);

88.

cout<

89.

90.

// 根据 key 获取对象中的值

91.

Local dString2 = Local::Cast(d->Get(String::NewFromUtf8(isolate, "n")));

92.

String::Utf8Value utfValued2(dString2);

93.

cout<

94.

95.

// js Booelan 类型转换成 v8 Boolean 类型

96.

Local FlagTrue = Local::Cast(args[5]);

97.

cout<<"Flag: "<BooleanValue()<

98.

99.

// js Function 类型转换成 v8 Function 类型

100.

Local cb = Local::Cast(args[8]);

101.

const unsigned argc = 2;

102.

Local argv[2];

103.

argv[0] = a;

104.

argv[1] = b;

105.

cb->Call(Null(isolate), argc, argv);

106.

107.

urnValue().Set(value);

108.

}

109.

110.

void Init(Local exports, Local module) {

111.

NODE_SET_METHOD(module, "exports", GetArgument);

112.

}

113.

114.

NODE_MODULE(argumentss, Init)

运行结果如下:

关于其他的类型,我这里就就不一一介绍,V8 文档里面都有对应的 API。

NAN

由于 V8 的 API 还没有彻底稳定下来,所以对于不同版本的 类型相关的 API 会发生变化,而 NAN 帮我们做了封装,在编码的时候不需要关心版本问题,只需要引入相应的头文件即可。

引入头文件后,可以如下使用方式:

v8::Local Nan::Undefined()

v8::Local Nan::Null()

参考资料

Type conversions from JavaScript to C++ in V8

node addon

v8 types documentation

node-gyp

gyp user documentation

nan

【编辑推荐】

1. 2016年12月编程语言排行榜:C作为优秀老语言 评级不足10%

2. Java程序员:工作还是游戏 该好好衡量一下

3. 常见的JavaScript易错知识点整理

4. .NET Core首例Office开源跨平台组件(NPOI Core)

5. JavaScript中Write和Writeln的区别

6. 能用HTML/CSS解决的问题就不要使用JS

7. Python爬虫爬取美剧网站


本文标签: 类型 参数 需要 变量 基本

更多相关文章

windows系统下常用系统目录变量

3月前

[db:摘要]

r9 7940h参数 r9 7940h性能怎么样 r97940h相当于什么水平

3月前

[db:摘要]

联想台式计算机参数的意义,电脑BIOS的作用简介及使用注意

3月前

[db:摘要]

MBR&amp;GPT硬盘分区类型&amp;属性详解(Win下更改设置OEM恢复分区方法)

3月前

[db:摘要]

linux 分区格式化类型,Linux分区格式化

3月前

[db:摘要]

读取注册表获取Windows系统XP7810类型

3月前

[db:摘要]

java easypoi导出word时添加空白页导出多组相同类型的数据

3月前

[db:摘要]

openai公司的chatgpt-3.5参数库内还未增加sora的语料信息

3月前

[db:摘要]

如何选择笔记本电脑?电脑参数怎么看?

3月前

[db:摘要]

Linux运维学习历程-第十四天-磁盘管理(一)磁盘分区表类型与文件系统

3月前

[db:摘要]

怎么确定windows启动类型是bios还是uefi?

3月前

怎么确定windows启动类型是bios还是uefi?随着装机越来越简单&#xff0c;大家对安装系统充满信心&#xff0c;但是了解到启动类型却一脸懵逼了&#xff0c;其实windows启动类型有bios和uef

计算机专业毕业设计题目大全——各种类型系统设计大全

2月前

计算机专业毕业设计题目大全 一、ASP类计算机专业毕业设计题目 1.网络留言薄2.客户管理系统3.多媒体积件管理库的开发与应用4.基于WEB的多媒体素材管理库的开发与应用5.网络教学软件中的教学设计与应用6.小型教育网站的开发与建设7.

windows操作系统的日志类型

2月前

Windows日志特指Windows操作系统中各种各样的日志文件&#xff0c;如应用程序日志&#xff0c;安全日志、系统日志、Scheduler服务日志、FTP日志、WWW日志、DNS服务器日志等&#xff0c;

R7 8745H参数 锐龙78745H怎么样 相当于什么水平

2月前

R7 8745H采用 Zen 4架构&#xff0c;拥有8核16线程&#xff0c;台积电4nm工艺制造&#xff0c;且内置基于RDNA 3架构、最高加速频率4.9GHz 拥有12个CU的Radeon 780M核显

东芝计算机配置,东芝Toshiba笔记本电脑开机进入BIOS的方法与bios设置参数介绍(ESC+F1)...

2月前

东芝Toshiba笔记本电脑开机进入BIOS的方法 东芝笔记本电脑的BIOS一般可分为3种&#xff0c;在Tecra&#xff0f;Portege&#xff0f;Satellite Pro 3个系列的笔记本电脑上使

2024年前端最全vue 获取当前登录的浏览器类型_vue获取浏览器类型,GitHub标星3.2K

2月前

最后 整理面试题&#xff0c;不是让大家去只刷面试题&#xff0c;而是熟悉目前实际面试中常见的考察方式和知识点&#xff0c;做到心中有数&#xff0c;也可以用来自查及完善知识体系。 开源分享&am

根据userAgent值的特性判断客户端设备及浏览器类型

2月前

https:blog.csdnI_need_a_name_articledetails88432278 前文《Web开发兼容性系列文章&#xff08;一&#xff09;&#xff1a;不一样设备浏览器的

通过userAgent判断手机浏览器类型

2月前

我们可以通过userAgent来判断&#xff0c;比如检测某些关键字&#xff0c;例如&#xff1a;AppleWebKit*****Mobile或AppleWebKit&#xff0c;需要注意的是有些浏

Js判断linux还是windows,js获取客户端操作系统类型的方法【测试可用】

17天前

本文实例讲述了js获取客户端操作系统类型的方法。分享给大家供大家参考&#xff0c;具体如下&#xff1a; p> "http:www.w3TRxhtml1DTDxhtml1-transitio

Windows命令创建用户,支持Windows Server或者Win10所有版本,可以在命令中实现自定义用户最全参数,自己运维经验,全部可用有效

14天前

命令创建用户 一、下面命令请在PowerShell上执行 使用PowerShell创建用户可以带上更多的参数&#xff0c;缺点就是用户密码只能设置一样的。 1、设置用户统一的密码 $PasswordRead-Host -

发表评论

全部评论 0
暂无评论