可以使用Go编写原生的Node.js扩展,而不是使用C++吗?

huangapple go评论103阅读模式
英文:

Could one write a native Node.js extension in Go, as opposed to C++?

问题

这就是我问题的全部内容,但我认为这是一个有趣的问题需要回答。

英文:

That's all there is to my question, really, but I think it's an interesting thing to have answered.

答案1

得分: 19

通过在Go语言中添加对共享库的支持,现在可以实现这一点。

calculator.go:

// 包名: calculator
package main

import "C"

//export Sum
func Sum(x, y float64) float64 {
    return x + y
}

func main() {
}

node-calculator.cc:

#include "calculator.h"
#include <node.h>

namespace calc {

  using v8::FunctionCallbackInfo;
  using v8::Isolate;
  using v8::Local;
  using v8::Object;
  using v8::String;
  using v8::Value;
  using v8::Number;
  using v8::Exception;

  void add(const FunctionCallbackInfo<Value>& args) {
    Isolate* isolate = args.GetIsolate();

    // 检查传入的参数个数
    if (args.Length() < 2) {
      // 抛出一个错误,将其传回JavaScript
      isolate->ThrowException(Exception::TypeError(
          String::NewFromUtf8(isolate, "参数个数错误")));
      return;
    }

    // 检查参数类型
    if (!args[0]->IsNumber() || !args[1]->IsNumber()) {
      isolate->ThrowException(Exception::TypeError(
          String::NewFromUtf8(isolate, "参数错误")));
      return;
    }

    // 执行操作
    Local<Number> num = Number::New(isolate, Sum(args[0]->NumberValue(), args[1]->NumberValue()));

    // 设置返回值(使用传入的FunctionCallbackInfo<Value>)
    args.GetReturnValue().Set(num);
  }

  void init(Local<Object> exports) {
    NODE_SET_METHOD(exports, "add", add);
  }

  NODE_MODULE(calculator, init)
}

binding.gyp:

{
  "targets": [
    {
      "target_name": "node-calculator",
      "sources": [
        "node-calculator.cc"
      ],
      "libraries": [
        "../calculator.a"
      ],
    },
  ],
}

test.js:

const calculator = require('./build/Release/node-calculator');
console.log('4+5=', calculator.add(4, 5));

构建:

go build -buildmode c-archive -o calculator.a calculator.go
node-gyp configure
node-gyp build

输出:

#> node test.js 
4+5= 9
英文:

With the addition of support for shared libraries in go, this is possible now.

calculator.go:

// package name: calculator
package main

import &quot;C&quot;

//export Sum
func Sum(x, y float64) float64 {
    return x + y
}

func main() {
}

node-calculator.cc:

#include &quot;calculator.h&quot;
#include &lt;node.h&gt;

namespace calc {

  using v8::FunctionCallbackInfo;
  using v8::Isolate;
  using v8::Local;
  using v8::Object;
  using v8::String;
  using v8::Value;
  using v8::Number;
  using v8::Exception;

  void add(const FunctionCallbackInfo&lt;Value&gt;&amp; args) {
    Isolate* isolate = args.GetIsolate();

    // Check the number of arguments passed.
    if (args.Length() &lt; 2) {
      // Throw an Error that is passed back to JavaScript
      isolate-&gt;ThrowException(Exception::TypeError(
          String::NewFromUtf8(isolate, &quot;Wrong number of arguments&quot;)));
      return;
    }

    // Check the argument types
    if (!args[0]-&gt;IsNumber() || !args[1]-&gt;IsNumber()) {
      isolate-&gt;ThrowException(Exception::TypeError(
          String::NewFromUtf8(isolate, &quot;Wrong arguments&quot;)));
      return;
    }

    // Perform the operation
    Local&lt;Number&gt; num = Number::New(isolate, Sum(args[0]-&gt;NumberValue(), args[1]-&gt;NumberValue()));

    // Set the return value (using the passed in
    // FunctionCallbackInfo&lt;Value&gt;&amp;)
    args.GetReturnValue().Set(num);
  }

  void init(Local&lt;Object&gt; exports) {
    NODE_SET_METHOD(exports, &quot;add&quot;, add);
  }

  NODE_MODULE(calculator, init)
}

binding.gyp:

{
  &quot;targets&quot;: [
    {
      &quot;target_name&quot;: &quot;node-calculator&quot;,
      &quot;sources&quot;: [
        &quot;node-calculator.cc&quot;
      ],
      &quot;libraries&quot;: [
        &quot;../calculator.a&quot;
      ],
    },
  ],
}

test.js:

