【问题标题】:Emscripten error when binding class with 2D double array使用 2D 双数组绑定类时出现 Emscripten 错误
【发布时间】:2013-06-12 02:06:21
【问题描述】:

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

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

#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中都不会直接调用。

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

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)
        ;
}

这些是我的 getter 和 setter

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 中处理双数组?我真的希望我没有错过部分文档。如果我没有,这确实需要包含在嵌入页面中。

另外,对于任何不一致之处,我深表歉意。这不是一个复杂的问题(表面上)。我只是不知道该怎么办。

【问题讨论】:

    标签: emscripten embind


    【解决方案1】:

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

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

    test.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())
            ;
    };
    

    test.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
    

    更新

    使用二维数组而不是整数的示例。不过,函数的返回类型仍然是 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,所以我只使用了上面示例中所示的函数。

    【讨论】:

    • 我稍后会测试这个。如果我在赏金到期前不解决这个问题,你至少会得到看起来完美的建议。
    • 谢谢,洛根。我想我读到如果它过期并超过宽限期,它需要 2 票。但这根本不是什么大事,我只是希望这能让你到达你需要的地方。我确实注意到有一些关于处理 emscripten 的创建者参与的数组的讨论,但我认为一个很好的指针指向对象、数组或 JS 端处理内存访问的原始函数套件和 C/ 上的一些约定C++ 方面可能会满足我能想到的大多数需求。无论如何,我的 cmets 在代码中,所以我希望它可以正常工作。
    • 好的,所以我的指针fu 确实很弱。我的数组是double p[NX+1][NY+1],我使用了与您的示例相同的getter,将ints 更改为doubles。但是,我仍然收到错误消息:C-style cast from 'double (*)[66][66]' to 'double' is not allowed
    • 我在第二个示例中使用整数作为二维数组的数据类型可能令人困惑,因为第二个示例 getux2 的返回类型仍需要为 int,而不管二维数组的数据类型如何,因为它使用 & 运算符返回二维数组的地址以获取引用。我将更新另一个使用双打的示例。
    • 哦。这就说得通了。我一回到我的电脑就测试一下。
    猜你喜欢
    • 1970-01-01
    • 2019-06-16
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2019-05-29
    • 2021-09-13
    相关资源
    最近更新 更多