Node.js And C++__9.Addons替代解决方案

​ 注:本附录主要取自作者网站blog.scottfrees.com上的一系列博客文章,其中涵盖了将现有的C/C++程序*集成到一个Node.js 的各种方法。这个系列是独立于本书的,因此,您可能会发现其中的一些内容是多余的,但是它展示了一个真实的例子,它集成了legacy(老版本)的c++代码,而不是书中狭义的例子。该部分还有一个不同的github存储库,可以从本书的其他例子中下载代码。强烈建议您获取代码,因为其中有很多没有在文本中显示!

​ 这本书的重点是 Node.js C++ addons作为集成Node.js和c++的方法。在许多情况下,addons确实是进行这种集成的最佳选择,但是还有其他选择——如果不讨论这些选项,这本书就不完整了。在这样做之前,让我们后退一步,在第1章中重新询问这个问题:为什么要集成 Node.js 和 C++?为了加强讨论,让我们从现有的c++ /C程序的角度来看这个问题,您希望能够访问web

​ 我就不能写一个c++网站吗?

​ 嗯…是的,你可以!在很长一段时间内,人们一直在用c++使用CGI编写web应用程序的部分内容。CGI并不是当今网络上最受欢迎的东西,它缺乏大量的生产力增强,使得web开发今天变得如此的伟大。更重要的是,它引入了一些重要的性能和可伸缩性问题。另一方面,c++在过去的几年里在表达方面取得了长足的进步,c++ 14标准使一些非常酷的项目专注于在纯c++中编写现代的MVC-styled的web应用程序。如果这是你的事情,那就去看看Silicon

​ 大多数web开发人员不是c++程序员,坦率地说,除非您的web层的超高性能是至关重要的,否则您最好使用提供更高级别抽象的语言。网络上后台的通常运行的是Ruby, Go, Node.js, Python, PHP等等。

​ Node.js有很多优点。首先,它以几种不同的方式与c++很好地集成在一起——当然,我们在本书中看到了这一点!一般来说, Node.js也有很多好处,它与你最初使用c++的原因是一样的——它是高度可移植性的,它促进了规模的性能,并且有一个繁荣的ecosystem

​ 啊…每个开发人员的第一个本能——“让我们重写这个用语言X编写的旧代码,因为语言Y要比|好得多,|更快|更容易!”首先,如果您有一些简单、小且不需要高性能的 legacy(老版本)c++代码,这可能是最好的答案。然而,如果你属于那个类别,你可能没有读到这篇文章——你很可能已经重写了c++代码。

首先:不重写代码有一些实际的原因。首先,您可能没有代码!信不信由你,如果你为一家使用传统工具来支持业务的公司工作,那么这些工具的源代码常常会丢失。这是当您的遗留代码使用第三方依赖时,不能重写或修改的时候。

**第二:**C/C++可能是复杂的,如果它是旧的,可能很难破译。您是一个web开发人员,也是c++的专家吗?你能完全重现这个程序的精确输入/输出吗?如果这是一个关键的业务工具,你就会给你的盘子带来很多风险。

第三:不重写c++的原因是它可能真的想要使用c++ !当Node.js 的性能很好,根本不是 C/C++。如果您的应用程序有极端的性能标准,那么您将不会超过c++。

C++ 集成到 Node.js 的方案

有三种通用的方法将c++代码与Node.js应用程序集成在一起。——尽管每个类别中有很多不同的变体。

  1. Automation :在子进程中,将c++作为一个独立的应用程序。
  2. Shared library :在共享库(dll)中打包您的c++例程,并从Node.js 直接调用这些事例。
  3. Node.js Addon :编译你的c++代码作为一个本地Node.js 模块/addon(我们现在都知道了,对吧?)

    每个选项都有各自的优点和缺点,它们主要在您需要修改c++的程度上有所不同,在调用c++时,您愿意接受的性能打击,以及您在处理 Node.js和V8 API 时的熟练度/安全性。
    

选择依据

​ 最明显的问题是,您是否可以访问c++源代码,或者仅仅是二进制文件?如果没有源代码,您需要希望c++程序可以是命令行程序,也可以是dll/lib共享库。如果你看的是一个只有图形用户界面的程序,那么你就处在痛苦的世界里。您可能需要重写您的应用程序,以便使其在web上工作。

Automation

​ 如果您的c++运行作为一个独立的命令行,您不需要源代码来利用选项1 -automation 选项。您可以使用Node的子进程API 运行您的c++程序。这个选项适用于将任何东西带到web上——如果你只是运行它的话,你的命令行程序写在什么语言上并没有什么区别。如果您正在阅读这篇文章,希望获得C代码、Fortran代码或其他一些语言,那么这个选项值得一读。

