2

我确信对此必须有一个简单的答案,但我在文档中或通过一些初步的谷歌搜索找不到任何参考。

基本上,我有一个看起来像这样的类:

#define NX 65
#define NY 65

class myclass{
    // other stuff
    public:
        //other stuff.. more functions and more variables
        // a function I want to call every so often with a few different cases
        void solve(int case);
        // a 2D double array that I want to access in JS
        double ux[NX+1][NY+1];
}

还有其他使用的函数和变量,但它们都不会在 JavaScript 中直接调用。

现在,我想我们 embind 以便我可以创建我的对象并执行以下操作:

x = new Module.myclass();
x.solve(2); // parameter is irrelevant
for (i=0; i<x.ux.length; i++) {
    for (j=0; j<x.ux[i].length; j++) {
        // do something with the data
        console.log(x.ux[i][j]);
    }
}

所以,很自然,我会做这样的事情:

EMSCRIPTEN_BINDINGS(myclass) {
    class_<myclass>("myclass")
        .function("solve", &myclass::solve)
        .property("ux", &LBM::getux, &LBM::setux)
        ;
}

这些是我的吸气剂和二传手

void setux(double uxnew[NX+1][NY+1]) {
        for (int i=0; i<NX+1; i++) {
                for (int j=0; j<NY+1; j++) {
                        ux[i][j] = uxnew[i][j];
                }
        }
};
double getux() { return **ux; };

然后有这些错误:

In file included from ../../lbm.cpp:10:
/home/vagrant/src/emscripten/system/include/emscripten/bind.h:1043:33: error: implicit instantiation of undefined template 'emscripten::internal::GetterPolicy<double (LBM::*)()>'
                TypeID<typename GP::ReturnType>::get(),
                                ^
../../lbm.cpp:1264:18: note: in instantiation of function template specialization 'emscripten::class_<LBM, emscripten::internal::NoBaseClass>::property<double (LBM::*)(), void (LBM::*)(double (*)[66])>' requested here
                .property("p", &LBM::getp, &LBM::setp)
                 ^
/home/vagrant/src/emscripten/system/include/emscripten/bind.h:428:16: note: template is declared here
        struct GetterPolicy;

那么有谁知道如何在 emscripten 中处理双数组?我真的希望我没有错过部分文档。如果我没有,这确实需要包含在嵌入页面中。

另外,我为任何不一致的地方道歉。这不是一个复杂的问题(表面上)。我只是不知道该怎么办。

4

1 回答 1

4

我认为你有几个选择,虽然可能不是很漂亮......

此示例将 int 数组用于直接内存访问示例 2,但您可以使用双精度数或其他任何内容,只要您在 javascript 端适当地映射直接内存大小:

测试.cpp:

#include <emscripten/bind.h>
#include <stdlib.h>
#include <iostream>

#define NX 65
#define NY 65

class myclass{
    // other stuff
    public:
        myclass() {
            //Just initializing some values to see:
            ux2[0][0] = 3;
            ux2[0][1] = 5;
            ux2[1][0] = 7;
            ux2[1][1] = 9;
        }

        //Example 1: only the  setux seems to work, not getux:
        std::vector<std::vector<double>> ux;
        std::vector<std::vector<double>> getux() { return ux; }
        void setux(std::vector<std::vector<double>> uxnew) {
            for (int i=0; i<NX+1; i++) {
                    for (int j=0; j<NY+1; j++) {
                            std::cout << uxnew[i][j] << std::endl;
                            ux[i][j] = uxnew[i][j];
                    }
            }
        }

        //Example 2: But if we know the address of ux2, then we can get
        // the values and set them, no need for vector overhead:
        int ux2[NX+1][NY+1];
        int getux2() {
            return (int)&ux2;
        };

};


// Required for example 1:
EMSCRIPTEN_BINDINGS(stl_wrappers) {
    emscripten::register_vector<double>("VectorDouble");
    emscripten::register_vector<std::vector<double>>("VectorVectorDouble");
}

