Question

I'm sure there must be a simple answer for this, but I couldn't find any reference on the documentation or through a few initial google searches.

Basically, I have a class that looks something like this:

#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];
}

There are other functions and variables that are used, but none of them will be directly called in JavaScript.

Now, I want to us embind so that I can create my object and do something like this:

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

So, naturally, I do something like this:

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

These are my getters and setters

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

Then there are these errors:

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;

So does anyone know how to handle double arrays in emscripten? I really hope I didn't just miss part of the documentation. If I didn't, this really need to be included on the embind page.

Also, I apologize for any incoherence. It's not a complicated problem (on the surface). I just don't know what to do.

Was it helpful?

Solution

I think you have a couple of options, though not probably pretty...

This example uses arrays of int's for the direct memory access example 2, but you can use doubles or anything else as long as you map the direct memory size appropriately on the javascript side:

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

vectors seem like more of a pain and added overhead than just direct memory access, so I'd probably just provide some javascript pointer arithmetic functions that convert to the necessary types using direct access, and return pointers from the C++ functions to make it easier to use. So that you can do:

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.

Oh by the way:

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

UPDATE

Example using 2d array of doubles rather than int's. The return type of the function is still an int though, because it is returning the address of the thing via the ampersand operator. Nothing to do with its data types, all data types will have the same size of pointer. It is interesting how emscripten emulates this whole pointer concept, it was kind of neat to think about that in JS code :) Anyway, you can always return it as an int no matter what it is.

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

So the only thing that changes there is the data type at the declaration of ux2, you still get the address of ux2 and return it as an int, probably doesn't need the cast either but what the heck, won't hurt.

Make sure to do the allow_raw_pointers in the emscripten bindings:

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

And as I mentioned, I couldn't figure out how to tell it to do the allow_raw_pointers when it was a property as you had in your example, so I just used functions as shown in the example above.

Licensed under: CC-BY-SA with attribution
Not affiliated with StackOverflow
scroll top