在C++程序中嵌入quickjs实现C++和javascript互相调用

quickjs是一个C++实现的轻量级javascript解析引擎,可以嵌入到C++程序中,实现C++和js代码的交互。

以下基于quickjs-ng这一社区分支实现样例代码演示利用quickjs编写程序进行C++和js互相调用,支持linux和windows。

代码结构

quickjs_demo
	- quickjs-0.5.0 
	- main.cpp        # C++主执行程序
	- main.js         # js执行程序
	- sample.hpp      # C++模块代码,供js调用
	- sample.js       # js模块代码,供C++调用
	- CMakeLists.txt

CMakeLists.txt

cmake_minimum_required(VERSION 3.15)

project(quickjs_demo)

set(CMAKE_CXX_STANDARD 20)
set(CMAKE_CXX_STANDARD_REQUIRED ON)

if (WIN32)
    add_definitions(
        -D_CRT_SECURE_NO_WARNINGS
        -D_WINSOCK_DEPRECATED_NO_WARNINGS
    )
elseif (UNIX)
    add_compile_options(
        -fPIC
        -O3
    )
endif()

add_subdirectory(./quickjs-0.5.0)

include_directories(./quickjs-0.5.0)

# build host executable
file(GLOB SRC
    main.cpp
)

add_executable(${PROJECT_NAME} ${SRC})

target_link_libraries(${PROJECT_NAME} 
    qjs
    pthread
)

基本原理为

  • C++调用js:在C++中启动js运行时,加载js代码执行,可以返回js执行结果在C++中继续处理
  • js调用C++:仍然在C++中启动js运行时,将C++定义的代码模块注册,加载js代码执行,调用注册好的C++模块,返回的结果可以在js中继续处理

基于这样的机制,就可以做到在C++的程序框架中C++与js双向交互,实现很多纯C++或者纯js达不到的效果,例如代码热更新以及安全隔离,这种机制目前其实在金融数据分析系统和游戏引擎中广泛使用。

C++调用js

sample.js

const a = 3;
const b = 5;

function my_func(x, y, text)
{
	// the input params type, x is int, y is double, text is string, return z is double
	// console.log("my_func with params:", x, y, text);

	let z = x * y + (b - a);
	return z;
}

C++代码

void cpp_call_js_test()
{
	std::cout << "--- cpp call js test ---" << std::endl;

	// init js runtime and context
    JSRuntime* rt = JS_NewRuntime();
    JSContext* ctx = JS_NewContext(rt);

    // define global js object
    JSValue global_obj = JS_GetGlobalObject(ctx);

    // load js script
    std::string js_file = "./sample.js";
	std::ifstream in(js_file.c_str()); 
   	std::ostringstream sin; 
   	sin << in.rdbuf(); 
   	std::string script_text = sin.str();

   	std::cout << "script text: " << std::endl;
   	std::cout << script_text << std::endl;

   	// run script
   	std::cout << "script run: " << std::endl;
   	JSValue script = JS_Eval(ctx, script_text.c_str(), script_text.length(), "sample", JS_EVAL_TYPE_GLOBAL);
   	if (!JS_IsException(script))
   	{
   		int x = 7;
   		double y = 8.9;
   		std::string text = "called from cpp";

   		JSValue js_x = JS_NewInt32(ctx, x);
   		JSValue js_y = JS_NewFloat64(ctx, y);
   		JSValue js_text = JS_NewString(ctx, text.c_str());
   		JSValue js_result;

   		JSValue my_func = JS_GetPropertyStr(ctx, global_obj, "my_func");

   		if (JS_IsFunction(ctx, my_func))
   		{
   			JSValue params[] = {js_x, js_y, js_text};

   			// call js function
   			js_result = JS_Call(ctx, my_func, JS_UNDEFINED, 3, params);

   			if (!JS_IsException(js_result))
   			{
				double result = 0.0;
				JS_ToFloat64(ctx, &result, js_result);
				std::cout << "my_func result: " << result << std::endl;
   			}
   			else
   				std::cerr << "JS_Call failed" << std::endl;
   			
   		}

   		JS_FreeValue(ctx, my_func);
		JS_FreeValue(ctx, js_result);
		JS_FreeValue(ctx, js_text);
		JS_FreeValue(ctx, js_y);
		JS_FreeValue(ctx, js_x);
   	}
   	else
   		std::cerr << "JS_Eval failed" << std::endl;

   	// close js runtime and context
   	JS_FreeValue(ctx, global_obj);
    JS_FreeContext(ctx);
    JS_FreeRuntime(rt);
}

js调用C++

sample.hpp