const calculator = require(&#39;./build/Release/node-calculator&#39;);
console.log(&#39;4+5=&#39;, calculator.add(4, 5));

Build:

go build -buildmode c-archive -o calculator.a calculator.go
node-gyp configure
node-gyp build

Output:

#&gt; node test.js 
4+5= 9

答案2

得分: 12

Node.js的本地模块必须与包含许多V8概念(如垃圾回收、JavaScript上下文等)的V8进程进行深度交互。

我认为V8没有为其他语言提供兼容且稳定的API来与其交互。这就是为什么Node.js本地插件应该使用C++构建并始终导入V8的C++头文件的原因。

但是你可以使用GO通过将GO代码与C++包装来编写Node.js本地插件:

文件:module.go

package main

func Add(a, b int) int {
    return a + b
}

文件:module.c

#include <node.h>
#include <v8.h>

using namespace v8;

extern int go_add(int, int) __asm__ ("example.main.Add");

void init(Handle<Object> exports) {
    // 调用go_add
}

NODE_MODULE(module, init)

关于“如何从C/C++调用GO函数”的更多信息,请参考以下链接:

https://stackoverflow.com/questions/6125683/call-go-functions-from-c

编辑:

请参阅@jdi的评论和链接:https://groups.google.com/forum/#!msg/golang-nuts/FzPbOwbTlPs/dAJVWQHx6m4J

引用:“对于像加法这样的简单操作(不生成垃圾或不需要运行时),可能是可行的,但据我所知,目前(尚)不受任何编译器支持。在Linux上已经完成了部分工作(参见golang.org/issue/256),但还有一些未解决的问题(加载两个共享对象时会发生什么等等)。”

英文:

Native module for node.js must deeply interact with V8 process which contains a lot of v8 concepts such as gc, javascript context, ...

And I don't think V8 has exposed compatible and stable APIs for other language to interact with it. That is why node.js native addon should be built with C++ and always imports V8 C++ headers.


But you can use GO to write node.js native addons by wrapping GO code with C++:

file: module.go

package main

func Add(a, b int) int {
    return a + b
}

file: module.c

#include &lt;node.h&gt;
#include &lt;v8.h&gt;

using namespace v8;

extern int go_add(int, int) __asm__ (&quot;example.main.Add&quot;);

void init(Handle&lt;Object&gt; exports) {
    // call go_add
}

NODE_MODULE(module, init)

More about "how to call GO functionn from C/C++":

https://stackoverflow.com/questions/6125683/call-go-functions-from-c


Edit:

Please see @jdi comments and the link: https://groups.google.com/forum/#!msg/golang-nuts/FzPbOwbTlPs/dAJVWQHx6m4J

Quote: It might be doable for simple things like add (that don't generate garbage or require the runtime), but it's not supported (yet) by either compiler as far as I know. Part of the work is done for linux (see golang.org/issue/256), but there are a number of open questions (what happens when you load two shared objects? etc)

答案3

得分: 3

只是将这个作为答案而不是评论重新发布...

我向golang-nuts邮件列表追问了关于使用Go编写其他语言的扩展的支持情况。回复的来源可以在这里找到

> 对于像add(不生成垃圾或不需要运行时的简单事物)这样的简单事物,可能是可行的,但据我所知,目前没有(尚未)由任何编译器支持。在Linux上的一部分工作已经完成(参见golang.org/issue/256),但还有一些未解决的问题(当加载两个共享对象时会发生什么等等)。

所以实际上,在Go中编写扩展似乎没有太大意义,因为大部分语言特性都不可用,而且你已经处于C/C++领域,为入口点添加包装器。

英文:

Just to repost this as an answer instead of a comment...

I followed up with golang-nuts mailing list regarding the support for writing extensions in Go for other languages. The source of the response can be found here.

> It might be doable for simple things like add (that don't generate
> garbage or require the runtime), but it's not supported (yet) by
> either compiler as far as I know. Part of the work is done for linux
> (see golang.org/issue/256), but there are a number of open questions
> (what happens when you load two shared objects? etc)

So really, there doesn't seem to be much point in writing an extension in Go, yet, as most of the language features would not be available, and you are already in C/C++ land anyways to add the wrapper for the entry point.

huangapple
  • 本文由 发表于 2013年12月22日 18:07:53
  • 转载请务必保留本文链接:https://go.coder-hub.com/20728255.html
匿名

发表评论

匿名网友

:?: :razz: :sad: :evil: :!: :smile: :oops: :grin: :eek: :shock: :???: :cool: :lol: :mad: :twisted: :roll: :wink: :idea: :arrow: :neutral: :cry: :mrgreen:

确定