about interacting with compiled code (returning structs)

1,093 views
Skip to first unread message

rolando

unread,
Apr 20, 2012, 1:27:18 PM4/20/12
to emscripten-discuss
Hi,

I'm trying to use the chipmunk physics library, the project comes with
a CMakefile, but I just created a very simple Makefile (no configure,
it's just a bunch of c files) to be able to use emscripten.
So far so good, but I'm having problems interacting with the code...
actually, I have no idea how to interact with the code.

Most of the functions will deal with cpVect, which is just a struct
packing two floats inside (x and y).
Just to test a few things, I compiled only cpVect.c (https://
github.com/slembcke/Chipmunk-Physics/blob/master/src/cpVect.c) and
tried to interact with it. The problem is that I don't know what to
pass to cwrap...

This is my "make test":

#### Makefile
CC=../emscripten/emcc
CFLAGS=-Iinclude/chipmunk

test:
${CC} -O1 ${CFLAGS} src/cpVect.c -o cpVect.js
#### End Makefile

after generating the .js, I added this at the end, but I was just
playing:

var cpv = cwrap('cpv', 'struct', ['number', 'number']);
var a = cpv(100, 100);
console.log("out: " + a);

My question is: what am I supposed to put as a return type? array?
('array' throws an exception when calling the function)

As a reference, this is the compiled _cpv function:

function _cpv($agg_result, $x, $y) {
var $agg_result_0 = $agg_result | 0;
tempDoubleF64[0] = $x, HEAP32[$agg_result_0 >> 2] =
tempDoubleI32[0], HEAP32[$agg_result_0 + 4 >> 2] = tempDoubleI32[1];
var $agg_result_1 = $agg_result + 8 | 0;
tempDoubleF64[0] = $y, HEAP32[$agg_result_1 >> 2] =
tempDoubleI32[0], HEAP32[$agg_result_1 + 4 >> 2] = tempDoubleI32[1];
return;
}


Thanks for any help.
Rolando

rolando

unread,
Apr 20, 2012, 1:43:42 PM4/20/12
to emscripten-discuss
I tried also cpvstr (which returns a char *) and I got this:

var cpvstr = cwrap('cpvstr', 'string', ['number', 'number']);
var a = cpvstr(1.0, 1.0);
console.log("out: " + a);

$ node cpVect.js
out: (% .3f, % .3f)

I also tried modifying the arguments from number,number to 'array'
with the same effect. It seems like it's dumping the format string
passed to sprintf...
Any clues?

Thanks!
Rolando

rolando

unread,
Apr 20, 2012, 3:23:41 PM4/20/12
to emscripten-discuss
Ok, I think I got it... The compiled functions are adding an extra
argument to use it as a pointer to the stack/heap but apparently
there's no interface for that from the real javascript world. I
created this simple function (in C, then compiled) to test:

char* testThis()
{
cpVect v = cpv(100.3f, 55.f);
return cpvstr(v);
}

The compiled version is like this:

function _testThis() {
var __stackBase__ = STACKTOP;
STACKTOP += 16;
var $v = __stackBase__;
_cpv($v, 100.30000305175781, 55);
var $1 = $v | 0;
var $2 = (tempDoubleI32[0] = HEAP32[$1 >> 2], tempDoubleI32[1] =
HEAP32[$1 + 4 >> 2], tempDoubleF64[0]);
var $3 = $v + 8 | 0;
var $4 = (tempDoubleI32[0] = HEAP32[$3 >> 2], tempDoubleI32[1] =
HEAP32[$3 + 4 >> 2], tempDoubleF64[0]);
var $5 = _cpvstr($2, $4);
STACKTOP = __stackBase__;
return _cpvstr_str | 0;
}

And this is what I did to call "testThis":

var testThis = cwrap('testThis', 'string');
var str = testThis();
console.log("out: " + str);

(btw, there's a bug in sprintf, it's not taking the format "% .3f",
where there's a space used as a filler)

And that outputs the right values... From looking at the way testThis
was compiled, it makes sense: first it gets a pointer to the (top of
the) stack, then moves the stack (16 == 2 floats), then calls cpv
(which should return a struct with two floats in it) and then it gets
the x and y value from the struct (in the stack) and passes those
values to cpvstr... :S

Now the question remains: what would be the simple way to interact
from a regular javascript world with these compiled functions?
Should I create my own wrapper functions?

Thanks!
Rolando

Alon Zakai

unread,
Apr 20, 2012, 6:11:23 PM4/20/12
to emscripte...@googlegroups.com
ccall and cwrap are meant to be used with C code. C++ methods add
|this| which makes it more complicated, and there is also name
mangling.

If you really want C++ classes, see the bindings_generator.py, but it
is experimental. Much more recommended, as mentioned in

https://github.com/kripken/emscripten/wiki/Interacting-with-code

, is to create a C interface that uses basic types (no return of
compound structs, etc.) and use that.

- azakai

rolando

unread,
Apr 20, 2012, 6:20:38 PM4/20/12
to emscripten-discuss
It's not C++ code or classes: Chipmunk Physics is an (almost?) ANSI-C
library. It's using some more complex types wrapped as structs (point,
body, etc.).
It's also not object oriented...

Anyway, creating a simple wrapper seems to work:

#### JS Wrappers

var cpv = function (x, y) {
var v = STACKTOP;
STACKTOP += 16;
_cpv(v, x, y);
var o = {};
o.ptr = v;
Object.defineProperty(o, 'x', {
get: cpv.getX,
set: cpv.setX
});
Object.defineProperty(o, 'y', {
get: cpv.getY,
set: cpv.setY
});
return o;
};
cpv.getX = function () {
v = this.ptr;
tempDoubleI32[0] = HEAP32[v >> 2];
tempDoubleI32[1] = HEAP32[v + 4 >> 2];
return tempDoubleF64[0];
};
cpv.getY = function () {
v = this.ptr;
tempDoubleI32[0] = HEAP32[v + 8 >> 2];
tempDoubleI32[1] = HEAP32[v + 12 >> 2];
return tempDoubleF64[0];
};
cpv.setX = function (x) {
v = this.ptr;
tempDoubleF64[0] = x, HEAP32[v >> 2] = tempDoubleI32[0], HEAP32[v +
4 >> 2] = tempDoubleI32[1];
};
cpv.setY = function (y) {
v = this.ptr;
tempDoubleF64[0] = y, HEAP32[v + 8 >> 2] = tempDoubleI32[0],
HEAP32[v + 12 >> 2] = tempDoubleI32[1];
};

var cpvstr = function (p) {
assert(p.ptr !== undefined);
var str = _cpvstr(p.x, p.y);
return Pointer_stringify(str);
};

var pt1 = cpv(13.37, 37.1387);
var pt2 = cpv(5500, 1300);

console.log("pt1: " + cpvstr(pt1));
console.log("pt2: " + cpvstr(pt2));

pt1.x = pt2.y;
pt2.y = pt1.x;

console.log("pt1: " + cpvstr(pt1));
console.log("pt2: " + cpvstr(pt2));

#####

output:

$ node cpVect.js
pt1: (13.370, 37.139)
pt2: (5500.000, 1300.000)
pt1: (1300.000, 37.139)
pt2: (5500.000, 1300.000)

#####

So maybe I'll go that way :) - not exactly the best/fastest solution,
but seems to do the trick.

Thanks,
Rolando

Alon Zakai

unread,
Apr 20, 2012, 6:23:25 PM4/20/12
to emscripte...@googlegroups.com
Then yeah, sounds like making a simpler wrapper as you say is the way to go.

Future versions of JS may add better ways to access binary data, and
we will be able to access C structs in emscripten from native JS more
easily. Right now this is still a little painful.

- azakai

rolando

unread,
Apr 20, 2012, 6:26:07 PM4/20/12
to emscripten-discuss
EDIT:

much simpler with get/setValue:

cpv.getX = function () {
v = this.ptr;
return getValue(v, 'double');
};
cpv.getY = function () {
v = this.ptr;
return getValue(v + 8, 'double');
};
cpv.setX = function (x) {
v = this.ptr;
setValue(v, 'double');
};
cpv.setY = function (y) {
v = this.ptr;
setValue(v + 8, 'double');
};

:D

Rolando
Reply all
Reply to author
Forward
0 new messages