#include <iostream>
#include "quickjs.h"


#define JS_INIT_MODULE js_init_module
#define countof(x) (sizeof(x) / sizeof((x)[0]))

// define native variable and function
const int a = 3;
const int b = 5;

static double my_func(int x, double y, const char* text)
{
    std::cout << "my_func with params: " << x << ", " << y << ", " << text << std::endl;

    double z = x * y + (b - a);
    return z;
}

// define quickjs C function
static JSValue js_my_func(JSContext *ctx, JSValueConst this_val, int argc, JSValueConst *argv)
{
    std::cout << "js_my_func, argc: " << argc << std::endl;

    if (argc != 3)
        return JS_EXCEPTION;

    int a = 0;
    double b = 0.0;

    if (JS_ToInt32(ctx, &a, argv[0]))
        return JS_EXCEPTION;
    
    if (JS_ToFloat64(ctx, &b, argv[1]))
        return JS_EXCEPTION;
    
    if (!JS_IsString(argv[2]))
        return JS_EXCEPTION;

    const char* text = JS_ToCString(ctx, argv[2]);
    double z = my_func(a, b, text);

    std::cout << "a: " << a << ", b: " << b << ", text: " << text << ", z: " << z << std::endl;

    return JS_NewFloat64(ctx, z);
}


// define function entry list
static const JSCFunctionListEntry js_my_funcs[] = 
{
    JS_CFUNC_DEF("my_func", 3, js_my_func),
};

static int js_my_init(JSContext *ctx, JSModuleDef *m)
{
    return JS_SetModuleExportList(ctx, m, js_my_funcs, countof(js_my_funcs));
}

JSModuleDef *JS_INIT_MODULE(JSContext *ctx, const char *module_name)
{
    JSModuleDef *m;
    m = JS_NewCModule(ctx, module_name, js_my_init);
    if (!m)
        return NULL;
    JS_AddModuleExportList(ctx, m, js_my_funcs, countof(js_my_funcs));
    return m;
}

main.js

let a = 7;
let b = 8.9;
let text = "called from js";

// call cpp function
let result = my_func(a, b, text);
// console.log("my_func result: ", result);

C++代码

void js_call_cpp_test()
{
	std::cout << "--- js call cpp test ---" << std::endl;
    
    // init js runtime and context
    JSRuntime* rt = JS_NewRuntime();
    JSContext* ctx = JS_NewContext(rt);

    // define global js object
    JSValue global_obj = JS_GetGlobalObject(ctx); 

    // register C++ function to current context
    JSValue func_val = JS_NewCFunction(ctx, js_my_func, "my_func", 1); 
    if (JS_IsException(func_val))
        std::cerr << "JS_NewCFunction failed" << std::endl;

    if (JS_DefinePropertyValueStr(ctx, global_obj, "my_func", func_val, JS_PROP_WRITABLE | JS_PROP_CONFIGURABLE) < 0)
		std::cerr << "JS_DefinePropertyValue failed" << std::endl;

    std::string js_file = "./main.js";
	std::ifstream in(js_file.c_str()); 
   	std::ostringstream sin; 
   	sin << in.rdbuf(); 
   	std::string script_text = sin.str();

   	std::cout << "script text: " << std::endl;
   	std::cout << script_text << std::endl;

   	std::cout << "script run: " << std::endl;
   	JSValue script = JS_Eval(ctx, script_text.c_str(), script_text.length(), "main", JS_EVAL_TYPE_GLOBAL);

   	if (JS_IsException(script))
   		std::cerr << "JS_Eval failed" << std::endl;

   	// close js runtime and context
    JS_FreeContext(ctx);
    JS_FreeRuntime(rt);
}

主程序

#include <iostream>
#include <fstream>
#include <sstream>
#include <string>
#include "quickjs.h"
#include "quickjs-libc.h"
#include "sample.hpp"

// void cpp_call_js_test();
// ...

// void js_call_cpp_test();
//  ...

int main()
{
	// test cpp call js script
	cpp_call_js_test();

	// test js call cpp module
	js_call_cpp_test();

	return 0;
}

执行结果

--- cpp call js test ---
script text: 
const a = 3;
const b = 5;

function my_func(x, y, text)
{
	// the input params type, x is int, y is double, text is string, return z is double
	// console.log("my_func with params:", x, y, text);

	let z = x * y + (b - a);
	return z;
}
script run: 
my_func result: 64.3
--- js call cpp test ---
script text: 
let a = 7;
let b = 8.9;
let text = "called from js";