​ 自动化选项不仅仅针对那些没有c++代码的人。如果您有c++代码,或者可以很容易地转换成命令行程序,那么这个选项是合理的,如果您可以使用性能,并且您并不想陷入语言集成的麻烦中。

Shared Library / DLL

​ 如果您处理的是c++ dll/lib,或者您有c++源代码,并且可以进行适当的修改,以创建动态库,那么 shared library 方法可能对您很有效。在本章中,我们将详细介绍如何使用外部函数接口模块进行此操作。这个选项可以让您更精确地控制如何将c++集成到节点中,因为对c++例程的调用通常可以直接写到Node.js代码中。虽然这种方法可以使您更接近完整的集成,但是您仍然需要处理类型转换和在调用c++时阻塞。如果您想要更好的集成,这是一个很好的选择,而无需花费大量时间来处理V8。

Node.js Addon

​ 如果您有c++源代码,那么第三个选项是创建一个本机 Node.js模块调用你的c++。虽然这是一个更具挑战性的方法,但是您获得了大量的灵活性和性能。您还可以选择异步调用您的c++,这样您就不会阻塞web应用程序的事件循环,而c++正在处理数字。当我们在本节中介绍这部分内容时,它将主要作为对书中主要章节中已经介绍的材料的回顾。

例子 - 质数分解

​ 在本节中,我将向您展示如何实现上述每个选项的示例。我想在每个例子中都使用相同的基本例子。素数对于很多东西(比如密码学)来说是极其重要的,而它们的生成往往是非常耗时的。在线快速搜索将会引导你转向C和c++的实现,而真正高效的实现是复杂的。看看他们的来源,你会马上意识到你可能不想重写他们——除非你只是在寻找一个挑战——这很好)。

​ 一种更有效的算法叫做 Sieve of Eratosthenes。有一个非常流行的c++套件,primseieve——但是构建起来相当复杂。相反,我发现一个更简单的实现更适合于我们的目的。您可以在http://wwwhomes.uni-bielefeld.de/achim/prime_sieve.html中找到它的源代码,但它也在git存储库中——https://github.com/freezer333/cppwebify-tutorial

Node.js Express Web app

​ 在这一节中,我将使用完全相同的 Node.js的web应用程序。它是非常简单的,有一个HTML页面,有一些JavaScript (AngularJS),它要求web服务器在用户指定的值下提供质数。web服务器使用一个JSON对象来响应,其中包含了primes,它使用了我将实现的几种技术之一。

​ 我假设读者对一个 Node.js web应用程序 有一些基本的理解,我在后台用Express和AngularJS创建了这个应用程序,但是我避开了任何复杂性和eye/candy,以免分散这些教程的目的。它也是一个很好的API到你的c++代码的设置-只是抛弃UI!

$ git clone https://github.com/freezer333/cppwebify-tutorial.git

$ git checkout start

​ 你可以自己浏览网页应用——但相关的部分是前端——在/web/views 和后端找到的,在/index.js和 /routes中找到。

​ 让我们快速浏览一下/index.js。前十行左右只是样板代码:

var express = require('express');
var app = express();
var bodyParser = require('body-parser');

app.use(express.static('public'));
app.set('view engine', 'jade');

app.use(bodyParser.json());
app.use(bodyParser.urlencoded({ extended: true }));

​ 下一行是构建一个 “types” 数组——它最终将为post系列中的每个示例保留条目。现在,我们只有一个Node.js primesieve实现。

var types = [
  {
    title: "pure_node",
    description: "Execute a really primitive " + 
        "implementation of prime sieve in Node.js"
  }];

​ 类型中的每个条目将对应于/route目录中找到的路由。这些是从 index.js动态加载,web服务器是由最后的一行代码开始的。

types.forEach(function (type) {
   
    app.use('/'+type.title, require('./routes/' + type.title));
});

app.get('/', function (req, res) {
   
  res.render('index', { routes: types});
});

var server = app.listen(3000, function () {
   
  console.log('Web server listing at http://localhost:%s', 
    server.address().port);
});

​ 要启动web服务器,请导航到终端的 /web 目录并键入以下内容:

$ npm install
... dependencies will be installed
$ node index

​ 现在将浏览器指向http://localhost:3000。您将获得索引页,其中列出了实现选项。现在,您只需要一个选项——“pure_node”。单击它,您将看到一个带有单个数字框的页面。输入100并提交-和节点。primesieve的js实现将运行并返回100以下的所有质数。

这里写图片描述
​ 在 Node.js上的primesieve实现是在 routes/pure_node.js中。与我们将在本系列的其余部分中使用的C实现相比,它非常简单——但是它完成了任务!处理实际响应的代码是路由器的post方法:

router.post('/', function(req, res) {
   
    var under = parseInt(req.body.under); // from the user

    var primes = find_primes(under);

    res.setHeader('Content-Type', 'application/json');
    res.end(JSON.stringify({
      results: primes
    }));
});

Automating + Node.js Web app

​ 如果您的c++独立于命令行运行——或者可以这样做——您可以使用Node的child process API运行它。这个选项适用于将任何东西带到web上——如果你只是运行它的话,你的命令行程序写在什么语言上并没有什么区别。

​ 自动化的两个特点使它具有吸引力。首先,由于在另一个进程中执行c++应用程序,所以实际上是异步地执行c++处理——这在web上是一个很大的胜利,因为您可以处理其他传入的HTTP流量,而c++应用程序正在工作。其次,你真的不需要做大量的语言集成或者使用复杂的V8 API——实际上这很简单!

​ 对于这个特定的部分,从git存储库签出 automation 标记。

$ git checkout automation

质数 C/C++分解器

​ 如上所述,我们正在构建 Sieve of Eratosthenes 素数计算策略筛选器的C实现的所有示例。这是一个很好的例子,因为速度对于质数来说很重要,而我所使用的C代码并不是你想要重写的类型。我所使用的示例——http://wwwhomes.uni-bielefeld.de/achim/prime_sieve.html——实际上是非常简单的,相比之下,更复杂的技术可以利用CPU缓存。到primesieve.org 去了解一下。对于Sieve的实现,程序的用户必须输入一个最大值,该算法将输出所有质数“在”这个值下。在本章的大部分内容中,我们将把这个输入值称为“under”。

​ 请先看一看最初的报价单。c代码现在在https://gist.github.com/freezer333/ee7c9880c26d3bf83b8e中找到,但是不要太纠结于细节,我们不需要过多地处理它(这就是重点!)。

primesieve.c

​ 当面对集成遗留(legacy )程序时,您可能没有访问代码的特权。为了本章的目的,我将模拟一些常见的集成场景——我将编辑一些原始的 primesieve.c。

  • 场景1:一个应用程序只从命令行参数中获取输入,并输出到标准输出。
  • 场景2:一个应用程序从用户(stdin)中获取输入,并输出到标准输出。
  • 场景3:从文件中获取输入并输出到另一个文件的应用程序。

    为了模拟每个场景,我们希望能够将FILE传递到主程序primesieve.c中,所以程序并不总是打印到控制台。让我们将 `main` 重命名为 `generate_args` ,并为其添加第三个参数FILE。我们将在场景3中具体使用它。
    
// in cppwebify-tutorial/cpp/prime4standalone/prime_sieve.c, 
// I've renamed int main(int argc, char *argv[])
// to:
int generate_args(int argc, char * argv[], FILE * out) {
    ... complicated prime number stuff ...

​ 我将在另一个文件(main.cpp)中写入入口点,因此我也将 generate_args的声明添加到一个名为 prime_sieve.h的头文件中。

​ 我正在创建第二个函数- generate ,它提供一个简化的接口——它只接受“under”参数,而不是命令行参数。这个定义在 prime_sieve.c的底部。将参数转换为字符参数并调用 generate_args.。这只是为了让我不太编辑原始代码,并使场景2更简洁。显然,富有想象力的读者可以想出更好的方法来完成这一切:)

// at the bottom of cppwebify-tutorial/cpp/prime4standalone/prime_sieve.c,
// an adapter function for use when we aren't using command-line arguments
int generate(int under, FILE *out) {
  char * name = "primes";
  char param [50];
  sprintf(param, "%d", under);
  char * values[] = { name, param};
  generate_args(2, values, out);
}

​ 所以,我们剩下的是 prime_sieve.h -使用 extern C 来确保我们的C函数可以正确地与c++主文件集成,我将在示例中使用。

extern "C" {
    // the old main, renamed - with a third parameter"
    // to direct output to a file as needed
    int generate_args(int argc, char * argv[], FILE * out);

    // an adapter function when the caller hasn't
    // received under through command line arguments
    int generate(int under, FILE * out);
}

The Node.js Child Process API

​ Node.js包含一个 child_process 模块,它公开了创建和控制进程的健壮的API。有三个基本的调用来创建新的子进程——每个进程都有自己的用例。

​ 第一个是 execFile,它接受(至少是)一个可执行程序的文件路径。您可以传递一个由程序调用的参数数组。函数的最后一个参数是当程序终止时要执行的回调。这个回调会有一个错误,一个stdout缓冲区,以及一个给定的 stderr缓冲区,它可以用来查询程序的输出。需要注意的是,这个回调只是在程序执行之后才调用。 execFile还返回一个表示子进程的对象,您可以将其写入stdin流。

// standard node module
var execFile = require('child_process').execFile

// this launches the executable and returns immediately
var child = execFile("path to executable
  • 1
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

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

抵扣说明:

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

余额充值