Calling C from JS with Emscripten, minimal examples

There are a few ways of doing this, this is the simplest, but also slowest. The options are listed on the Emscripten wiki here.

Return an int, complete example

Sample C file (I called it call.c):

1
2
3
4
5
6
7
8
9
#include <math.h>
 
int test1(int x) {
  return sqrt(x);
}
 
int test2(int x) {
  return sqrt(x)+1;
}

Compile as follows:

emcc call.c -o call.js -s EXPORTED_FUNCTIONS="['_test1','_test2']"

You can then create a html file which calls this JS code:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
<!DOCTYPE html>
<html>
<body>
<script src="call.js"></script>
 
<script>
var result = Module.ccall(
'test1', // name of C function
'number', // return type
['number'], // argument types
[28]); // arguments
 
document.write(result);
</script>
</body>
</html>

Passing and returning an array of ints

This is a little harder, Emscripten doesn’t really help you out, you have to format the arrays in memory manually.

Sample C program two functions, one passing and returning a char *, the other int *s.

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
#include <math.h>
#include <stdlib.h>
#include <string.h>
 
char *test1(char *instr) {
 
  char *array = malloc(sizeof(char)*strlen(instr));
  strcpy(array,instr);        // the horror!!
  int size = strlen(array);
  for(int n=0;n<size;n++) {
    if((array[n] >= 'a') && (array[n] <= 'z')) array[n] -= 'a'-'A';
  }
 
  return array;
}
 
int *test2(int *in,int size) {
 
  int *array = malloc(sizeof(int)*size);
 
  for(int n=0;n<size;n++) {
    array[n] = in[n]*2;
  }
 
  return array;
}
emcc call.c -o call.js -s EXPORTED_FUNCTIONS="['_test1','_test2']"

The JS can then be called from html as follows:

1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
24
25
26
27
28
29
30
31
32
33
34
35
36
37
38
39
40
41
42
43
44
45
46
47
48
49
50
51
52
53
54
55
56
57
58
59
60
61
62
<!DOCTYPE html>
<html>
<body>
<script src="call.js"></script>
 
<script>
 
function write_1d_int32_array(ptr,array) {
 
  for(i=0;i<array.length;i++) {
    Module.setValue(ptr,array[i],'i32');
    ptr += 4;
  }
 
}
 
function read_1d_int32_array(ptr,length) {
 
  var array = [];
 
  for(i=0;i<length;i++) {
    var value = Module.getValue(ptr+(i*4),'i32');
    array.push(value);
  }
 
  return array;
}
 
// test1 - a C function that takes, and returns a C style string (char *)
 
var mystring = "test";
var strptr = Module._malloc(mystring.length);
Module.writeAsciiToMemory(mystring, strptr);
 
var retstrptr = Module.ccall('test1', // name of C function
  'number', // return type
  ['number'], // argument types
  [strptr]); // arguments
 
// Convert the resulting string to a JS string
var retstr = Pointer_stringify(retstrptr);
document.write(retstr);
 
// test2 - a C function that takes, and returns a C style int array (int *)
 
var myarray = [10,20,30,40,50];
var arrayptr = Module._malloc(myarray.length*4);
write_1d_int32_array(arrayptr,myarray);
 
var retarrayptr = Module.ccall('test2', // name of C function
  'number', // return type
  ['number'], // argument types
  [arrayptr,myarray.length]); // arguments
 
// Convert the resulting pointer to a JS array
var retarray = read_1d_int32_array(retarrayptr,myarray.length);
document.write(retarray);
 
</script>
 
</body>
</html>

Comments are closed.