// call cpp function
let result = my_func(a, b, text);
// console.log("my_func result: ", result);
script run: 
js_my_func, argc: 3
my_func with params: 7, 8.9, called from js
a: 7, b: 8.9, text: called from js, z: 64.3

记得要将sample.js和main.js拷贝到执行目录

备注:

  • 由于quickjs的执行环境比较轻量级,在js代码里不能使用console.log等浏览器支持的内置函数,如果要打印日志,可以在C++中封装函数模块给js调用
  • 需要在支持C++20的编译器下使用,如果编译不过,建议升级gcc或msvc

源码

quickjs_demo

本文来自互联网用户投稿,该文观点仅代表作者本人,不代表本站立场。本站仅提供信息存储空间服务,不拥有所有权,不承担相关法律责任。如若转载,请注明出处:http://www.mfbz.cn/a/775772.html

如若内容造成侵权/违法违规/事实不符,请联系我们进行投诉反馈qq邮箱809451989@qq.com,一经查实,立即删除!

相关文章

如何有效管理你的Facebook时间线?

Facebook作为全球最大的社交平台之一&#xff0c;每天都有大量的信息和内容在用户的时间线上展示。有效管理你的Facebook时间线&#xff0c;不仅可以提升用户体验&#xff0c;还能够帮助你更好地控制信息流和社交互动。本文将探讨多种方法和技巧&#xff0c;帮助你有效管理个人…

开发者评测|操作系统智能助手OS Copilot

操作系统智能助手OS Copilot 文章目录 操作系统智能助手OS CopilotOS Copilot 是什么优势功能 操作步骤创建实验重置密码创建Access Key配置安全组安装 os-copilot环境变量配置功能评测命令行模式多轮交互模式 OS Copilot 产品体验评测反馈OS Copilot 产品功能评测反馈 参考文档…

C++基础(七):类和对象(中-2)

上一篇博客学的默认成员函数是类和对象的最重要的内容&#xff0c;相信大家已经掌握了吧&#xff0c;这一篇博客接着继续剩下的内容&#xff0c;加油&#xff01; 目录 一、const成员&#xff08;理解&#xff09; 1.0 引入 1.1 概念 1.2 总结 1.2.1 对象调用成员函数 …

使用 mongo2neo4j 和 SemSpect 通过各种方式进行图探索

用于可视化和探索每个 MEAN 堆栈背后的数据图的 ETL 您是否正在努力回答有关 MEANS Web 服务数据的紧急问题&#xff1f;哪里有 BI 可以快速回答“上个季度哪些亚洲的artisan.plus 用户触发了订单&#xff1f;”这个问题&#xff0c;而无需编写查询&#xff1f;使用 mongo2neo4…

Linux:进程间通信(一.初识进程间通信、匿名管道与命名管道、共享内存)

上次结束了基础IO&#xff1a;Linux&#xff1a;基础IO&#xff08;三.软硬链接、动态库和静态库、动精态库的制作和加载&#xff09; 文章目录 1.认识进程间通信2.管道2.1匿名管道2.2pipe()函数 —创建匿名管道2.3匿名管道的四种情况2.4管道的特征 3.基于管道的进程池设计4.命…

FineBI在线学习资源-数据处理

FineBI在线学习资源汇总&#xff1a; 学习资源 视频课程 帮助文档 问答 数据处理学习文档&#xff1a; 相关资料&#xff1a; 故事背景概述-https://help.fanruan.com/finebi6.0/doc-view-1789.html 基础表处理-https://help.fanruan.com/finebi6.0/doc-view-1791.html …

软件设计之Java入门视频(11)

软件设计之Java入门视频(11) 视频教程来自B站尚硅谷&#xff1a; 尚硅谷Java入门视频教程&#xff0c;宋红康java基础视频 相关文件资料&#xff08;百度网盘&#xff09; 提取密码&#xff1a;8op3 idea 下载可以关注 软件管家 公众号 学习内容&#xff1a; 该视频共分为1-7…

Ubuntu 24.04 上安装 Kubernetes,超级详细的教程!

Kubernetes 是一个免费的开源容器编排工具&#xff0c;它允许基于容器的应用程序的自动化部署、扩展和管理。 我们将介绍如何使用 Kubeadm 逐步在 Ubuntu 24.04 上安装 Kubernetes 此次演示中&#xff0c;我们将使用以下三个 Ubuntu 24.04 实例 Instance 1 : Master Node (k…

计算机视觉——opencv快速入门(二) 图像的基本操作

前言 上一篇文章中我们介绍了如何配置opencv&#xff0c;而在这篇文章我们主要介绍的是如何使用opencv来是实现一些常见的图像操作。 图像的读取&#xff0c;显示与存储 读取图像文件 在opencv中我们利用imread函数来读取图像文件,函数语法如下&#xff1a; imagecv2.imre…

植物大战僵尸融合版最新版1.0下载及安装教程

《植物大战僵尸融合版》最新版1.0已经发布&#xff0c;为粉丝们带来了全新的游戏体验。这个版本由B站UP主蓝飘飘fly精心打造&#xff0c;引入了创新的植物融合玩法&#xff0c;让玩家可以享受策略和创意的结合。以下是游戏的详细介绍和安装指南&#xff1a; 游戏特色介绍 全新…

TF-IDF计算过程一步步推导详解含代码演示

相关概念 TF-IDF TF-IDF&#xff08;Term Frequency–Inverse Document Frequency&#xff09;是一种用于资讯检索与文本挖掘的常用加权技术。TF-IDF是一种统计方法&#xff0c;用以评估一个字词对于一个文件集或一个语料库中的其中一份文件的重要程度。字词的重要性随着它在…

lua入门(2) - 数据类型

前言 本文参考自: Lua 数据类型 | 菜鸟教程 (runoob.com) 希望详细了解的小伙伴还请查看上方链接: 八个基本类型 type - 函数查看数据类型: 测试程序: print(type("Hello world")) --> string print(type(10.4*3)) --> number print(t…

pdf可以删除其中一页吗?6个软件教你快速进行pdf编辑

pdf可以删除其中一页吗&#xff1f;6个软件教你快速进行pdf编辑 编辑PDF文件并删除特定页面是处理文档时常见的需求&#xff0c;特别是在需要定制或精简文件内容时。以下是几款广受欢迎的PDF编辑软件&#xff0c;它们提供了强大的页面删除功能&#xff0c;帮助用户轻松管理和修…

Vue3学习笔记(n.0)

vue指令之v-for 首先创建自定义组件&#xff08;practice5.vue&#xff09;&#xff1a; <!--* Author: RealRoad1083425287qq.com* Date: 2024-07-05 21:28:45* LastEditors: Mei* LastEditTime: 2024-07-05 21:35:40* FilePath: \Fighting\new_project_0705\my-vue-app\…

安卓开发定时截屏

此处有两种方式&#xff1a;&#xff08;都是定时截屏&#xff0c;不需要定时功能可以剔除service&#xff09; 1.app内截屏 https://download.csdn.net/download/hdhhd/89517797 2.截取当前任意手机显示屏幕 https://download.csdn.net/download/hdhhd/89517800 第一种…

hitcontraining_uaf

BUUCTF[PWN][堆] 题目&#xff1a;BUUCTF在线评测 (buuoj.cn) 程序del是没有将申请的指针清零&#xff0c;导致可以再次调用输出print。 查看add_note函数&#xff1a;根据当前 notelist 是否为空&#xff0c;来申请了一个8字节的空间将地址(指针)放在notelist[i]中&#xff…

海尔智家:科技优秀是一种习惯

海尔智家&#xff1a;科技优秀是一种习惯 2024-06-28 15:19代锡海 6月24日&#xff0c;2023年度国家科学技术奖正式揭晓。海尔智家“温湿氧磁多维精准控制家用保鲜电器技术创新与产业化”项目荣获国家科学技术进步奖&#xff0c;成为家电行业唯一牵头获奖企业。 很多人说&…

RK3568平台(USB篇)TYPE-C接口与PD协议

一.TYPE-C接口简介 type-c 插座&#xff1a; type-c 插头&#xff1a; type-c 线缆&#xff1a; type-c 接口定义之插座&#xff1a; type-c 硬件原理图&#xff1a; VBUS&#xff1a;供电引脚&#xff0c;用于传输电源电压&#xff0c;一般为5V或12V。 GND&#xff1a;地引…

使用ChatGPT写论文,只需四步突破论文写作瓶颈!

欢迎关注&#xff0c;为大家带来最酷最有效的智能AI学术科研写作攻略。关于使用ChatGPT等AI学术科研的相关问题可以和作者七哥&#xff08;yida985&#xff09;交流 地表最强大的高级学术AI专业版已经开放&#xff0c;拥有全球领先的GPT学术科研应用&#xff0c;有兴趣的朋友可…

一键式创建GTest测试平台

适用于C GTest测试平台搭建。直接上python脚本。 #!/usr/bin/env python3 # -*- coding: utf-8 -*-import argparse import os import platform import subprocess from xml.etree import ElementTree as ETdefault_root_path "d:\\test\\UTtest"class DeveloperTe…