EMSCRIPTEN_BINDINGS(myclass) {
    emscripten::class_<myclass>("myclass")
        .constructor()
        //// I could not seem to get properties to work with Vector or pointers:
        //.property("ux", &myclass::getux, &myclass::setux)

        //// So fell back to functions:
        //// Example 1:
        .function("setux",&myclass::setux)
        .function("getux",&myclass::getux) // returns undefined?
        //// Example 2: just work with pointers on JS side (note the allow_raw_pointers here)
        .function("getux2",&myclass::getux2,emscripten::allow_raw_pointers())
        ;
};

测试.js:

var M = require('./test.js');

var vdd = new M.VectorVectorDouble();

var doublearray = [];
for(var i=0; i<66; ++i){
    var vd = new M.VectorDouble();
    for(var j=0; j<66; ++j){
        vd.push_back(i+j);
    }
    vdd.push_back(vd);
}

var testclass = new M.myclass();
//This works:
testclass.setux(vdd);
var noworkie = testclass.getux();
//But this does not: (?)
console.log(noworkie.get(0));


// Direct memory access:
var sz = 4;
var ln = 66;
var ind0 = 0;
var ind1 = 1;
var t = new M.myclass();
var ux2ptr = t.getux2();
console.log(M.getValue(ux2ptr+(0*ln + ind0)*sz,'i8*'));
console.log(M.getValue(ux2ptr+(0*ln + ind1)*sz,'i8*'));
console.log(M.getValue(ux2ptr+(1*ln + ind0)*sz,'i8*'));
console.log(M.getValue(ux2ptr+(1*ln + ind1)*sz,'i8*'));
M.setValue(ux2ptr+(0*ln + ind0)*sz,10,'i8*');
console.log(M.getValue(ux2ptr+(0*ln + ind0)*sz,'i8*'));
console.log(M.getValue(ux2ptr+(0*ln + ind1)*sz,'i8*'));
console.log(M.getValue(ux2ptr+(1*ln + ind0)*sz,'i8*'));
console.log(M.getValue(ux2ptr+(1*ln + ind1)*sz,'i8*'));

emcc test.cpp -o test.js -std=c++11 --bind

向量似乎比直接内存访问更痛苦和增加开销,所以我可能只是提供一些 javascript 指针算术函数,它们使用直接访问转换为必要的类型,并从 C++ 函数返回指针以使其更容易采用。这样你就可以做到:

var dimension1 = NX+1, dimension2 = NY+1;
var blah = doubleptrptr(ux2ptr, dimension1, dimension2);
var first = blah[0][0];  // so you can use the way you expect on the js side.

哦顺便说一句:

emcc -v
emcc (Emscripten GCC-like replacement + linker emulating GNU ld ) 2.0
clang version 3.2 (tags/RELEASE_32/final)
Target: i386-pc-linux-gnu
Thread model: posix

更新

使用 2d 双精度数组而不是 int 的示例。不过,函数的返回类型仍然是 int,因为它通过 & 运算符返回事物的地址。与其数据类型无关,所有数据类型都会有相同大小的指针。有趣的是 emscripten 如何模拟整个指针概念,在 JS 代码中考虑这一点很巧妙 :) 不管它是什么,你总是可以将它作为 int 返回。

//Example 2: If we know the address of ux2, then we can get
// the values and set them, no need for vector overhead:
double p[NX+1][NY+1];
int getp() {
    return (int)&p;
};

所以唯一改变的是 ux2 声明中的数据类型,你仍然得到 ux2 的地址并将它作为一个 int 返回,可能也不需要强制转换,但见鬼,不会有伤害。

确保在 emscripten 绑定中执行allow_raw_pointers

.function("getp",&myclass::getp,emscripten::allow_raw_pointers())

正如我所提到的,当它是您在示例中的属性时,我无法弄清楚如何告诉它执行 allow_raw_pointers,因此我只使用了上面示例中所示的函数。

于 2013-06-23T23:29:19.047 回答