简体   繁体   English

用输出参数包装一个 C++ 函数,以便在 javascript/node 中使用

[英]Wrap a c++ function with output parameter for use in javascript/node

Given a function with the definition int test(char* input, char** output);给定一个定义为int test(char* input, char** output);的函数. . How do I configure SWIG interface so that I can get the output value?如何配置 SWIG 接口以便获得输出值?

This is how I've called it from js:这是我从 js 调用它的方式:

var input = 'input1';
var output = '';
var result = mylib.test(input, output);
console.log(output);

I understand this does not work because string is immutable in javascript, plus the interface created in SWIG does not write back to the output parameter.我知道这不起作用,因为字符串在 javascript 中是不可变的,而且在 SWIG 中创建的接口不会写回输出参数。

Here is the C++ code:这是C++代码:

int test(char* input, char** output) {
  *output = input;
  return 0;
}

The easiest way to map the C++ semantics you've shown onto JavaScript is to make SWIG make the function behave as you would if you'd written it in real JavaScript instead of C++.将您展示的 C++ 语义映射到 JavaScript 的最简单方法是使 SWIG 使函数的行为与您使用真正的 JavaScript 而不是 C++ 编写的一样。 That is to say we're going to make your function behave like the following JavaScript pseudo code:也就是说,我们将使您的函数的行为类似于以下 JavaScript 伪代码:

function test(input) {
    if (error) {
        throw ....
    }
    return input;
}

To do that with SWIG we need to write a few typemaps.要使用 SWIG 做到这一点,我们需要编写一些类型映射。 (In some cases those typemaps already exist for us as part of the standard SWIG library, but for char ** there's no such typemap because the semantics are a little less obvious). (在某些情况下,这些类型映射已经作为标准 SWIG 库的一部分存在,但对于char **没有这样的类型映射,因为语义不太明显)。

I made your code work with the following SWIG interface, annotated below:我使您的代码与以下 SWIG 接口一起工作,注释如下:

%module test

// #1
%typemap(in,numinputs=0) char **output (char *tmp) {
    $1 = &tmp; // #2
}

// #3
%typemap(argout,fragment="SWIG_FromCharPtr") char **output {
    $result = SWIG_FromCharPtr(tmp$argnum);
    // Without more effort the following would be an illegal cast I think:
    //SWIG_AppendOutput($result, tmp$argnum);
}

// #4
%typemap(out) int test %{
    if ($1) {
        SWIG_exception_fail(SWIG_ERROR, "Well, that was unexpected");
    }
%}

%inline %{
int test(char *input, char **output) {
    *output = input;
    return 0;
}
%}

Essentially we've done 4 things here:基本上我们在这里做了 4 件事:

  1. A typemap that sets up the **output argument, without taking an input from JavaScript at all.设置**output参数的类型映射,根本不从 JavaScript 获取输入。numinputs=0 is what suppresses the need to receive an argument from JavaScript callees.numinputs=0是抑制从 JavaScript 被调用者接收参数的需要。
  2. Instead we're using a local variable to be the output purely inside the wrapper相反,我们使用局部变量作为纯粹在包装器内部的输出
  3. After the function has been called use the value in our local variable as the function return to JavaScript.在函数被调用后,使用我们本地变量中的值作为函数返回给 JavaScript。 We need to refer to it as tmp$argnum because SWIG has internally numbered the local variable to avoid clashes if a typemap gets matched multiple times on the in typemap, but it doesn't do that automatically on argout.我们需要将它称为tmp$argnum因为 SWIG 在内部对局部变量进行了编号,以避免在类型映射在 in 类型映射上多次匹配时发生冲突,但它不会在 argout 上自动执行此操作。 There are some standard macros that SWIG supplies for appending things to return multiple items, but they don't work out the box here because it ends up doing an illegal cast and I don't much like using those semantics anyway. SWIG 提供了一些标准宏,用于附加内容以返回多个项目,但它们在这里不起作用,因为它最终会进行非法转换,而且我不太喜欢使用这些语义。 The fragment here makes sure we've got some pre-written support for string outputs available in our generated code.这里的片段确保我们在生成的代码中获得了对可用字符串输出的一些预先编写的支持。
  4. Do something with the original return value, in this instance throw an exception if we get non-zero (ie error) from the C++.对原始返回值做一些事情,在这种情况下,如果我们从 C++ 得到非零(即错误),则会抛出异常。 There are other ways of handling this, but this one is the simplest way to no just silently ignore errors.还有其他方法可以处理这个问题,但这是最简单的方法,而不是默默地忽略错误。

I'd almost never used SWIG's JavaScript support before, so once I'd worked my way through the basic documentation for building a module this was sufficient for me to run the following test:我之前几乎从未使用过 SWIG 的 JavaScript 支持,所以一旦我完成了构建模块基本文档,这足以让我运行以下测试:

var test = require("./build/Release/test");

console.log(test.test("blah blah"));

Which worked as expected.这按预期工作。 I also did a quick test changing the return value to force an exception and that behaved as intended also.我还做了一个快速测试,更改返回值以强制异常,并且其行为也符合预期。

Disclaimer: Doing this has pretty much doubled my exposure to node/V8, so check my workings carefully.免责声明:这样做几乎使我接触 node/V8 的机会增加了一倍,所以请仔细检查我的工作。

声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM