【问题标题】:Is it possible to write a node.js extension in C (not C++)?是否可以用 C(不是 C++)编写 node.js 扩展?
【发布时间】:2012-05-15 08:00:49
【问题描述】:

一个快速的谷歌搜索产生至少一个为 node.js 编写 C++ "Hello World" 的教程,但目前尚不清楚是否可以仅使用 C 编写这样的扩展。假设有可能,我将面临哪些挑战/限制?

【问题讨论】:

标签: node.js c node.js-addon


【解决方案1】:

如果需要,您可以用 C 编写部分扩展,但至少需要一小部分 C++ 代码才能将 C 代码与 Node 粘合在一起。

正如您在 HelloWorld 中所见,扩展依赖于 v8.h 和 node.h 标头,它们具有 Node 期望的所有类。没有这些,您将无法正确创建 JS 对象以导出回 Node。

也就是说,您可以很容易地编写一小部分 C++ 函数,这些函数只调用 C 函数,并包装某种 C 结构。

【讨论】:

    【解决方案2】:

    在 Hacker News 上找到这个:

    https://github.com/wesolows/v8plus

    v8+:C++ 到 C 边界的节点插件

    这一层提供了一种用 C 语言编写至少简单的 Node 插件的方法,而无需您原本应该使用的所有可怕的 C++ goop。那个 goop 仍然存在,但你不必写它。更重要的是,您可以在一个健全的编程环境中编写您的模块,避免令人困惑和容易出错的 C++ 语义。

    【讨论】:

    • 看起来它不适用于版本 > Node.js 0.10,刚刚提交了一个问题来确定
    【解决方案3】:

    现在我们至少有 3 个不错的选择:

    node-ffi: Node.js 外部函数接口
    使用纯 JavaScript 加载和调用动态库的插件。它可用于创建与本机库的绑定,而无需编写任何 C 代码
    https://github.com/node-ffi/node-ffi

    SWIG: 简化的包装器和接口生成器
    (它为多种语言生成包装器,一次解决了很多问题)
    http://www.swig.org/

    emscripten
    将 C 和 C++ 编译成高度可优化的 JavaScript,即使在 Web 上也能以接近本机的速度运行,无需插件。
    http://kripken.github.io/emscripten-site/

    【讨论】:

      【解决方案4】:

      需要使用 extern "C" 语法在 C++ 代码中声明单个 C 函数

      例子:

      #define BUILDING_NODE_EXTENSION
      #include <node.h>
      
      extern "C" void f(int i, char c, float x);
      
      using namespace v8;
      

      如果你有多个 C 函数,那么可以通过大括号进行分组:

      extern "C" {
        void   f(int i, char c, float x);
        int    g(char* s, char const* s2);
        double sqrtOfSumOfSquares(double a, double b);
      }
      

      然后从 C++ 函数中调用函数:

      Handle<Value> MyFunction(const Arguments& args) {
        HandleScope scope;
         f(7, 'x', 3.14);    // <--- 
        return scope.Close(String::New("Hello"));
      }
      
      Handle<Value> CreateFunction(const Arguments& args) {
        HandleScope scope;
      
        Local<FunctionTemplate> tpl = FunctionTemplate::New(MyFunction);
        Local<Function> fn = tpl->GetFunction();
        fn->SetName(String::NewSymbol("theFunction")); // omit this to make it anonymous
      
        return scope.Close(fn);
      }
      
      void Init(Handle<Object> target) {
        target->Set(String::NewSymbol("createFunction"),
            FunctionTemplate::New(CreateFunction)->GetFunction());
      }
      
      
      NODE_MODULE(addon, Init)
      

      注意:使用来自Nodejs Addons的示例代码

      【讨论】:

        【解决方案5】:

        如果您的模块使用 libuv,您可以将其链接到节点可执行文件。它将 libuv 函数导出为共享库。

        然后你可以使用 node-ffi 与之交互(这里不需要 C++ 知识)。

        这是我在 Windows 上使用 MSVS 的方法:

        • 在 MSVS 中创建新的 DLL 解决方案
        • 下载libuv并将include和lib文件复制到MSVS
        • 下载node.lib文件,放到MSVS的lib文件夹中
        • 编译下面的示例源代码,将计时器添加到主事件循环中

        testlib.c:

        #include <stdio.h>
        #include <stdlib.h>
        #include "uv.h"
        
        void (*p_callback)(int number, char *text);
        
        void timer_cb1 (uv_timer_t* timer, int status) {
          printf("libuv timer here\n", status);
          p_callback(123, "it worked!");
        }
        
        void set_timer (int interval, void *pfunction) {
          uv_loop_t *loop;
          uv_timer_t *timer1;
        
          printf("set_timer called. interval=%d callback=%p\n", interval, pfunction);
        
          p_callback = pfunction;
        
          printf("uv_version_string = %s\n", uv_version_string());
        
          loop = uv_default_loop();
          if (loop == 0) {
            puts("could not get the reference to the default loop");
            return;
          }
        
          puts("got the default loop. now allocating the timer struct");
        
          timer1 = (uv_timer_t *) malloc(sizeof(uv_timer_t));
          if (timer1 == 0) {
            puts("malloc failed");
            return;
          }
        
          puts("initializing timer");
          uv_timer_init(loop, timer1);
        
          puts("starting timer");
          uv_timer_start(timer1, (uv_timer_cb) &timer_cb1, interval, interval);
        
          puts("timer created. returning");
        
        }
        

        使用 testlib.def:

        EXPORTS set_timer
        

        记得链接到node.lib

        • 将创建的 dll 移动到测试文件夹并在那里运行这些命令:

        npm install ffi(目前需要构建工具,查看说明)

        node test-lib.js

        test-lib.js 在这里:

        var ffi = require('ffi');
        
        var testlib = ffi.Library('testlib', {
          'set_timer': [ 'void', [ 'int', 'pointer' ] ]
        });
        
        var callback = ffi.Callback('void', ['int', 'string'],
          function(number, text) {
            console.log("javascript callback here!!!  number=" + number + "  text=" + text);
          }
        );
        
        console.log('registering the callback...');
        testlib.set_timer(500, callback);
        console.log('done')
        

        发挥你的想象力。我们在 libuv 中有网络、工作线程和其他选项...

        【讨论】:

          【解决方案6】:

          与node.js直接交互的代码需要用C++编写。

          您可以使用不透明类型编写 extern "C" 包装器来满足您从 node.h 和 v8.h 中需要的所有内容,但改用 C++ 可能更容易(当然,可以调用输出到 C 代码)。

          【讨论】:

            猜你喜欢
            • 2014-01-10
            • 2016-05-18
            • 2016-11-10
            • 1970-01-01
            • 2017-07-19
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            • 1970-01-01
            相关资源
            最近更新 更多